leo_sobral@2: /* GStreamer MythTV Plug-in leo_sobral@2: * Copyright (C) <2006> Rosfran Borges leo_sobral@2: * leo_sobral@2: * This library is free software; you can redistribute it and/or rosfran@289: * modify it under the terms of the GNU Library General Public rosfran@289: * License as published by the Free Software Foundation; either leo_sobral@2: * version 2 of the License, or (at your option) any later version. leo_sobral@2: * leo_sobral@2: * This library is distributed in the hope that it will be useful, leo_sobral@2: * but WITHOUT ANY WARRANTY; without even the implied warranty of leo_sobral@2: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU rosfran@289: * Library General Public License for more details. rosfran@289: * rosfran@289: * You should have received a copy of the GNU Library General Public rosfran@289: * License along with this library; if not, write to the rosfran@289: * Free Software Foundation, Inc., 59 Temple Place - Suite 330, rosfran@289: * Boston, MA 02111-1307, USA. leo_sobral@2: */ rosfran@289: /** rosfran@371: * If you want to get the LiveTV content (set channel, TV tuner, RemoteEncoder, rosfran@371: * Recorder), rosfran@371: * put the following URI: rosfran@289: * rosfran@371: * myth://xxx.xxx.xxx.xxx:6543/livetv?channel=BBC rosfran@371: * rosfran@371: * This URI will say to the gmyth library to configure the Recorder instance (used to rosfran@371: * change the channel, start the TV multimedia content transmition, etc.), using rosfran@371: * the IP address (xxx.xxx.xxx.xxx) and port number (6543) of the MythTV backend rosfran@371: * server, and setting the channel name to "BBC". rosfran@371: * rosfran@371: * To get a already recorded the MythTV NUV file, put the following URI: rosfran@371: * rosfran@371: * myth://xxx.xxx.xxx.xxx:6543/filename.nuv rosfran@371: * rosfran@371: * This URI will say to the gmyth library to configure the Recorder instance (used to rosfran@371: * change the channel, start the TV multimedia content transmition, etc.), using rosfran@371: * the IP address (xxx.xxx.xxx.xxx) and port number (6543) of the MythTV backend rosfran@371: * server, and setting the channel name to "BBC". rosfran@371: * rosfran@371: * Another possible way to use the LiveTV content, and just in the case you want to rosfran@371: * use the mysql database, put the location URI in the following format: rosfran@371: * rosfran@375: * myth://mythtv:mythtv@xxx.xxx.xxx.xxx:6543/?mythconverg&channel=9 rosfran@289: * rosfran@289: * Where the first field is the protocol (myth), the second and third are user rosfran@289: * name (mythtv) and password (mythtv), then backend host name and port number, rosfran@289: * and the last field is the database name (mythconverg). rosfran@289: */ leo_sobral@2: leo_sobral@2: #ifdef HAVE_CONFIG_H leo_sobral@2: #include "config.h" leo_sobral@2: #endif leo_sobral@2: leo_sobral@2: #include "gstmythtvsrc.h" rosfran@52: #include rosfran@52: #include leo_sobral@2: leo_sobral@2: #include leo_sobral@2: #include leo_sobral@2: leo_sobral@2: #include leo_sobral@2: #include leo_sobral@2: leo_sobral@2: GST_DEBUG_CATEGORY_STATIC (mythtvsrc_debug); leo_sobral@2: #define GST_CAT_DEFAULT mythtvsrc_debug leo_sobral@2: rosfran@289: #define GST_GMYTHTV_ID_NUM 1 rosfran@70: rosfran@289: #define GST_GMYTHTV_CHANNEL_DEFAULT_NUM (-1) leo_sobral@2: rosfran@289: #define GMYTHTV_VERSION_DEFAULT 30 leo_sobral@2: rosfran@289: #define GMYTHTV_TRANSFER_MAX_WAITS 100 rosfran@102: rosfran@289: #define GMYTHTV_TRANSFER_MAX_RESENDS 2 leo_sobral@2: rosfran@289: #define GMYTHTV_TRANSFER_MAX_BUFFER (128*1024) leo_sobral@2: rosfran@289: #define MAX_READ_SIZE (4*1024) rosfran@70: rosfran@289: #define GST_FLOW_ERROR_NO_DATA (-101) rosfran@124: rosfran@289: #define REQUEST_MAX_SIZE (64*1024) rosfran@90: rosfran@289: #define INTERNAL_BUFFER_SIZE (90*1024) leo_sobral@2: leo_sobral@2: static const GstElementDetails gst_mythtv_src_details = rosfran@289: GST_ELEMENT_DETAILS ("MythTV client source", leo_sobral@2: "Source/Network", rosfran@289: "Control and receive data as a client over the network " rosfran@289: "via raw socket connections using the MythTV protocol", rosfran@289: "Rosfran Borges "); leo_sobral@2: rosfran@289: static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", leo_sobral@2: GST_PAD_SRC, leo_sobral@2: GST_PAD_ALWAYS, rosfran@289: GST_STATIC_CAPS ("video/x-nuv")); rosfran@289: leo_sobral@2: enum leo_sobral@2: { leo_sobral@2: PROP_0, leo_sobral@2: PROP_LOCATION, leo_sobral@2: #ifndef GST_DISABLE_GST_DEBUG rosfran@40: PROP_GMYTHTV_DBG, leo_sobral@2: #endif rosfran@40: PROP_GMYTHTV_VERSION, rosfran@40: PROP_GMYTHTV_LIVE, rosfran@40: PROP_GMYTHTV_LIVEID, rosfran@52: PROP_GMYTHTV_LIVE_CHAINID, rosfran@70: PROP_GMYTHTV_ENABLE_TIMING_POSITION, rosfran@70: PROP_GMYTHTV_CHANNEL_NUM leo_sobral@2: }; leo_sobral@2: leo_sobral@2: static void gst_mythtv_src_finalize (GObject * gobject); leo_sobral@2: rosfran@289: static GstFlowReturn gst_mythtv_src_create (GstPushSrc * psrc, rosfran@289: GstBuffer ** outbuf); rosfran@52: leo_sobral@2: static gboolean gst_mythtv_src_start (GstBaseSrc * bsrc); leo_sobral@2: static gboolean gst_mythtv_src_stop (GstBaseSrc * bsrc); leo_sobral@2: static gboolean gst_mythtv_src_get_size (GstBaseSrc * bsrc, guint64 * size); rosfran@289: static gboolean gst_mythtv_src_is_seekable (GstBaseSrc * push_src); rosfran@87: rosfran@289: static gboolean gst_mythtv_src_do_seek (GstBaseSrc * base, rosfran@289: GstSegment * segment); rosfran@98: rosfran@52: static GstStateChangeReturn rosfran@52: gst_mythtv_src_change_state (GstElement * element, GstStateChange transition); leo_sobral@2: leo_sobral@2: static void gst_mythtv_src_set_property (GObject * object, guint prop_id, leo_sobral@2: const GValue * value, GParamSpec * pspec); leo_sobral@2: static void gst_mythtv_src_get_property (GObject * object, guint prop_id, leo_sobral@2: GValue * value, GParamSpec * pspec); leo_sobral@2: rosfran@289: static void gst_mythtv_src_uri_handler_init (gpointer g_iface, rosfran@289: gpointer iface_data); rosfran@30: rosfran@87: static gboolean gst_mythtv_src_handle_query (GstPad * pad, GstQuery * query); rosfran@87: rosfran@90: static gboolean gst_mythtv_src_handle_event (GstPad * pad, GstEvent * event); rosfran@30: renatofilho@506: static GMythFileTransferReadResult do_read_request_response (GstMythtvSrc * src, guint size, rosfran@289: GByteArray * data_ptr); leo_sobral@2: rosfran@52: static void leo_sobral@2: _urihandler_init (GType type) leo_sobral@2: { leo_sobral@2: static const GInterfaceInfo urihandler_info = { leo_sobral@2: gst_mythtv_src_uri_handler_init, leo_sobral@2: NULL, leo_sobral@2: NULL leo_sobral@2: }; leo_sobral@2: leo_sobral@2: g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info); leo_sobral@2: rosfran@289: GST_DEBUG_CATEGORY_INIT (mythtvsrc_debug, "mythtvsrc", 0, "MythTV src"); leo_sobral@2: } leo_sobral@2: rosfran@100: GST_BOILERPLATE_FULL (GstMythtvSrc, gst_mythtv_src, GstPushSrc, rosfran@100: GST_TYPE_PUSH_SRC, _urihandler_init) rosfran@289: rosfran@289: static void gst_mythtv_src_base_init (gpointer g_class) leo_sobral@2: { leo_sobral@2: GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); leo_sobral@2: leo_sobral@2: gst_element_class_add_pad_template (element_class, leo_sobral@2: gst_static_pad_template_get (&srctemplate)); rosfran@289: leo_sobral@2: gst_element_class_set_details (element_class, &gst_mythtv_src_details); rosfran@289: rosfran@52: element_class->change_state = gst_mythtv_src_change_state; rosfran@289: leo_sobral@2: } leo_sobral@2: rosfran@30: static void leo_sobral@2: gst_mythtv_src_class_init (GstMythtvSrcClass * klass) leo_sobral@2: { rosfran@289: GObjectClass *gobject_class; rosfran@100: GstPushSrcClass *gstpushsrc_class; leo_sobral@2: GstBaseSrcClass *gstbasesrc_class; leo_sobral@2: leo_sobral@2: gobject_class = (GObjectClass *) klass; leo_sobral@2: gstbasesrc_class = (GstBaseSrcClass *) klass; rosfran@100: gstpushsrc_class = (GstPushSrcClass *) klass; leo_sobral@2: leo_sobral@2: gobject_class->set_property = gst_mythtv_src_set_property; leo_sobral@2: gobject_class->get_property = gst_mythtv_src_get_property; leo_sobral@2: gobject_class->finalize = gst_mythtv_src_finalize; leo_sobral@2: leo_sobral@2: g_object_class_install_property rosfran@289: (gobject_class, PROP_LOCATION, rosfran@289: g_param_spec_string ("location", "Location", rosfran@289: "The location. In the form:" rosfran@289: "\n\t\t\tmyth://a.com/file.nuv" rosfran@289: "\n\t\t\tmyth://a.com:23223/file.nuv " rosfran@289: "\n\t\t\ta.com/file.nuv - default scheme 'myth'", rosfran@289: "", G_PARAM_READWRITE)); leo_sobral@2: leo_sobral@2: g_object_class_install_property rosfran@289: (gobject_class, PROP_GMYTHTV_VERSION, rosfran@289: g_param_spec_int ("mythtv-version", "mythtv-version", rosfran@289: "Change MythTV version", 26, 30, 26, G_PARAM_READWRITE)); leo_sobral@2: leo_sobral@2: g_object_class_install_property rosfran@289: (gobject_class, PROP_GMYTHTV_LIVEID, rosfran@289: g_param_spec_int ("mythtv-live-id", "mythtv-live-id", rosfran@289: "Change MythTV version", rosfran@289: 0, 200, GST_GMYTHTV_ID_NUM, G_PARAM_READWRITE)); leo_sobral@2: leo_sobral@2: g_object_class_install_property rosfran@289: (gobject_class, PROP_GMYTHTV_LIVE_CHAINID, rosfran@289: g_param_spec_string ("mythtv-live-chainid", "mythtv-live-chainid", rosfran@289: "Sets the MythTV chain ID (from TV Chain)", "", G_PARAM_READWRITE)); leo_sobral@2: leo_sobral@2: g_object_class_install_property rosfran@289: (gobject_class, PROP_GMYTHTV_LIVE, rosfran@289: g_param_spec_boolean ("mythtv-live", "mythtv-live", rosfran@289: "Enable MythTV Live TV content streaming", FALSE, G_PARAM_READWRITE)); leo_sobral@2: leo_sobral@2: g_object_class_install_property rosfran@289: (gobject_class, PROP_GMYTHTV_ENABLE_TIMING_POSITION, rosfran@289: g_param_spec_boolean ("mythtv-enable-timing-position", rosfran@289: "mythtv-enable-timing-position", rosfran@289: "Enable MythTV Live TV content size continuous updating", FALSE, rosfran@289: G_PARAM_READWRITE)); leo_sobral@2: rosfran@52: g_object_class_install_property rosfran@289: (gobject_class, PROP_GMYTHTV_CHANNEL_NUM, rosfran@308: g_param_spec_string ("mythtv-channel", "mythtv-channel", rosfran@289: "Change MythTV channel number", rosfran@308: "", G_PARAM_READWRITE)); rosfran@52: leo_sobral@2: #ifndef GST_DISABLE_GST_DEBUG leo_sobral@2: g_object_class_install_property rosfran@289: (gobject_class, PROP_GMYTHTV_DBG, rosfran@289: g_param_spec_boolean ("mythtv-debug", "mythtv-debug", rosfran@289: "Enable MythTV debug messages", FALSE, G_PARAM_READWRITE)); leo_sobral@2: #endif leo_sobral@2: leo_sobral@2: gstbasesrc_class->start = gst_mythtv_src_start; leo_sobral@2: gstbasesrc_class->stop = gst_mythtv_src_stop; leo_sobral@2: gstbasesrc_class->get_size = gst_mythtv_src_get_size; rosfran@87: gstbasesrc_class->is_seekable = gst_mythtv_src_is_seekable; rosfran@289: rosfran@100: gstbasesrc_class->do_seek = gst_mythtv_src_do_seek; rosfran@100: gstpushsrc_class->create = gst_mythtv_src_create; rosfran@289: leo_sobral@2: GST_DEBUG_CATEGORY_INIT (mythtvsrc_debug, "mythtvsrc", 0, leo_sobral@2: "MythTV Client Source"); leo_sobral@2: } leo_sobral@2: rosfran@30: static void leo_sobral@2: gst_mythtv_src_init (GstMythtvSrc * this, GstMythtvSrcClass * g_class) leo_sobral@2: { leo_sobral@2: this->file_transfer = NULL; leo_sobral@2: leo_sobral@2: this->unique_setup = FALSE; leo_sobral@2: rosfran@40: this->mythtv_version = GMYTHTV_VERSION_DEFAULT; rosfran@289: rosfran@70: this->state = GST_MYTHTV_SRC_FILE_TRANSFER; leo_sobral@2: leo_sobral@2: this->bytes_read = 0; rosfran@289: rosfran@63: this->prev_content_size = 0; leo_sobral@2: rosfran@52: this->content_size = 0; leo_sobral@2: this->read_offset = 0; leo_sobral@2: rosfran@61: this->content_size_last = 0; rosfran@61: leo_sobral@2: this->live_tv = FALSE; rosfran@289: rosfran@52: this->enable_timing_position = FALSE; rosfran@289: this->update_prog_chain = FALSE; leo_sobral@2: leo_sobral@2: this->user_agent = g_strdup ("mythtvsrc"); rosfran@52: this->update_prog_chain = FALSE; rosfran@289: rosfran@308: this->channel_name = NULL; rosfran@289: rosfran@52: this->eos = FALSE; rosfran@289: rosfran@98: this->bytes_queue = NULL; leo_sobral@2: rosfran@289: this->wait_to_transfer = 0; rosfran@289: rosfran@289: gst_base_src_set_format (GST_BASE_SRC (this), GST_FORMAT_BYTES); rosfran@289: rosfran@289: gst_pad_set_event_function (GST_BASE_SRC_PAD (GST_BASE_SRC (this)), rosfran@289: gst_mythtv_src_handle_event); rosfran@289: gst_pad_set_query_function (GST_BASE_SRC_PAD (GST_BASE_SRC (this)), rosfran@289: gst_mythtv_src_handle_query); rosfran@30: leo_sobral@2: } leo_sobral@2: rosfran@30: static void leo_sobral@2: gst_mythtv_src_finalize (GObject * gobject) leo_sobral@2: { leo_sobral@2: GstMythtvSrc *this = GST_MYTHTV_SRC (gobject); rosfran@114: rosfran@371: if (this->spawn_livetv) { rosfran@371: g_object_unref (this->spawn_livetv); rosfran@371: this->spawn_livetv = NULL; leo_sobral@2: } rosfran@371: /* leo_sobral@2: if (this->file_transfer) { leo_sobral@2: g_object_unref (this->file_transfer); leo_sobral@2: this->file_transfer = NULL; leo_sobral@2: } rosfran@371: */ rosfran@118: if (this->backend_info) { rosfran@118: g_object_unref (this->backend_info); rosfran@118: this->backend_info = NULL; rosfran@118: } rosfran@52: leo_sobral@2: if (this->uri_name) { leo_sobral@2: g_free (this->uri_name); leo_sobral@2: } leo_sobral@2: leo_sobral@2: if (this->user_agent) { leo_sobral@2: g_free (this->user_agent); leo_sobral@2: } rosfran@114: rosfran@289: if (this->bytes_queue) { rosfran@289: g_byte_array_free (this->bytes_queue, TRUE); rosfran@114: this->bytes_queue = NULL; rosfran@100: } leo_sobral@2: leo_sobral@2: G_OBJECT_CLASS (parent_class)->finalize (gobject); leo_sobral@2: } leo_sobral@2: renatofilho@506: static GMythFileTransferReadResult renatofilho@506: do_read_request_response (GstMythtvSrc * src, guint size, GByteArray *data_ptr) leo_sobral@2: { rosfran@37: gint read = 0; rosfran@52: guint sizetoread = size; rosfran@142: gint max_iters = GMYTHTV_TRANSFER_MAX_RESENDS; renatofilho@503: GMythFileTransferReadResult result; rosfran@289: rosfran@289: GST_LOG_OBJECT (src, "Starting: Reading %d bytes...", sizetoread); leo_sobral@2: rosfran@30: /* Loop sending the Myth File Transfer request: leo_sobral@2: * Retry whilst authentication fails and we supply it. */ rosfran@61: rosfran@289: while (sizetoread == size && --max_iters > 0) { renatofilho@503: result = gmyth_file_transfer_read (src->file_transfer, rosfran@407: data_ptr, sizetoread, src->live_tv); rosfran@289: renatofilho@506: if (data_ptr->len > 0) { renatofilho@503: read += data_ptr->len; renatofilho@503: sizetoread -= data_ptr->len; renatofilho@506: } else if (data_ptr->len < 0) { rosfran@289: if (src->live_tv == FALSE) { renatofilho@506: result = GMYTH_FILE_TRANSFER_READ_EOF; rosfran@289: goto eos; rosfran@289: } else { renatofilho@503: if (result == GMYTH_FILE_TRANSFER_READ_ERROR) { /* -314 */ rosfran@289: GST_INFO_OBJECT (src, "[LiveTV] FileTransfer READ_ERROR!"); rosfran@289: goto done; renatofilho@503: } else if (result == GMYTH_FILE_TRANSFER_READ_NEXT_PROG_CHAIN) { /* -315 */ rosfran@289: GST_INFO_OBJECT (src, rosfran@289: "[LiveTV] FileTransfer - Go to the next program chain!"); rosfran@289: continue; rosfran@289: } rosfran@289: goto done; rosfran@90: } rosfran@30: renatofilho@503: } else if (data_ptr->len == 0) rosfran@289: goto done; rosfran@61: rosfran@289: if (read == sizetoread) rosfran@90: goto done; rosfran@100: } rosfran@61: renatofilho@506: if ((read < 0 && !src->live_tv) || max_iters == 0){ renatofilho@506: result = GMYTH_FILE_TRANSFER_READ_EOF; rosfran@61: goto eos; renatofilho@506: } rosfran@61: rosfran@30: goto done; leo_sobral@2: leo_sobral@2: eos: leo_sobral@2: src->eos = TRUE; rosfran@61: leo_sobral@2: done: renatofilho@506: return result; leo_sobral@2: } leo_sobral@2: rosfran@30: static GstFlowReturn rosfran@289: gst_mythtv_src_create (GstPushSrc * psrc, GstBuffer ** outbuf) rosfran@81: { rosfran@81: GstMythtvSrc *src; rosfran@81: GstFlowReturn ret = GST_FLOW_OK; rosfran@90: rosfran@289: src = GST_MYTHTV_SRC (psrc); rosfran@90: rosfran@81: /* The caller should know the number of bytes and not read beyond EOS. */ rosfran@289: if (G_UNLIKELY (src->eos)) rosfran@81: goto eos; rosfran@289: if (G_UNLIKELY (src->update_prog_chain)) rosfran@81: goto change_progchain; rosfran@95: rosfran@289: GST_DEBUG_OBJECT (src, "offset = %" G_GUINT64_FORMAT ", size = %d...", rosfran@289: src->read_offset, MAX_READ_SIZE); rosfran@95: rosfran@289: GST_DEBUG_OBJECT (src, "Create: buffer_remain: %d, buffer_size = %d.", rosfran@289: (gint) src->buffer_remain, src->bytes_queue->len); rosfran@95: rosfran@429: program_chain_changed: rosfran@100: /* just get from the byte array, no network effort... */ rosfran@289: if ((src->buffer_remain = src->bytes_queue->len) < MAX_READ_SIZE) { rosfran@364: GByteArray *buffer; renatofilho@503: GMythFileTransferReadResult result = GMYTH_FILE_TRANSFER_READ_OK; rosfran@359: rosfran@364: buffer = NULL; rosfran@289: guint buffer_size_inter = (INTERNAL_BUFFER_SIZE - src->buffer_remain); rosfran@95: rosfran@289: if (buffer_size_inter > REQUEST_MAX_SIZE) rosfran@289: buffer_size_inter = REQUEST_MAX_SIZE; rosfran@289: rosfran@289: buffer = g_byte_array_new (); rosfran@289: renatofilho@503: result = do_read_request_response (src, buffer_size_inter, buffer); rosfran@142: renatofilho@503: if (G_UNLIKELY (buffer->len < 0)) { rosfran@359: rosfran@359: if (buffer != NULL) { rosfran@359: g_byte_array_free (buffer, TRUE); rosfran@359: buffer = NULL; rosfran@359: } rosfran@359: renatofilho@503: if (src->live_tv || ( result == GMYTH_FILE_TRANSFER_READ_NEXT_PROG_CHAIN )) rosfran@289: goto change_progchain; rosfran@142: else rosfran@289: goto read_error; rosfran@289: } else if (G_UNLIKELY (read == 0)) { rosfran@359: rosfran@359: if (buffer != NULL) { rosfran@359: g_byte_array_free (buffer, TRUE); rosfran@359: buffer = NULL; rosfran@359: } rosfran@359: rosfran@289: if (!src->live_tv) rosfran@303: goto done; rosfran@289: else rosfran@429: goto program_chain_changed; rosfran@174: } rosfran@142: rosfran@289: if (G_UNLIKELY (src->update_prog_chain)) rosfran@359: { rosfran@359: if (buffer != NULL) { rosfran@359: g_byte_array_free (buffer, TRUE); rosfran@359: buffer = NULL; rosfran@359: } rosfran@359: goto change_progchain; rosfran@359: } rosfran@289: rosfran@289: src->bytes_queue = renatofilho@503: g_byte_array_append (src->bytes_queue, buffer->data, buffer->len); renatofilho@503: if (buffer->len > buffer_size_inter) rosfran@289: GST_WARNING_OBJECT (src, rosfran@289: "INCREASED buffer size! Backend sent more than we ask him... (%d)", rosfran@289: abs (read - buffer_size_inter)); rosfran@142: renatofilho@503: src->buffer_remain += buffer->len; rosfran@142: rosfran@289: if (buffer != NULL) { rosfran@289: g_byte_array_free (buffer, TRUE); rosfran@289: buffer = NULL; rosfran@142: } rosfran@289: rosfran@289: GST_DEBUG_OBJECT (src, rosfran@289: "BYTES READ (actual) = %d, BYTES READ (cumulative) = %llu, " rosfran@289: "OFFSET = %llu, CONTENT SIZE = %llu.", read, rosfran@289: src->bytes_read, src->read_offset, src->content_size); rosfran@289: rosfran@81: } rosfran@289: rosfran@289: guint buffer_size = rosfran@289: (src->buffer_remain < MAX_READ_SIZE) ? src->buffer_remain : MAX_READ_SIZE; rosfran@289: rosfran@289: *outbuf = gst_buffer_new (); rosfran@289: rosfran@289: /* gets the first buffer_size bytes from the byte array buffer variable */ rosfran@235: /* guint8 *buf = g_memdup( src->bytes_queue->data, buffer_size ); */ rosfran@95: rosfran@289: GST_DEBUG_OBJECT (src, "read from network? %s!, buffer_remain = %d", rosfran@289: (read == rosfran@289: -1) ? "NO, got from buffer" : "YES, go see the backend's log file", rosfran@289: src->buffer_remain); rosfran@95: rosfran@100: GST_BUFFER_SIZE (*outbuf) = buffer_size; rosfran@289: GST_BUFFER_MALLOCDATA (*outbuf) = g_malloc0 (GST_BUFFER_SIZE (*outbuf)); rosfran@289: GST_BUFFER_DATA (*outbuf) = GST_BUFFER_MALLOCDATA (*outbuf); rosfran@289: g_memmove (GST_BUFFER_DATA ((*outbuf)), src->bytes_queue->data, rosfran@289: GST_BUFFER_SIZE (*outbuf)); rosfran@95: GST_BUFFER_OFFSET (*outbuf) = src->read_offset; rosfran@289: GST_BUFFER_OFFSET_END (*outbuf) = rosfran@289: src->read_offset + GST_BUFFER_SIZE (*outbuf); rosfran@95: rosfran@95: src->buffer_remain -= GST_BUFFER_SIZE (*outbuf); rosfran@95: rosfran@95: src->read_offset += GST_BUFFER_SIZE (*outbuf); rosfran@95: src->bytes_read += GST_BUFFER_SIZE (*outbuf); rosfran@289: GST_DEBUG_OBJECT (src, "Buffer output with size: %d", rosfran@289: GST_BUFFER_SIZE (*outbuf)); rosfran@289: rosfran@100: /* flushs the newly buffer got from byte array */ rosfran@289: src->bytes_queue = rosfran@289: g_byte_array_remove_range (src->bytes_queue, 0, buffer_size); rosfran@95: rosfran@359: GST_DEBUG_OBJECT ( src, "Got buffer: BUFFER --->SIZE = %d, OFFSET = %llu, " rosfran@289: "OFFSET_END = %llu.", GST_BUFFER_SIZE (*outbuf), rosfran@359: GST_BUFFER_OFFSET (*outbuf), GST_BUFFER_OFFSET_END (*outbuf) ); rosfran@95: rosfran@289: GST_DEBUG_OBJECT (src, "CONTENT_SIZE = %llu, BYTES_READ = %llu.", rosfran@289: src->content_size, src->bytes_read); rosfran@289: rosfran@359: if ( G_UNLIKELY (src->eos) || ( !src->live_tv rosfran@359: && ( src->bytes_read >= src->content_size ) ) ) rosfran@289: goto eos; rosfran@90: rosfran@81: done: rosfran@90: { rosfran@81: const gchar *reason = gst_flow_get_name (ret); rosfran@81: rosfran@81: GST_DEBUG_OBJECT (src, "DONE task, reason %s", reason); rosfran@90: return ret; rosfran@90: } rosfran@81: eos: rosfran@81: { rosfran@81: const gchar *reason = gst_flow_get_name (ret); rosfran@81: rosfran@81: GST_DEBUG_OBJECT (src, "pausing task, reason %s", reason); rosfran@81: return GST_FLOW_UNEXPECTED; rosfran@81: } rosfran@81: /* ERRORS */ rosfran@81: read_error: rosfran@81: { rosfran@81: GST_ELEMENT_ERROR (src, RESOURCE, READ, rosfran@289: (NULL), ("Could not read any bytes (%i, %s)", read, src->uri_name)); rosfran@81: return GST_FLOW_ERROR; rosfran@81: } rosfran@81: change_progchain: rosfran@81: { rosfran@81: GST_ELEMENT_ERROR (src, RESOURCE, READ, rosfran@289: (NULL), ("Seek failed, go to the next program info... (%i, %s)", read, rosfran@289: src->uri_name)); rosfran@90: rosfran@375: /* rosfran@429: TODO: need to send a new segment event to NUVDemux? rosfran@289: gst_pad_push_event (GST_BASE_SRC_PAD (GST_BASE_SRC (psrc)), rosfran@289: gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, -1, 0)); rosfran@375: */ rosfran@303: rosfran@359: goto program_chain_changed; rosfran@37: } rosfran@30: leo_sobral@2: } rosfran@98: rosfran@52: gint64 rosfran@289: gst_mythtv_src_get_position (GstMythtvSrc * src) leo_sobral@2: { rosfran@30: rosfran@52: gint64 size_tmp = 0; rosfran@71: guint max_tries = 2; rosfran@71: rosfran@289: if (src->live_tv == TRUE && (abs (src->content_size - src->bytes_read) < rosfran@289: GMYTHTV_TRANSFER_MAX_BUFFER)) { rosfran@289: rosfran@289: get_file_pos: rosfran@289: g_usleep (10); rosfran@289: size_tmp = gmyth_recorder_get_file_position (src->spawn_livetv->recorder); rosfran@289: if (size_tmp > (src->content_size + GMYTHTV_TRANSFER_MAX_BUFFER)) rosfran@61: src->content_size = size_tmp; rosfran@289: else if (size_tmp > 0 && --max_tries > 0) rosfran@61: goto get_file_pos; rosfran@289: GST_LOG_OBJECT (src, "GET_POSITION: file_position = %lld", size_tmp); rosfran@71: /* sets the last content size amount before it can be updated */ rosfran@71: src->prev_content_size = src->content_size; rosfran@30: } rosfran@52: rosfran@289: return src->content_size; rosfran@30: leo_sobral@2: } leo_sobral@2: rosfran@87: static gboolean rosfran@289: gst_mythtv_src_do_seek (GstBaseSrc * base, GstSegment * segment) rosfran@87: { rosfran@289: GstMythtvSrc *src = GST_MYTHTV_SRC (base); rosfran@90: gint64 new_offset = -1; rosfran@90: gint64 actual_seek = segment->start; rosfran@87: gboolean ret = TRUE; rosfran@87: rosfran@289: GST_LOG_OBJECT (src, "seek, segment: %" GST_SEGMENT_FORMAT, segment); rosfran@90: rosfran@289: if (segment->format == GST_FORMAT_TIME) { rosfran@289: goto done; rosfran@90: } rosfran@289: GST_LOG_OBJECT (src, rosfran@289: "Trying to seek at the value (actual_seek = %lld, read_offset = %lld)", rosfran@289: actual_seek, src->read_offset); rosfran@87: /* verify if it needs to seek */ rosfran@289: if (src->read_offset != actual_seek) { rosfran@87: rosfran@289: new_offset = rosfran@289: gmyth_file_transfer_seek (src->file_transfer, segment->start, SEEK_SET); rosfran@87: rosfran@289: GST_LOG_OBJECT (src, rosfran@289: "Segment offset start = %lld, SRC Offset = %lld, NEW actual backend SEEK Offset = %lld.", rosfran@289: segment->start, src->read_offset, new_offset); rosfran@289: if (G_UNLIKELY (new_offset < 0)) { rosfran@87: ret = FALSE; rosfran@371: if (!src->live_tv) rosfran@289: goto eos; rosfran@87: } rosfran@87: rosfran@87: src->read_offset = new_offset; rosfran@87: rosfran@289: if (ret == FALSE) { rosfran@289: GST_INFO_OBJECT (src, "Failed to set the SEEK on segment!"); rosfran@90: } rosfran@90: rosfran@87: } rosfran@90: rosfran@90: done: rosfran@289: return ret; rosfran@87: rosfran@87: eos: rosfran@87: { rosfran@87: rosfran@87: GST_DEBUG_OBJECT (src, "EOS found on seeking!!!"); rosfran@87: return FALSE; rosfran@87: } rosfran@87: rosfran@87: } rosfran@87: leo_sobral@2: /* create a socket for connecting to remote server */ rosfran@30: static gboolean rosfran@289: gst_mythtv_src_start (GstBaseSrc * bsrc) leo_sobral@2: { leo_sobral@2: GstMythtvSrc *src = GST_MYTHTV_SRC (bsrc); leo_sobral@2: rosfran@293: GString *chain_id_local = NULL; rosfran@293: GMythURI *gmyth_uri = NULL; leo_sobral@2: gboolean ret = TRUE; rosfran@90: leo_sobral@2: if (src->unique_setup == FALSE) { leo_sobral@2: src->unique_setup = TRUE; leo_sobral@2: } else { leo_sobral@2: goto done; leo_sobral@2: } rosfran@174: rosfran@293: gmyth_uri = gmyth_uri_new_with_value( src->uri_name ); rosfran@289: rosfran@289: src->backend_info = gmyth_backend_info_new_with_uri (src->uri_name); rosfran@304: src->live_tv |= gmyth_uri_is_livetv( gmyth_uri ); rosfran@289: /* testing UPnP... */ rosfran@289: /* gmyth_backend_info_set_hostname( src->backend_info, NULL ); */ rosfran@304: if ( src->live_tv ) { rosfran@448: src->spawn_livetv = gmyth_livetv_new (src->backend_info); rosfran@236: rosfran@308: gchar* ch = gmyth_uri_get_channel_name( gmyth_uri ); rosfran@308: if ( ch != NULL ) rosfran@308: src->channel_name = ch; rosfran@289: rosfran@308: if (src->channel_name != NULL) { rosfran@448: if (gmyth_livetv_channel_name_setup (src->spawn_livetv, src->channel_name) == FALSE) { rosfran@289: GST_INFO_OBJECT (src, "LiveTV setup felt down on error"); rosfran@289: ret = FALSE; rosfran@289: goto init_failed; rosfran@289: } rosfran@236: } else { rosfran@448: if (gmyth_livetv_setup (src->spawn_livetv) == FALSE) { rosfran@289: GST_INFO_OBJECT (src, "LiveTV setup felt down on error"); rosfran@289: ret = FALSE; rosfran@289: goto init_failed; rosfran@289: } rosfran@236: } rosfran@311: rosfran@311: /* testing change channel... */ rosfran@311: /* gmyth_recorder_change_channel( src->spawn_livetv->recorder, CHANNEL_DIRECTION_UP ); */ rosfran@52: rosfran@289: src->file_transfer = gmyth_livetv_create_file_transfer (src->spawn_livetv); rosfran@289: rosfran@289: if (NULL == src->file_transfer) { rosfran@289: GST_INFO_OBJECT (src, "[LiveTV] FileTransfer equals to NULL"); rosfran@289: ret = FALSE; rosfran@289: goto init_failed; leo_sobral@2: } rosfran@325: rosfran@325: if ( !gmyth_file_transfer_open( src->file_transfer, src->spawn_livetv->uri != NULL ? rosfran@325: gmyth_uri_get_path(src->spawn_livetv->uri) : rosfran@325: src->spawn_livetv->proginfo->pathname->str ) ) rosfran@325: { rosfran@325: GST_INFO_OBJECT (src, "Error: couldn't open the FileTransfer from LiveTV source!" ); rosfran@325: g_object_unref( src->file_transfer ); rosfran@325: src->file_transfer = NULL; rosfran@325: goto init_failed; rosfran@325: } rosfran@217: } else { rosfran@217: rosfran@289: src->file_transfer = gmyth_file_transfer_new (src->backend_info); leo_sobral@2: rosfran@289: ret = gmyth_file_transfer_open (src->file_transfer, src->uri_name); rosfran@110: leo_sobral@2: } leo_sobral@2: rosfran@289: if (NULL == src->file_transfer) { rosfran@289: GST_INFO_OBJECT (src, "FileTransfer is NULL"); rosfran@289: goto init_failed; rosfran@289: } rosfran@289: /*GST_INFO_OBJECT( src, "uri = %s", src->spawn_livetv->file_transfer); */ rosfran@289: rosfran@289: if (ret == FALSE) { rosfran@289: #ifndef GST_DISABLE_GST_DEBUG rosfran@289: if (src->mythtv_msgs_dbg) rosfran@289: GST_INFO_OBJECT (src, rosfran@289: "MythTV FileTransfer request failed when setting up socket connection!"); leo_sobral@2: #endif leo_sobral@2: goto begin_req_failed; leo_sobral@2: } rosfran@289: rosfran@289: GST_INFO_OBJECT (src, rosfran@289: "MythTV FileTransfer filesize = %lld, content_size = %lld!", rosfran@289: src->file_transfer->filesize, src->content_size); leo_sobral@2: renatofilho@503: src->content_size = gmyth_file_transfer_get_filesize (src->file_transfer); leo_sobral@2: rosfran@71: src->do_start = FALSE; rosfran@289: rosfran@114: /* this is used for the buffer cache */ rosfran@289: src->bytes_queue = g_byte_array_sized_new (INTERNAL_BUFFER_SIZE); rosfran@90: src->buffer_remain = 0; rosfran@114: rosfran@289: gst_pad_push_event (GST_BASE_SRC_PAD (GST_BASE_SRC (src)), rosfran@289: gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME, 0, rosfran@289: src->content_size, 0)); rosfran@90: rosfran@30: done: rosfran@293: /*if ( gmyth_uri != NULL ) rosfran@289: { rosfran@289: g_object_unref( gmyth_uri ); rosfran@289: gmyth_uri = NULL; rosfran@293: }*/ rosfran@210: rosfran@289: if (chain_id_local != NULL) { rosfran@289: g_string_free (chain_id_local, TRUE); rosfran@289: chain_id_local = NULL; rosfran@289: } rosfran@289: rosfran@30: return TRUE; rosfran@30: rosfran@30: /* ERRORS */ rosfran@30: init_failed: rosfran@30: { rosfran@289: if (src->spawn_livetv != NULL) rosfran@289: g_object_unref (src->spawn_livetv); rosfran@30: rosfran@30: GST_ELEMENT_ERROR (src, LIBRARY, INIT, rosfran@289: (NULL), ("Could not initialize MythTV library (%i, %s)", ret, rosfran@289: src->uri_name)); rosfran@30: return FALSE; rosfran@30: } rosfran@30: begin_req_failed: rosfran@30: { rosfran@30: GST_ELEMENT_ERROR (src, LIBRARY, INIT, rosfran@289: (NULL), ("Could not begin request sent to MythTV server (%i, %s)", ret, rosfran@289: src->uri_name)); rosfran@30: return FALSE; rosfran@30: } rosfran@61: rosfran@52: } rosfran@52: rosfran@30: static gboolean leo_sobral@2: gst_mythtv_src_get_size (GstBaseSrc * bsrc, guint64 * size) leo_sobral@2: { rosfran@31: GstMythtvSrc *src = GST_MYTHTV_SRC (bsrc); rosfran@30: gboolean ret = TRUE; rosfran@289: rosfran@289: GST_LOG_OBJECT (src, "Differs from previous content size: %d (max.: %d)", rosfran@289: abs (src->content_size - src->prev_content_size), rosfran@289: GMYTHTV_TRANSFER_MAX_BUFFER); rosfran@289: rosfran@289: if (src->live_tv) { rosfran@289: ret = FALSE; rosfran@289: } else if (src->live_tv && src->enable_timing_position rosfran@289: && (abs (src->content_size - src->bytes_read) < rosfran@289: GMYTHTV_TRANSFER_MAX_BUFFER)) { rosfran@289: rosfran@289: gint64 new_offset = rosfran@289: gmyth_recorder_get_file_position (src->spawn_livetv->recorder); rosfran@289: if (new_offset > 0 && new_offset > src->content_size) { rosfran@289: src->content_size = new_offset; rosfran@289: } else if (new_offset < src->content_size) { rosfran@289: src->update_prog_chain = TRUE; rosfran@289: } rosfran@61: leo_sobral@2: } leo_sobral@2: leo_sobral@2: *size = src->content_size; rosfran@289: GST_LOG_OBJECT (src, "Content size = %lld", src->content_size); rosfran@289: rosfran@30: return ret; rosfran@30: leo_sobral@2: } leo_sobral@2: leo_sobral@2: /* close the socket and associated resources leo_sobral@2: * used both to recover from errors and go to NULL state */ rosfran@52: static gboolean leo_sobral@2: gst_mythtv_src_stop (GstBaseSrc * bsrc) leo_sobral@2: { leo_sobral@2: GstMythtvSrc *src; leo_sobral@2: leo_sobral@2: src = GST_MYTHTV_SRC (bsrc); leo_sobral@2: leo_sobral@2: if (src->uri_name) { leo_sobral@2: g_free (src->uri_name); leo_sobral@2: src->uri_name = NULL; leo_sobral@2: } leo_sobral@2: leo_sobral@2: src->eos = FALSE; leo_sobral@2: leo_sobral@2: return TRUE; leo_sobral@2: } leo_sobral@2: rosfran@30: static gboolean leo_sobral@2: gst_mythtv_src_handle_event (GstPad * pad, GstEvent * event) leo_sobral@2: { leo_sobral@2: GstMythtvSrc *src = GST_MYTHTV_SRC (GST_PAD_PARENT (pad)); rosfran@52: gint64 cont_size = 0; rosfran@90: gboolean ret = FALSE; leo_sobral@2: leo_sobral@2: switch (GST_EVENT_TYPE (event)) { rosfran@30: case GST_EVENT_EOS: rosfran@289: GST_WARNING_OBJECT (src, "Got EOS event"); rosfran@61: rosfran@289: if (src->live_tv) { rosfran@289: cont_size = gst_mythtv_src_get_position (src); rosfran@289: if (cont_size > src->content_size) { rosfran@289: src->content_size = cont_size; rosfran@289: src->eos = FALSE; rosfran@289: } else { rosfran@289: src->eos = TRUE; rosfran@289: gst_element_set_state (GST_ELEMENT (src), GST_STATE_NULL); rosfran@289: gst_element_set_locked_state (GST_ELEMENT (src), FALSE); rosfran@289: } rosfran@114: } rosfran@30: break; leo_sobral@2: default: rosfran@90: ret = gst_pad_event_default (pad, event); leo_sobral@2: } leo_sobral@2: rosfran@90: return ret; leo_sobral@2: } leo_sobral@2: rosfran@30: static gboolean rosfran@289: gst_mythtv_src_is_seekable (GstBaseSrc * push_src) leo_sobral@2: { rosfran@100: return TRUE; leo_sobral@2: } rosfran@87: rosfran@87: static gboolean rosfran@87: gst_mythtv_src_handle_query (GstPad * pad, GstQuery * query) rosfran@87: { rosfran@87: gboolean res = FALSE; rosfran@87: GstMythtvSrc *myth = GST_MYTHTV_SRC (gst_pad_get_parent (pad)); rosfran@114: GstFormat formt; rosfran@87: rosfran@87: switch (GST_QUERY_TYPE (query)) { rosfran@87: case GST_QUERY_POSITION: rosfran@114: { rosfran@289: gst_query_parse_position (query, &formt, NULL); rosfran@289: if (formt == GST_FORMAT_BYTES) { rosfran@289: gst_query_set_position (query, formt, myth->read_offset); rosfran@289: GST_DEBUG_OBJECT (myth, "POS %" G_GINT64_FORMAT, myth->read_offset); rosfran@289: res = TRUE; rosfran@289: } else if (formt == GST_FORMAT_TIME) { rosfran@289: res = gst_pad_query_default (pad, query); rosfran@124: } rosfran@87: break; rosfran@114: } rosfran@87: case GST_QUERY_DURATION: rosfran@114: { rosfran@87: #if 0 rosfran@87: if (myth->duration != 0) { rosfran@289: gint64 total; rosfran@289: gint64 fps; rosfran@87: rosfran@289: fps = nuv->h->i_fpsn / nuv->h->i_fpsd; rosfran@289: total = rosfran@289: gst_util_uint64_scale_int (GST_SECOND, nuv->h->i_video_blocks, fps); rosfran@114: } rosfran@81: #endif rosfran@289: rosfran@289: gst_query_parse_duration (query, &formt, NULL); rosfran@289: if (formt == GST_FORMAT_BYTES) { rosfran@289: gst_query_set_duration (query, formt, myth->content_size); rosfran@289: GST_DEBUG_OBJECT (myth, "SIZE %" G_GINT64_FORMAT, myth->content_size); rosfran@289: res = TRUE; rosfran@289: } else if (formt == GST_FORMAT_TIME) { rosfran@289: res = gst_pad_query_default (pad, query); rosfran@289: } rosfran@70: break; rosfran@114: } rosfran@70: default: rosfran@114: { rosfran@289: res = gst_pad_query_default (pad, query); rosfran@87: break; rosfran@114: } rosfran@70: } rosfran@61: rosfran@87: gst_object_unref (myth); rosfran@61: rosfran@70: return res; rosfran@61: } rosfran@61: rosfran@52: static GstStateChangeReturn rosfran@52: gst_mythtv_src_change_state (GstElement * element, GstStateChange transition) rosfran@52: { rosfran@289: GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE; rosfran@52: GstMythtvSrc *src = GST_MYTHTV_SRC (element); rosfran@52: rosfran@52: switch (transition) { rosfran@52: case GST_STATE_CHANGE_NULL_TO_READY: rosfran@52: break; rosfran@52: case GST_STATE_CHANGE_READY_TO_PAUSED: rosfran@52: case GST_STATE_CHANGE_PAUSED_TO_PLAYING: rosfran@289: if (src->live_tv) { rosfran@289: if (!gmyth_recorder_send_frontend_ready_command (src->spawn_livetv-> rosfran@289: recorder)) rosfran@289: GST_WARNING_OBJECT (src, rosfran@289: "Couldn't send the FRONTEND_READY message to the backend!"); rosfran@289: else rosfran@289: GST_DEBUG_OBJECT (src, "FRONTEND_READY was sent to the backend"); rosfran@124: } rosfran@52: break; rosfran@52: default: rosfran@52: break; rosfran@52: } rosfran@61: rosfran@61: ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); rosfran@52: if (ret == GST_STATE_CHANGE_FAILURE) rosfran@52: return ret; rosfran@52: rosfran@52: switch (transition) { rosfran@52: case GST_STATE_CHANGE_READY_TO_NULL: rosfran@52: break; rosfran@61: case GST_STATE_CHANGE_PLAYING_TO_PAUSED: rosfran@52: case GST_STATE_CHANGE_PAUSED_TO_READY: rosfran@61: break; rosfran@52: default: rosfran@52: break; rosfran@52: } rosfran@52: rosfran@52: return ret; rosfran@52: } rosfran@52: rosfran@30: static void leo_sobral@2: gst_mythtv_src_set_property (GObject * object, guint prop_id, leo_sobral@2: const GValue * value, GParamSpec * pspec) leo_sobral@2: { leo_sobral@2: GstMythtvSrc *mythtvsrc = GST_MYTHTV_SRC (object); leo_sobral@2: leo_sobral@2: GST_OBJECT_LOCK (mythtvsrc); leo_sobral@2: switch (prop_id) { leo_sobral@2: case PROP_LOCATION: rosfran@289: { rosfran@289: if (!g_value_get_string (value)) { rosfran@289: GST_WARNING ("location property cannot be NULL"); leo_sobral@2: break; leo_sobral@2: } rosfran@289: rosfran@289: if (mythtvsrc->uri_name != NULL) { rosfran@289: g_free (mythtvsrc->uri_name); rosfran@289: mythtvsrc->uri_name = NULL; rosfran@289: } rosfran@289: mythtvsrc->uri_name = g_value_dup_string (value); rosfran@289: rosfran@289: break; rosfran@289: } leo_sobral@2: #ifndef GST_DISABLE_GST_DEBUG rosfran@40: case PROP_GMYTHTV_DBG: rosfran@289: { rosfran@289: mythtvsrc->mythtv_msgs_dbg = g_value_get_boolean (value); rosfran@289: break; rosfran@289: } rosfran@289: #endif rosfran@289: case PROP_GMYTHTV_VERSION: rosfran@289: { rosfran@289: mythtvsrc->mythtv_version = g_value_get_int (value); rosfran@289: break; rosfran@289: } rosfran@289: case PROP_GMYTHTV_LIVEID: rosfran@289: { rosfran@289: mythtvsrc->live_tv_id = g_value_get_int (value); rosfran@289: break; rosfran@289: } rosfran@289: case PROP_GMYTHTV_LIVE: rosfran@289: { rosfran@289: mythtvsrc->live_tv = g_value_get_boolean (value); rosfran@289: break; rosfran@289: } rosfran@289: case PROP_GMYTHTV_ENABLE_TIMING_POSITION: rosfran@289: { rosfran@289: mythtvsrc->enable_timing_position = g_value_get_boolean (value); rosfran@289: break; rosfran@289: } rosfran@289: case PROP_GMYTHTV_LIVE_CHAINID: rosfran@289: { rosfran@289: if (!g_value_get_string (value)) { rosfran@289: GST_WARNING ("MythTV Live chainid property cannot be NULL"); leo_sobral@2: break; leo_sobral@2: } rosfran@289: rosfran@289: if (mythtvsrc->live_chain_id != NULL) { rosfran@289: g_free (mythtvsrc->live_chain_id); rosfran@289: mythtvsrc->live_chain_id = NULL; leo_sobral@2: } rosfran@289: mythtvsrc->live_chain_id = g_value_dup_string (value); rosfran@289: break; rosfran@289: } rosfran@70: case PROP_GMYTHTV_CHANNEL_NUM: rosfran@289: { rosfran@308: mythtvsrc->channel_name = g_value_dup_string (value); rosfran@289: break; rosfran@289: } leo_sobral@2: default: leo_sobral@2: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); leo_sobral@2: break; leo_sobral@2: } rosfran@289: leo_sobral@2: GST_OBJECT_UNLOCK (mythtvsrc); leo_sobral@2: } leo_sobral@2: rosfran@100: static void leo_sobral@2: gst_mythtv_src_get_property (GObject * object, guint prop_id, leo_sobral@2: GValue * value, GParamSpec * pspec) leo_sobral@2: { leo_sobral@2: GstMythtvSrc *mythtvsrc = GST_MYTHTV_SRC (object); leo_sobral@2: leo_sobral@2: GST_OBJECT_LOCK (mythtvsrc); leo_sobral@2: switch (prop_id) { leo_sobral@2: case PROP_LOCATION: rosfran@289: { rosfran@289: gchar *str = g_strdup (""); leo_sobral@2: rosfran@289: if (mythtvsrc->uri_name == NULL) { rosfran@289: g_free (mythtvsrc->uri_name); rosfran@289: mythtvsrc->uri_name = NULL; rosfran@289: } else { rosfran@289: str = g_strdup (mythtvsrc->uri_name); leo_sobral@2: } rosfran@289: g_value_set_string (value, str); rosfran@289: break; rosfran@289: } leo_sobral@2: #ifndef GST_DISABLE_GST_DEBUG rosfran@40: case PROP_GMYTHTV_DBG: rosfran@289: g_value_set_boolean (value, mythtvsrc->mythtv_msgs_dbg); leo_sobral@2: break; leo_sobral@2: #endif rosfran@40: case PROP_GMYTHTV_VERSION: rosfran@289: { rosfran@289: g_value_set_int (value, mythtvsrc->mythtv_version); rosfran@289: break; rosfran@289: } rosfran@40: case PROP_GMYTHTV_LIVEID: rosfran@289: { rosfran@289: g_value_set_int (value, mythtvsrc->live_tv_id); rosfran@289: break; rosfran@289: } rosfran@40: case PROP_GMYTHTV_LIVE: rosfran@289: g_value_set_boolean (value, mythtvsrc->live_tv); leo_sobral@2: break; rosfran@52: case PROP_GMYTHTV_ENABLE_TIMING_POSITION: rosfran@289: g_value_set_boolean (value, mythtvsrc->enable_timing_position); rosfran@52: break; rosfran@40: case PROP_GMYTHTV_LIVE_CHAINID: rosfran@289: { rosfran@289: gchar *str = g_strdup (""); leo_sobral@2: rosfran@289: if (mythtvsrc->live_chain_id == NULL) { rosfran@289: g_free (mythtvsrc->live_chain_id); rosfran@289: mythtvsrc->live_chain_id = NULL; rosfran@289: } else { rosfran@289: str = g_strdup (mythtvsrc->live_chain_id); leo_sobral@2: } rosfran@289: g_value_set_string (value, str); rosfran@289: break; rosfran@289: } rosfran@70: case PROP_GMYTHTV_CHANNEL_NUM: rosfran@289: { rosfran@308: g_value_set_string (value, mythtvsrc->channel_name); rosfran@289: break; rosfran@289: } leo_sobral@2: default: leo_sobral@2: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); leo_sobral@2: break; leo_sobral@2: } leo_sobral@2: GST_OBJECT_UNLOCK (mythtvsrc); leo_sobral@2: } leo_sobral@2: rosfran@40: static gboolean leo_sobral@2: plugin_init (GstPlugin * plugin) leo_sobral@2: { leo_sobral@2: return gst_element_register (plugin, "mythtvsrc", GST_RANK_NONE, leo_sobral@2: GST_TYPE_MYTHTV_SRC); leo_sobral@2: } leo_sobral@2: leo_sobral@2: GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, leo_sobral@2: GST_VERSION_MINOR, leo_sobral@2: "mythtv", leo_sobral@2: "lib MythTV src", rosfran@312: plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); leo_sobral@2: leo_sobral@2: leo_sobral@2: /*** GSTURIHANDLER INTERFACE *************************************************/ rosfran@289: static guint leo_sobral@2: gst_mythtv_src_uri_get_type (void) leo_sobral@2: { leo_sobral@2: return GST_URI_SRC; leo_sobral@2: } leo_sobral@2: rosfran@289: static gchar ** leo_sobral@2: gst_mythtv_src_uri_get_protocols (void) leo_sobral@2: { leo_sobral@2: static gchar *protocols[] = { "myth", "myths", NULL }; leo_sobral@2: leo_sobral@2: return protocols; leo_sobral@2: } leo_sobral@2: rosfran@289: static const gchar * leo_sobral@2: gst_mythtv_src_uri_get_uri (GstURIHandler * handler) leo_sobral@2: { leo_sobral@2: GstMythtvSrc *src = GST_MYTHTV_SRC (handler); leo_sobral@2: leo_sobral@2: return src->uri_name; leo_sobral@2: } leo_sobral@2: rosfran@289: static gboolean leo_sobral@2: gst_mythtv_src_uri_set_uri (GstURIHandler * handler, const gchar * uri) leo_sobral@2: { leo_sobral@2: GstMythtvSrc *src = GST_MYTHTV_SRC (handler); leo_sobral@2: leo_sobral@2: gchar *protocol; leo_sobral@2: leo_sobral@2: protocol = gst_uri_get_protocol (uri); leo_sobral@2: if ((strcmp (protocol, "myth") != 0) && (strcmp (protocol, "myths") != 0)) { leo_sobral@2: g_free (protocol); leo_sobral@2: return FALSE; leo_sobral@2: } leo_sobral@2: g_free (protocol); leo_sobral@2: g_object_set (src, "location", uri, NULL); leo_sobral@2: leo_sobral@2: return TRUE; leo_sobral@2: } leo_sobral@2: rosfran@289: static void leo_sobral@2: gst_mythtv_src_uri_handler_init (gpointer g_iface, gpointer iface_data) leo_sobral@2: { leo_sobral@2: GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; leo_sobral@2: leo_sobral@2: iface->get_type = gst_mythtv_src_uri_get_type; leo_sobral@2: iface->get_protocols = gst_mythtv_src_uri_get_protocols; leo_sobral@2: iface->get_uri = gst_mythtv_src_uri_get_uri; leo_sobral@2: iface->set_uri = gst_mythtv_src_uri_set_uri; leo_sobral@2: } leo_sobral@2: rosfran@289: void leo_sobral@2: size_header_handler (void *userdata, const char *value) leo_sobral@2: { leo_sobral@2: GstMythtvSrc *src = GST_MYTHTV_SRC (userdata); leo_sobral@2: leo_sobral@2: GST_DEBUG_OBJECT (src, "content size = %lld bytes", src->content_size); leo_sobral@2: }