2 * Copyright (C) 2004-2012, Eric Lund
3 * http://www.mvpmc.org/
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * ringbuf.c - functions to handle operations on MythTV ringbuffers. A
22 * MythTV Ringbuffer is the part of the backend that handles
23 * recording of live-tv for streaming to a MythTV frontend.
24 * This allows the watcher to do things like pause, rewind
25 * and so forth on live-tv.
31 #include <sys/types.h>
32 #include <cmyth_local.h>
35 * cmyth_ringbuf_destroy(cmyth_ringbuf_t rb)
37 * Scope: PRIVATE (static)
41 * Clean up and free a ring buffer structure. This should only be done
42 * by the ref_release() code. Everyone else should call
43 * ref_release() because ring buffer structures are reference
51 cmyth_ringbuf_destroy(cmyth_ringbuf_t rb)
53 cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
58 if (rb->ringbuf_url) {
59 ref_release(rb->ringbuf_url);
61 if (rb->ringbuf_hostname) {
62 ref_release(rb->ringbuf_hostname);
67 * cmyth_ringbuf_create(void)
73 * Allocate and initialize a ring buffer structure.
77 * Success: A non-NULL cmyth_ringbuf_t (this type is a pointer)
79 * Failure: A NULL cmyth_ringbuf_t
82 cmyth_ringbuf_create(void)
84 cmyth_ringbuf_t ret = ref_alloc(sizeof(*ret));
86 cmyth_dbg(CMYTH_DBG_DEBUG, "%s\n", __FUNCTION__);
91 ret->conn_data = NULL;
92 ret->ringbuf_url = NULL;
93 ret->ringbuf_size = 0;
94 ret->ringbuf_fill = 0;
97 ret->ringbuf_hostname = NULL;
98 ret->ringbuf_port = 0;
99 ref_set_destroy(ret, (ref_destroy_t)cmyth_ringbuf_destroy);
104 * cmyth_ringbuf_setup(cmyth_recorder_t old_rec)
110 * Set up the ring buffer inside a recorder for use in playing live
111 * tv. The recorder is supplied. This will be duplicated and
112 * released, so the caller can re-use the same variable to hold the
113 * return. The new copy of the recorder will have a ringbuffer set up
118 * Success: A pointer to a new recorder structure with a ringbuffer
123 cmyth_ringbuf_setup(cmyth_recorder_t rec)
125 static const char service[]="rbuf://";
126 cmyth_recorder_t new_rec = NULL;
138 cmyth_conn_t control;
141 cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n",
146 control = rec->rec_conn;
148 pthread_mutex_lock(&mutex);
150 snprintf(msg, sizeof(msg),
151 "QUERY_RECORDER %u[]:[]SETUP_RING_BUFFER[]:[]0",
154 if ((err=cmyth_send_message(control, msg)) < 0) {
155 cmyth_dbg(CMYTH_DBG_ERROR,
156 "%s: cmyth_send_message() failed (%d)\n",
161 count = cmyth_rcv_length(control);
163 if (control->conn_version >= 16) {
164 r = cmyth_rcv_string(control, &err, buf, sizeof(buf)-1, count);
167 r = cmyth_rcv_string(control, &err, url, sizeof(url)-1, count);
170 if ((r=cmyth_rcv_int64(control, &err, &size, count)) < 0) {
171 cmyth_dbg(CMYTH_DBG_ERROR,
172 "%s: cmyth_rcv_length() failed (%d)\n",
178 if ((r=cmyth_rcv_int64(control, &err, &fill, count)) < 0) {
179 cmyth_dbg(CMYTH_DBG_ERROR,
180 "%s: cmyth_rcv_length() failed (%d)\n",
185 cmyth_dbg(CMYTH_DBG_DEBUG, "%s: url is: '%s'\n",
188 if (strncmp(url, service, sizeof(service) - 1) == 0) {
190 * The URL starts with rbuf://. The rest looks like
191 * <host>:<port>/<filename>.
193 host = url + strlen(service);
194 port = strchr(host, ':');
197 * This does not seem to be a proper URL, so just
198 * assume it is a filename, and get out.
200 cmyth_dbg(CMYTH_DBG_DEBUG,
201 "%s: 1 port %s, host = %s\n",
202 __FUNCTION__, port, host);
206 path = strchr(port, '/');
209 * This does not seem to be a proper URL, so just
210 * assume it is a filename, and get out.
212 cmyth_dbg(CMYTH_DBG_DEBUG, "%s: no path\n",
218 new_rec = cmyth_recorder_dup(rec);
219 if (new_rec == NULL) {
220 cmyth_dbg(CMYTH_DBG_DEBUG, "%s: cannot create recorder\n",
225 new_rec->rec_ring = cmyth_ringbuf_create();
229 new_rec->rec_ring->ringbuf_hostname = ref_strdup(host);
233 new_rec->rec_ring->ringbuf_port = atoi(port);
235 new_rec->rec_ring->ringbuf_url = ref_strdup(url);
236 new_rec->rec_ring->ringbuf_size = size;
237 new_rec->rec_ring->ringbuf_fill = fill;
240 pthread_mutex_unlock(&mutex);
246 cmyth_ringbuf_pathname(cmyth_recorder_t rec)
248 return ref_hold(rec->rec_ring->ringbuf_url);
252 * cmyth_ringbuf_get_block(cmyth_recorder_t rec, char *buf, unsigned long len)
255 * Read incoming file data off the network into a buffer of length len.
258 * Sucess: number of bytes read into buf
262 cmyth_ringbuf_get_block(cmyth_recorder_t rec, char *buf, unsigned long len)
273 FD_SET(rec->rec_ring->conn_data->conn_fd, &fds);
274 if (select((int)rec->rec_ring->conn_data->conn_fd+1,
275 NULL, &fds, NULL, &tv) == 0) {
276 rec->rec_ring->conn_data->conn_hang = 1;
279 rec->rec_ring->conn_data->conn_hang = 0;
281 return recv(rec->rec_ring->conn_data->conn_fd, buf, len, 0);
285 cmyth_ringbuf_select(cmyth_recorder_t rec, struct timeval *timeout)
293 fd = rec->rec_ring->conn_data->conn_fd;
298 ret = select((int)fd+1, &fds, NULL, NULL, timeout);
301 rec->rec_ring->conn_data->conn_hang = 1;
303 rec->rec_ring->conn_data->conn_hang = 0;
309 * cmyth_ringbuf_request_block(cmyth_ringbuf_t file, unsigned long len)
315 * Request a file data block of a certain size, and return when the
316 * block has been transfered.
320 * Sucess: number of bytes transfered
322 * Failure: an int containing -errno
325 cmyth_ringbuf_request_block(cmyth_recorder_t rec, unsigned long len)
333 cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n",
338 pthread_mutex_lock(&mutex);
340 if(len > (unsigned int)rec->rec_conn->conn_tcp_rcvbuf)
341 len = (unsigned int)rec->rec_conn->conn_tcp_rcvbuf;
343 snprintf(msg, sizeof(msg),
344 "QUERY_RECORDER %u[]:[]REQUEST_BLOCK_RINGBUF[]:[]%ld",
347 if ((err = cmyth_send_message(rec->rec_conn, msg)) < 0) {
348 cmyth_dbg(CMYTH_DBG_ERROR,
349 "%s: cmyth_send_message() failed (%d)\n",
355 count = cmyth_rcv_length(rec->rec_conn);
356 if ((r=cmyth_rcv_long(rec->rec_conn, &err, &c, count)) < 0) {
357 cmyth_dbg(CMYTH_DBG_ERROR,
358 "%s: cmyth_rcv_length() failed (%d)\n",
364 rec->rec_ring->file_pos += c;
368 pthread_mutex_unlock(&mutex);
374 * cmyth_ringbuf_read (cmyth_recorder_t rec, char *buf, unsigned long len)
380 * Request and read a block of data from backend
384 * Sucess: number of bytes transfered
386 * Failure: an int containing -errno
388 int cmyth_ringbuf_read(cmyth_recorder_t rec, char *buf, unsigned long len)
399 cmyth_dbg (CMYTH_DBG_ERROR, "%s: no connection\n",
404 pthread_mutex_lock (&mutex);
406 snprintf(msg, sizeof(msg),
407 "QUERY_RECORDER %u[]:[]REQUEST_BLOCK_RINGBUF[]:[]%ld",
410 if ( (err = cmyth_send_message (rec->rec_conn, msg) ) < 0)
412 cmyth_dbg (CMYTH_DBG_ERROR,
413 "%s: cmyth_send_message() failed (%d)\n",
424 while (cur < end || req)
430 if((int)rec->rec_conn->conn_fd > nfds)
431 nfds = (int)rec->rec_conn->conn_fd;
432 FD_SET (rec->rec_conn->conn_fd, &fds);
434 if((int)rec->rec_ring->conn_data->conn_fd > nfds)
435 nfds = (int)rec->rec_ring->conn_data->conn_fd;
436 FD_SET (rec->rec_ring->conn_data->conn_fd, &fds);
438 if ((ret = select (nfds+1, &fds, NULL, NULL,&tv)) < 0)
440 cmyth_dbg (CMYTH_DBG_ERROR,
441 "%s: select(() failed (%d)\n",
448 rec->rec_ring->conn_data->conn_hang = 1;
449 rec->rec_conn->conn_hang = 1;
454 /* check control connection */
455 if (FD_ISSET(rec->rec_conn->conn_fd, &fds) )
458 if ((count = cmyth_rcv_length (rec->rec_conn)) < 0)
460 cmyth_dbg (CMYTH_DBG_ERROR,
461 "%s: cmyth_rcv_length() failed (%d)\n",
462 __FUNCTION__, count);
467 if ((ret = cmyth_rcv_ulong (rec->rec_conn, &err, &len, count))< 0)
469 cmyth_dbg (CMYTH_DBG_ERROR,
470 "%s: cmyth_rcv_long() failed (%d)\n",
476 rec->rec_ring->file_pos += len;
481 /* check data connection */
482 if (FD_ISSET(rec->rec_ring->conn_data->conn_fd, &fds))
485 if ((ret = recv (rec->rec_ring->conn_data->conn_fd, cur, end-cur, 0)) < 0)
487 cmyth_dbg (CMYTH_DBG_ERROR,
488 "%s: recv() failed (%d)\n",
498 pthread_mutex_unlock (&mutex);
503 * cmyth_ringbuf_seek(
504 * cmyth_ringbuf_t file, long long offset, int whence)
510 * Seek to a new position in the file based on the value of whence:
512 * The offset is set to offset bytes.
514 * The offset is set to the current position plus offset bytes.
516 * The offset is set to the size of the file minus offset bytes.
522 * Failure: an int containing -errno
525 cmyth_ringbuf_seek(cmyth_recorder_t rec,
526 long long offset, int whence)
534 cmyth_ringbuf_t ring;
539 ring = rec->rec_ring;
541 if ((offset == 0) && (whence == SEEK_CUR))
542 return ring->file_pos;
544 pthread_mutex_lock(&mutex);
546 snprintf(msg, sizeof(msg),
547 "QUERY_RECORDER %u[]:[]SEEK_RINGBUF[]:[]%d[]:[]%d[]:[]%d[]:[]%d[]:[]%d",
549 (int32_t)(offset >> 32),
550 (int32_t)(offset & 0xffffffff),
552 (int32_t)(ring->file_pos >> 32),
553 (int32_t)(ring->file_pos & 0xffffffff));
555 if ((err = cmyth_send_message(rec->rec_conn, msg)) < 0) {
556 cmyth_dbg(CMYTH_DBG_ERROR,
557 "%s: cmyth_send_message() failed (%d)\n",
563 count = cmyth_rcv_length(rec->rec_conn);
564 if ((r=cmyth_rcv_int64(rec->rec_conn, &err, &c, count)) < 0) {
565 cmyth_dbg(CMYTH_DBG_ERROR,
566 "%s: cmyth_rcv_length() failed (%d)\n",
574 ring->file_pos = offset;
577 ring->file_pos += offset;
580 ring->file_pos = ring->file_length - offset;
584 ret = ring->file_pos;
587 pthread_mutex_unlock(&mutex);