renatofilho@754: /* renatofilho@754: * GStreamer MythTV Plug-in Copyright (C) <2006> Rosfran Borges renatofilho@754: * This library is free software; you can renatofilho@754: * redistribute it and/or modify it under the terms of the GNU Library renatofilho@754: * General Public License as published by the Free Software Foundation; renatofilho@754: * either version 2 of the License, or (at your option) any later version. renatofilho@754: * This library is distributed in the hope that it will be useful, but renatofilho@754: * WITHOUT ANY WARRANTY; without even the implied warranty of renatofilho@754: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library renatofilho@754: * General Public License for more details. You should have received a copy renatofilho@754: * of the GNU Library General Public License along with this library; if renatofilho@754: * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite renatofilho@754: * 330, Boston, MA 02111-1307, USA. renatofilho@608: */ renatofilho@608: /** renatofilho@608: * SECTION:element-mythtvsrc renatofilho@608: * renatofilho@608: * renatofilho@608: * renatofilho@608: * MythTVSrc allows to access a remote MythTV backend streaming Video/Audio server, renatofilho@608: * and to render audio and video content through a TCP/IP connection to a specific renatofilho@608: * port on this server, and based on a known MythTV protocol that is based on renatofilho@608: * some message passing, such as REQUEST_BLOCK on a specified number of bytes, to get renatofilho@608: * some chunk of remote file data. renatofilho@608: * You should pass the information aboute the remote MythTV backend server renatofilho@608: * through the location property. renatofilho@608: * renatofilho@608: * Examples renatofilho@608: * renatofilho@608: * If you want to get the LiveTV content (set channel, TV tuner, RemoteEncoder, renatofilho@608: * Recorder), renatofilho@608: * put the following URI: renatofilho@608: * renatofilho@608: * renatofilho@608: * myth://xxx.xxx.xxx.xxx:6543/livetv?channel=BBC renatofilho@608: * renatofilho@608: * renatofilho@608: * This URI will say to the gmyth library to configure the Recorder instance (used to renatofilho@608: * change the channel, start the TV multimedia content transmition, etc.), using renatofilho@608: * the IP address (xxx.xxx.xxx.xxx) and port number (6543) of the MythTV backend renatofilho@608: * server, and setting the channel name to "BBC". renatofilho@608: * renatofilho@608: * To get a already recorded the MythTV NUV file, put the following URI: renatofilho@608: * renatofilho@608: * renatofilho@608: * myth://xxx.xxx.xxx.xxx:6543/filename.nuv renatofilho@608: * renatofilho@608: * renatofilho@608: * This URI will say to the gmyth library to configure the Recorder instance (used to renatofilho@608: * change the channel, start the TV multimedia content transmition, etc.), using renatofilho@608: * the IP address (xxx.xxx.xxx.xxx) and port number (6543) of the MythTV backend renatofilho@608: * server, and setting the channel name to "BBC". renatofilho@608: * renatofilho@608: * Another possible way to use the LiveTV content, and just in the case you want to renatofilho@608: * use the mysql database, put the location URI in the following format: renatofilho@608: * renatofilho@608: * renatofilho@608: * myth://mythtv:mythtv@xxx.xxx.xxx.xxx:6543/?mythconverg&channel=9 renatofilho@608: * renatofilho@608: * renatofilho@608: * Where the first field is the protocol (myth), the second and third are user renatofilho@608: * name (mythtv) and password (mythtv), then backend host name and port number, renatofilho@608: * and the last field is the database name (mythconverg). renatofilho@608: * renatofilho@608: * renatofilho@608: */ renatofilho@608: #ifdef HAVE_CONFIG_H renatofilho@608: #include "config.h" renatofilho@608: #endif renatofilho@608: renatofilho@608: #include "gstmythtvsrc.h" renatofilho@608: #include renatofilho@608: #include renatofilho@608: #include renatofilho@608: #include renatofilho@608: renatofilho@608: #include renatofilho@608: #include renatofilho@608: renatofilho@608: #include renatofilho@608: #include renatofilho@608: renatofilho@751: GST_DEBUG_CATEGORY_STATIC(mythtvsrc_debug); renatofilho@608: #define GST_GMYTHTV_ID_NUM 1 renatofilho@608: #define GST_GMYTHTV_CHANNEL_DEFAULT_NUM (-1) renatofilho@812: #define GMYTHTV_VERSION_DEFAULT 30 renatofilho@608: #define GMYTHTV_TRANSFER_MAX_WAITS 100 renatofilho@608: #define GMYTHTV_TRANSFER_MAX_RESENDS 2 renatofilho@608: #define GMYTHTV_TRANSFER_MAX_BUFFER (128*1024) renatofilho@822: #define READ_SIZE (4*1024) renatofilho@608: #define GST_FLOW_ERROR_NO_DATA (-101) renatofilho@608: renatofilho@608: static const GstElementDetails gst_mythtv_src_details = renatofilho@751: GST_ELEMENT_DETAILS("MythTV client source", renatofilho@754: "Source/Network", renatofilho@754: "Control and receive data as a client over the network " renatofilho@754: "via raw socket connections using the MythTV protocol", renatofilho@754: "Rosfran Borges "); renatofilho@608: renatofilho@751: static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE("src", renatofilho@754: GST_PAD_SRC, renatofilho@754: GST_PAD_ALWAYS, renatofilho@754: GST_STATIC_CAPS_ANY); renatofilho@754: /* renatofilho@754: * GST_STATIC_CAPS ("video/x-nuv")); renatofilho@754: */ renatofilho@608: renatofilho@754: enum { renatofilho@754: PROP_0, renatofilho@754: PROP_LOCATION, renatofilho@754: PROP_GMYTHTV_VERSION, renatofilho@754: PROP_GMYTHTV_LIVE, renatofilho@754: PROP_GMYTHTV_LIVEID, renatofilho@754: PROP_GMYTHTV_LIVE_CHAINID, renatofilho@754: PROP_GMYTHTV_ENABLE_TIMING_POSITION, renatofilho@812: PROP_GMYTHTV_CHANNEL_NUM renatofilho@608: }; renatofilho@608: renatofilho@754: static void gst_mythtv_src_clear(GstMythtvSrc * mythtv_src); renatofilho@608: renatofilho@754: static void gst_mythtv_src_finalize(GObject * gobject); renatofilho@608: renatofilho@751: static GstFlowReturn gst_mythtv_src_create(GstPushSrc * psrc, renatofilho@754: GstBuffer ** outbuf); renatofilho@608: renatofilho@751: static gboolean gst_mythtv_src_start(GstBaseSrc * bsrc); renatofilho@751: static gboolean gst_mythtv_src_stop(GstBaseSrc * bsrc); renatofilho@751: static gboolean gst_mythtv_src_get_size(GstBaseSrc * bsrc, guint64 * size); renatofilho@751: static gboolean gst_mythtv_src_is_seekable(GstBaseSrc * push_src); renatofilho@608: renatofilho@751: static gboolean gst_mythtv_src_do_seek(GstBaseSrc * base, renatofilho@754: GstSegment * segment); renatofilho@608: renatofilho@754: static GstStateChangeReturn renatofilho@754: gst_mythtv_src_change_state(GstElement * element, renatofilho@754: GstStateChange transition); renatofilho@608: renatofilho@754: static void gst_mythtv_src_set_property(GObject * object, renatofilho@754: guint prop_id, renatofilho@754: const GValue * value, renatofilho@754: GParamSpec * pspec); renatofilho@754: static void gst_mythtv_src_get_property(GObject * object, renatofilho@754: guint prop_id, GValue * value, renatofilho@754: GParamSpec * pspec); renatofilho@608: renatofilho@754: static void gst_mythtv_src_uri_handler_init(gpointer g_iface, renatofilho@754: gpointer iface_data); renatofilho@608: renatofilho@754: static gboolean gst_mythtv_src_handle_query(GstPad * pad, renatofilho@754: GstQuery * query); renatofilho@608: renatofilho@754: static gboolean gst_mythtv_src_handle_event(GstPad * pad, renatofilho@754: GstEvent * event); renatofilho@608: renatofilho@751: static GMythFileReadResult do_read_request_response(GstMythtvSrc * src, renatofilho@754: guint size, renatofilho@754: GByteArray * data_ptr); renatofilho@608: renatofilho@608: static void renatofilho@751: _urihandler_init(GType type) renatofilho@608: { renatofilho@754: static const GInterfaceInfo urihandler_info = { renatofilho@754: gst_mythtv_src_uri_handler_init, renatofilho@754: NULL, renatofilho@754: NULL renatofilho@754: }; renatofilho@608: renatofilho@754: g_type_add_interface_static(type, GST_TYPE_URI_HANDLER, renatofilho@754: &urihandler_info); renatofilho@608: renatofilho@754: GST_DEBUG_CATEGORY_INIT(mythtvsrc_debug, "mythtvsrc", 0, "MythTV src"); renatofilho@608: } renatofilho@608: renatofilho@751: GST_BOILERPLATE_FULL(GstMythtvSrc, gst_mythtv_src, GstPushSrc, renatofilho@754: GST_TYPE_PUSH_SRC, _urihandler_init) renatofilho@754: static void gst_mythtv_src_base_init(gpointer g_class) renatofilho@751: { renatofilho@754: GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); renatofilho@608: renatofilho@754: gst_element_class_add_pad_template(element_class, renatofilho@754: gst_static_pad_template_get renatofilho@754: (&srctemplate)); renatofilho@608: renatofilho@754: gst_element_class_set_details(element_class, &gst_mythtv_src_details); renatofilho@608: renatofilho@754: element_class->change_state = gst_mythtv_src_change_state; renatofilho@608: renatofilho@608: } renatofilho@608: renatofilho@608: static void renatofilho@751: gst_mythtv_src_class_init(GstMythtvSrcClass * klass) renatofilho@608: { renatofilho@754: GObjectClass *gobject_class; renatofilho@754: GstPushSrcClass *gstpushsrc_class; renatofilho@754: GstBaseSrcClass *gstbasesrc_class; renatofilho@608: renatofilho@754: gobject_class = (GObjectClass *) klass; renatofilho@754: gstbasesrc_class = (GstBaseSrcClass *) klass; renatofilho@754: gstpushsrc_class = (GstPushSrcClass *) klass; renatofilho@608: renatofilho@754: gobject_class->set_property = gst_mythtv_src_set_property; renatofilho@754: gobject_class->get_property = gst_mythtv_src_get_property; renatofilho@754: gobject_class->finalize = gst_mythtv_src_finalize; renatofilho@608: renatofilho@754: g_object_class_install_property renatofilho@754: (gobject_class, PROP_LOCATION, renatofilho@754: g_param_spec_string("location", "Location", renatofilho@754: "The location. In the form:" renatofilho@754: "\n\t\t\tmyth://a.com/file.nuv" renatofilho@754: "\n\t\t\tmyth://a.com:23223/file.nuv " renatofilho@754: "\n\t\t\ta.com/file.nuv - default scheme 'myth'", renatofilho@754: "", G_PARAM_READWRITE)); renatofilho@608: renatofilho@754: g_object_class_install_property renatofilho@754: (gobject_class, PROP_GMYTHTV_VERSION, renatofilho@754: g_param_spec_int("mythtv-version", "mythtv-version", renatofilho@754: "Change MythTV version", 26, 30, 26, renatofilho@754: G_PARAM_READWRITE)); renatofilho@608: renatofilho@754: g_object_class_install_property renatofilho@754: (gobject_class, PROP_GMYTHTV_LIVEID, renatofilho@754: g_param_spec_int("mythtv-live-id", "mythtv-live-id", renatofilho@754: "Change MythTV version", renatofilho@754: 0, 200, GST_GMYTHTV_ID_NUM, G_PARAM_READWRITE)); renatofilho@608: renatofilho@754: g_object_class_install_property renatofilho@754: (gobject_class, PROP_GMYTHTV_LIVE_CHAINID, renatofilho@754: g_param_spec_string("mythtv-live-chainid", "mythtv-live-chainid", renatofilho@754: "Sets the MythTV chain ID (from TV Chain)", renatofilho@754: "", G_PARAM_READWRITE)); renatofilho@608: renatofilho@754: g_object_class_install_property renatofilho@754: (gobject_class, PROP_GMYTHTV_LIVE, renatofilho@754: g_param_spec_boolean("mythtv-live", "mythtv-live", renatofilho@754: "Enable MythTV Live TV content streaming", renatofilho@754: FALSE, G_PARAM_READWRITE)); renatofilho@608: renatofilho@754: g_object_class_install_property renatofilho@754: (gobject_class, PROP_GMYTHTV_ENABLE_TIMING_POSITION, renatofilho@754: g_param_spec_boolean("mythtv-enable-timing-position", renatofilho@754: "mythtv-enable-timing-position", renatofilho@754: "Enable MythTV Live TV content size continuous updating", renatofilho@754: FALSE, G_PARAM_READWRITE)); renatofilho@608: renatofilho@754: g_object_class_install_property renatofilho@754: (gobject_class, PROP_GMYTHTV_CHANNEL_NUM, renatofilho@754: g_param_spec_string("mythtv-channel", "mythtv-channel", renatofilho@754: "Change MythTV channel number", renatofilho@754: "", G_PARAM_READWRITE)); renatofilho@608: renatofilho@754: gstbasesrc_class->start = gst_mythtv_src_start; renatofilho@754: gstbasesrc_class->stop = gst_mythtv_src_stop; renatofilho@754: gstbasesrc_class->get_size = gst_mythtv_src_get_size; renatofilho@754: gstbasesrc_class->is_seekable = gst_mythtv_src_is_seekable; renatofilho@754: gstbasesrc_class->do_seek = gst_mythtv_src_do_seek; renatofilho@754: gstpushsrc_class->create = gst_mythtv_src_create; renatofilho@608: renatofilho@754: GST_DEBUG_CATEGORY_INIT(mythtvsrc_debug, "mythtvsrc", 0, renatofilho@754: "MythTV Client Source"); renatofilho@608: } renatofilho@608: renatofilho@608: static void renatofilho@751: gst_mythtv_src_init(GstMythtvSrc * this, GstMythtvSrcClass * g_class) renatofilho@608: { renatofilho@754: this->file = NULL; renatofilho@754: this->unique_setup = FALSE; renatofilho@754: this->mythtv_version = GMYTHTV_VERSION_DEFAULT; renatofilho@754: this->state = GST_MYTHTV_SRC_FILE_TRANSFER; renatofilho@754: this->bytes_read = 0; renatofilho@754: this->prev_content_size = 0; renatofilho@754: this->content_size = 0; renatofilho@754: this->read_offset = 0; renatofilho@754: this->content_size_last = 0; renatofilho@754: this->live_tv = FALSE; renatofilho@754: this->enable_timing_position = FALSE; renatofilho@754: this->update_prog_chain = FALSE; renatofilho@754: this->user_agent = g_strdup("mythtvsrc"); renatofilho@754: this->update_prog_chain = FALSE; renatofilho@754: this->channel_name = NULL; renatofilho@754: this->eos = FALSE; renatofilho@754: this->wait_to_transfer = 0; renatofilho@754: gst_base_src_set_format(GST_BASE_SRC(this), GST_FORMAT_BYTES); renatofilho@754: gst_pad_set_event_function(GST_BASE_SRC_PAD(GST_BASE_SRC(this)), renatofilho@754: gst_mythtv_src_handle_event); renatofilho@754: gst_pad_set_query_function(GST_BASE_SRC_PAD(GST_BASE_SRC(this)), renatofilho@754: gst_mythtv_src_handle_query); renatofilho@608: renatofilho@608: } renatofilho@608: renatofilho@608: static void renatofilho@751: gst_mythtv_src_clear(GstMythtvSrc * mythtv_src) renatofilho@608: { renatofilho@754: mythtv_src->unique_setup = FALSE; renatofilho@608: renatofilho@754: if (mythtv_src->spawn_livetv) { renatofilho@754: g_object_unref(mythtv_src->spawn_livetv); renatofilho@754: mythtv_src->spawn_livetv = NULL; renatofilho@754: } renatofilho@608: renatofilho@754: if (mythtv_src->file) { renatofilho@754: g_object_unref(mythtv_src->file); renatofilho@754: mythtv_src->file = NULL; renatofilho@754: } renatofilho@608: renatofilho@754: if (mythtv_src->backend_info) { renatofilho@754: g_object_unref(mythtv_src->backend_info); renatofilho@754: mythtv_src->backend_info = NULL; renatofilho@754: } renatofilho@608: } renatofilho@608: renatofilho@608: static void renatofilho@751: gst_mythtv_src_finalize(GObject * gobject) renatofilho@608: { renatofilho@754: GstMythtvSrc *this = GST_MYTHTV_SRC(gobject); renatofilho@608: renatofilho@754: gst_mythtv_src_clear(this); renatofilho@608: renatofilho@754: if (this->uri_name) { renatofilho@754: g_free(this->uri_name); renatofilho@754: this->uri_name = NULL; renatofilho@754: } renatofilho@608: renatofilho@754: if (this->user_agent) { renatofilho@754: g_free(this->user_agent); renatofilho@754: this->user_agent = NULL; renatofilho@754: } renatofilho@608: renatofilho@754: G_OBJECT_CLASS(parent_class)->finalize(gobject); renatofilho@608: } renatofilho@608: renatofilho@758: static GMythFileReadResult renatofilho@751: do_read_request_response(GstMythtvSrc * src, guint size, renatofilho@754: GByteArray * data_ptr) renatofilho@608: { renatofilho@754: gint read = 0; renatofilho@754: guint sizetoread = size; renatofilho@754: gint max_iters = GMYTHTV_TRANSFER_MAX_RESENDS; renatofilho@754: GMythFileReadResult result; renatofilho@608: renatofilho@754: GST_LOG_OBJECT(src, "Starting: Reading %d bytes...", sizetoread); renatofilho@608: renatofilho@754: /* renatofilho@754: * Loop sending the Myth File Transfer request: Retry whilst renatofilho@754: * authentication fails and we supply it. renatofilho@754: */ renatofilho@608: renatofilho@754: while (sizetoread == size && --max_iters > 0) { renatofilho@754: /* renatofilho@754: * if ( gmyth_backend_info_is_local_file(src->backend_info) ) renatofilho@754: */ renatofilho@754: if (IS_GMYTH_FILE_LOCAL(src->file)) renatofilho@754: result = gmyth_file_local_read(GMYTH_FILE_LOCAL(src->file), renatofilho@754: data_ptr, sizetoread, renatofilho@754: src->live_tv); renatofilho@754: else if (IS_GMYTH_FILE_TRANSFER(src->file)) renatofilho@754: result = renatofilho@754: gmyth_file_transfer_read(GMYTH_FILE_TRANSFER(src->file), renatofilho@754: data_ptr, sizetoread, renatofilho@754: src->live_tv); renatofilho@608: renatofilho@754: if (data_ptr->len > 0) { renatofilho@754: read += data_ptr->len; renatofilho@754: sizetoread -= data_ptr->len; renatofilho@754: } else if (data_ptr->len <= 0) { renatofilho@754: if (src->live_tv == FALSE) { renatofilho@754: result = GMYTH_FILE_READ_EOF; renatofilho@754: goto eos; renatofilho@754: } else { renatofilho@754: if (result == GMYTH_FILE_READ_ERROR) { /* -314 */ renatofilho@754: GST_INFO_OBJECT(src, renatofilho@754: "[LiveTV] FileTransfer READ_ERROR!"); renatofilho@754: } renatofilho@754: goto done; renatofilho@754: } renatofilho@754: } renatofilho@754: /* renatofilho@754: * else if (data_ptr->len == 0) goto done; renatofilho@754: */ renatofilho@754: if (read == sizetoread) renatofilho@754: goto done; renatofilho@754: } renatofilho@608: renatofilho@754: if ((read < 0 && !src->live_tv) || max_iters == 0) { renatofilho@754: result = GMYTH_FILE_READ_EOF; renatofilho@754: goto eos; renatofilho@754: } renatofilho@608: renatofilho@754: goto done; renatofilho@608: renatofilho@754: eos: renatofilho@754: src->eos = TRUE; renatofilho@608: renatofilho@754: done: renatofilho@754: return result; renatofilho@608: } renatofilho@608: renatofilho@758: static GstFlowReturn renatofilho@751: gst_mythtv_src_create(GstPushSrc * psrc, GstBuffer ** outbuf) renatofilho@608: { renatofilho@822: GstMythtvSrc *src; renatofilho@754: GstFlowReturn ret = GST_FLOW_OK; renatofilho@822: GByteArray *buffer; renatofilho@822: gint buffer_remain = 0; renatofilho@822: GMythFileReadResult result = GMYTH_FILE_READ_OK; renatofilho@822: gboolean buffering = FALSE; renatofilho@608: renatofilho@754: src = GST_MYTHTV_SRC(psrc); renatofilho@608: renatofilho@822: buffer = g_byte_array_new (); renatofilho@822: result = do_read_request_response(src, READ_SIZE, buffer); renatofilho@822: if (result == GMYTH_FILE_READ_ERROR) renatofilho@822: goto read_error; renatofilho@608: renatofilho@608: renatofilho@754: *outbuf = gst_buffer_new(); renatofilho@822: GST_BUFFER_SIZE(*outbuf) = buffer->len; renatofilho@754: GST_BUFFER_MALLOCDATA(*outbuf) = g_malloc0(GST_BUFFER_SIZE(*outbuf)); renatofilho@754: GST_BUFFER_DATA(*outbuf) = GST_BUFFER_MALLOCDATA(*outbuf); renatofilho@822: g_memmove(GST_BUFFER_DATA((*outbuf)), buffer->data, renatofilho@754: GST_BUFFER_SIZE(*outbuf)); renatofilho@754: GST_BUFFER_OFFSET(*outbuf) = src->read_offset; renatofilho@754: GST_BUFFER_OFFSET_END(*outbuf) = renatofilho@754: src->read_offset + GST_BUFFER_SIZE(*outbuf); renatofilho@608: renatofilho@754: src->read_offset += GST_BUFFER_SIZE(*outbuf); renatofilho@754: src->bytes_read += GST_BUFFER_SIZE(*outbuf); renatofilho@608: renatofilho@822: g_byte_array_free (buffer, TRUE); renatofilho@608: renatofilho@822: if (result == GMYTH_FILE_READ_NEXT_PROG_CHAIN) { renatofilho@822: GstPad *peer; renatofilho@608: renatofilho@822: peer = gst_pad_get_peer (GST_BASE_SRC_PAD (GST_BASE_SRC (psrc))); renatofilho@822: gst_pad_send_event (peer, renatofilho@822: gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0)); renatofilho@608: renatofilho@822: gst_object_unref (peer); renatofilho@754: } renatofilho@608: renatofilho@822: if (src->eos || renatofilho@822: (!src->live_tv && (src->bytes_read >= src->content_size))) renatofilho@822: ret = GST_FLOW_UNEXPECTED; renatofilho@822: renatofilho@822: return ret; renatofilho@822: renatofilho@822: read_error: renatofilho@822: GST_ELEMENT_ERROR(src, RESOURCE, READ, renatofilho@822: (NULL), ("Could not read any bytes (%i, %s)", renatofilho@822: read, src->uri_name)); renatofilho@822: return GST_FLOW_ERROR; renatofilho@608: } renatofilho@608: renatofilho@608: gint64 renatofilho@751: gst_mythtv_src_get_position(GstMythtvSrc * src) renatofilho@608: { renatofilho@608: renatofilho@754: gint64 size_tmp = 0; renatofilho@754: guint max_tries = 2; renatofilho@608: renatofilho@754: if (src->live_tv == TRUE && (abs(src->content_size - src->bytes_read) < renatofilho@754: GMYTHTV_TRANSFER_MAX_BUFFER)) { renatofilho@608: renatofilho@754: get_file_pos: renatofilho@754: g_usleep(10); renatofilho@754: size_tmp = renatofilho@754: gmyth_recorder_get_file_position(src->spawn_livetv->recorder); renatofilho@754: if (size_tmp > (src->content_size + GMYTHTV_TRANSFER_MAX_BUFFER)) renatofilho@754: src->content_size = size_tmp; renatofilho@754: else if (size_tmp > 0 && --max_tries > 0) renatofilho@754: goto get_file_pos; renatofilho@754: GST_LOG_OBJECT(src, "GET_POSITION: file_position = %lld", renatofilho@754: size_tmp); renatofilho@754: /* renatofilho@754: * sets the last content size amount before it can be updated renatofilho@754: */ renatofilho@754: src->prev_content_size = src->content_size; renatofilho@754: } renatofilho@608: renatofilho@754: return src->content_size; renatofilho@608: renatofilho@608: } renatofilho@608: renatofilho@758: static gboolean renatofilho@751: gst_mythtv_src_do_seek(GstBaseSrc * base, GstSegment * segment) renatofilho@608: { renatofilho@754: GstMythtvSrc *src = GST_MYTHTV_SRC(base); renatofilho@754: gint64 new_offset = -1; renatofilho@754: gint64 actual_seek = segment->start; renatofilho@754: gboolean ret = TRUE; renatofilho@608: renatofilho@754: GST_LOG_OBJECT(src, "seek, segment: %" GST_SEGMENT_FORMAT, segment); renatofilho@608: renatofilho@754: if (segment->format == GST_FORMAT_TIME) { renatofilho@754: goto done; renatofilho@754: } renatofilho@754: GST_LOG_OBJECT(src, renatofilho@754: "Trying to seek at the value (actual_seek = %lld, read_offset = %lld)", renatofilho@754: actual_seek, src->read_offset); renatofilho@754: /* renatofilho@754: * verify if it needs to seek renatofilho@754: */ renatofilho@754: if (src->read_offset != actual_seek) { renatofilho@608: renatofilho@754: /* renatofilho@754: * if ( gmyth_backend_info_is_local_file(src->backend_info) ) renatofilho@754: */ renatofilho@754: if (IS_GMYTH_FILE_LOCAL(src->file)) renatofilho@754: new_offset = renatofilho@754: gmyth_file_local_seek(GMYTH_FILE_LOCAL(src->file), renatofilho@754: segment->start, G_SEEK_SET); renatofilho@754: else if (IS_GMYTH_FILE_TRANSFER(src->file)) renatofilho@754: new_offset = renatofilho@754: gmyth_file_transfer_seek(GMYTH_FILE_TRANSFER(src->file), renatofilho@754: segment->start, SEEK_SET); renatofilho@608: renatofilho@754: GST_LOG_OBJECT(src, renatofilho@754: "Segment offset start = %lld, SRC Offset = %lld, NEW actual backend SEEK Offset = %lld.", renatofilho@754: segment->start, src->read_offset, new_offset); renatofilho@754: if (G_UNLIKELY(new_offset < 0)) { renatofilho@754: ret = FALSE; renatofilho@754: if (!src->live_tv) renatofilho@754: goto eos; renatofilho@754: } renatofilho@608: renatofilho@754: src->read_offset = new_offset; renatofilho@608: renatofilho@754: if (ret == FALSE) { renatofilho@754: GST_INFO_OBJECT(src, "Failed to set the SEEK on segment!"); renatofilho@754: } renatofilho@751: renatofilho@754: } renatofilho@608: renatofilho@754: done: renatofilho@754: return ret; renatofilho@608: renatofilho@754: eos: renatofilho@754: { renatofilho@754: GST_DEBUG_OBJECT(src, "EOS found on seeking!!!"); renatofilho@754: return FALSE; renatofilho@754: } renatofilho@608: renatofilho@608: } renatofilho@608: renatofilho@754: /* renatofilho@754: * create a socket for connecting to remote server renatofilho@754: */ renatofilho@758: static gboolean renatofilho@751: gst_mythtv_src_start(GstBaseSrc * bsrc) renatofilho@608: { renatofilho@754: GstMythtvSrc *src = GST_MYTHTV_SRC(bsrc); renatofilho@608: renatofilho@754: GString *chain_id_local = NULL; renatofilho@754: GMythURI *gmyth_uri = NULL; renatofilho@754: gboolean ret = TRUE; renatofilho@754: GstMessage *msg; renatofilho@608: renatofilho@754: if (src->unique_setup == FALSE) { renatofilho@754: src->unique_setup = TRUE; renatofilho@754: } else { renatofilho@754: goto done; renatofilho@754: } renatofilho@679: renatofilho@754: gmyth_uri = gmyth_uri_new_with_value(src->uri_name); renatofilho@754: src->backend_info = gmyth_backend_info_new_with_uri(src->uri_name); renatofilho@754: src->live_tv = gmyth_uri_is_livetv(gmyth_uri); renatofilho@822: renatofilho@754: if (src->live_tv) { renatofilho@754: src->spawn_livetv = gmyth_livetv_new(src->backend_info); renatofilho@822: gchar *ch = gmyth_uri_get_channel_name(gmyth_uri); renatofilho@754: if (ch != NULL) renatofilho@754: src->channel_name = ch; renatofilho@608: renatofilho@754: if (src->channel_name != NULL) { renatofilho@754: gboolean result; renatofilho@754: result = renatofilho@754: gmyth_livetv_channel_name_setup(src->spawn_livetv, renatofilho@754: src->channel_name); renatofilho@754: if (result == FALSE) { renatofilho@754: GST_INFO_OBJECT(src, "LiveTV setup felt down on error"); renatofilho@754: ret = FALSE; renatofilho@754: goto init_failed; renatofilho@754: } renatofilho@608: renatofilho@754: } else { renatofilho@754: if (gmyth_livetv_setup(src->spawn_livetv) == FALSE) { renatofilho@754: GST_INFO_OBJECT(src, "LiveTV setup felt down on error"); renatofilho@754: ret = FALSE; renatofilho@754: goto init_failed; renatofilho@754: } renatofilho@754: } renatofilho@608: renatofilho@754: /* renatofilho@754: * testing change channel... renatofilho@754: */ renatofilho@754: /* renatofilho@754: * gmyth_recorder_change_channel( src->spawn_livetv->recorder, renatofilho@754: * CHANNEL_DIRECTION_UP ); renatofilho@754: */ renatofilho@751: renatofilho@754: src->file = renatofilho@754: GMYTH_FILE(gmyth_livetv_create_file_transfer renatofilho@754: (src->spawn_livetv)); renatofilho@751: renatofilho@822: renatofilho@754: if (NULL == src->file) { renatofilho@754: GST_INFO_OBJECT(src, "[LiveTV] FileTransfer equals to NULL"); renatofilho@754: ret = FALSE; renatofilho@754: goto init_failed; renatofilho@754: } renatofilho@608: renatofilho@754: /* renatofilho@754: * Check if the file is local to this specific client renderer renatofilho@754: */ renatofilho@754: if (gmyth_uri_is_local_file(gmyth_uri)) renatofilho@754: ret = gmyth_file_local_open(GMYTH_FILE_LOCAL(src->file)); renatofilho@754: else renatofilho@754: ret = renatofilho@754: gmyth_file_transfer_open(GMYTH_FILE_TRANSFER(src->file), renatofilho@754: src->spawn_livetv->uri != renatofilho@754: NULL ? gmyth_uri_get_path(src-> renatofilho@754: spawn_livetv-> renatofilho@754: uri) : renatofilho@754: src->spawn_livetv->proginfo-> renatofilho@754: pathname->str); renatofilho@608: renatofilho@754: /* renatofilho@754: * sets the mythtvsrc "location" property renatofilho@754: */ renatofilho@754: g_object_set(src, "location", gmyth_file_get_uri(src->file), NULL); renatofilho@608: renatofilho@754: if (!ret) { renatofilho@754: GST_INFO_OBJECT(src, renatofilho@754: "Error: couldn't open the FileTransfer from LiveTV source!"); renatofilho@754: g_object_unref(src->file); renatofilho@754: src->file = NULL; renatofilho@754: goto init_failed; renatofilho@754: } renatofilho@754: } else { renatofilho@751: renatofilho@754: /* renatofilho@754: * Check if the file is local to this specific client renderer, renatofilho@754: * and tries to open a local connection renatofilho@754: */ renatofilho@754: if (gmyth_uri_is_local_file(gmyth_uri)) { renatofilho@754: src->file = renatofilho@754: GMYTH_FILE(gmyth_file_local_new(src->backend_info)); renatofilho@754: ret = gmyth_file_local_open(GMYTH_FILE_LOCAL(src->file)); renatofilho@754: } else { renatofilho@754: src->file = renatofilho@754: GMYTH_FILE(gmyth_file_transfer_new(src->backend_info)); renatofilho@754: ret = renatofilho@754: gmyth_file_transfer_open(GMYTH_FILE_TRANSFER(src->file), renatofilho@754: src->uri_name); renatofilho@754: } renatofilho@751: renatofilho@754: } /* if (else) - recorded FileTransfer */ renatofilho@751: renatofilho@754: if (NULL == src->file) { renatofilho@754: GST_INFO_OBJECT(src, "FileTransfer is NULL"); renatofilho@754: goto init_failed; renatofilho@754: } renatofilho@754: /* renatofilho@754: * GST_INFO_OBJECT( src, "uri = %s", src->spawn_livetv->file); renatofilho@754: */ renatofilho@751: renatofilho@754: if (ret == FALSE) { renatofilho@812: GST_INFO_OBJECT(src, renatofilho@812: "MythTV FileTransfer request failed when setting up socket connection!"); renatofilho@812: goto begin_req_failed; renatofilho@754: } renatofilho@608: renatofilho@754: GST_INFO_OBJECT(src, renatofilho@754: "MythTV FileTransfer filesize = %lld, content_size = %lld!", renatofilho@754: gmyth_file_get_filesize(src->file), src->content_size); renatofilho@608: renatofilho@754: src->content_size = gmyth_file_get_filesize(src->file); renatofilho@608: renatofilho@754: msg = renatofilho@754: gst_message_new_duration(GST_OBJECT(src), GST_FORMAT_BYTES, renatofilho@754: src->content_size); renatofilho@754: gst_element_post_message(GST_ELEMENT(src), msg); renatofilho@674: renatofilho@674: renatofilho@754: src->do_start = FALSE; renatofilho@608: renatofilho@754: gst_pad_push_event(GST_BASE_SRC_PAD(GST_BASE_SRC(src)), renatofilho@754: gst_event_new_new_segment(TRUE, 1.0, renatofilho@754: GST_FORMAT_TIME, 0, renatofilho@754: src->content_size, 0)); renatofilho@608: renatofilho@754: done: renatofilho@754: if (gmyth_uri != NULL) { renatofilho@754: g_object_unref(gmyth_uri); renatofilho@754: gmyth_uri = NULL; renatofilho@754: } renatofilho@608: renatofilho@754: if (chain_id_local != NULL) { renatofilho@754: g_string_free(chain_id_local, TRUE); renatofilho@754: chain_id_local = NULL; renatofilho@754: } renatofilho@608: renatofilho@754: return TRUE; renatofilho@679: renatofilho@754: /* renatofilho@754: * ERRORS renatofilho@754: */ renatofilho@754: init_failed: renatofilho@754: if (gmyth_uri != NULL) { renatofilho@754: g_object_unref(gmyth_uri); renatofilho@754: gmyth_uri = NULL; renatofilho@754: } renatofilho@608: renatofilho@754: if (src->spawn_livetv != NULL) { renatofilho@754: g_object_unref(src->spawn_livetv); renatofilho@754: src->spawn_livetv = NULL; renatofilho@754: } renatofilho@751: renatofilho@754: GST_ELEMENT_ERROR(src, LIBRARY, INIT, renatofilho@754: (NULL), renatofilho@754: ("Could not initialize MythTV library (%i, %s)", ret, renatofilho@754: src->uri_name)); renatofilho@751: renatofilho@751: renatofilho@754: gst_mythtv_src_clear(src); renatofilho@751: renatofilho@754: return FALSE; renatofilho@754: begin_req_failed: renatofilho@754: if (gmyth_uri != NULL) { renatofilho@754: g_object_unref(gmyth_uri); renatofilho@754: gmyth_uri = NULL; renatofilho@754: } renatofilho@751: renatofilho@754: GST_ELEMENT_ERROR(src, LIBRARY, INIT, renatofilho@754: (NULL), renatofilho@754: ("Could not begin request sent to MythTV server (%i, %s)", renatofilho@754: ret, src->uri_name)); renatofilho@754: return FALSE; renatofilho@608: renatofilho@608: } renatofilho@608: renatofilho@758: static gboolean renatofilho@751: gst_mythtv_src_get_size(GstBaseSrc * bsrc, guint64 * size) renatofilho@608: { renatofilho@754: GstMythtvSrc *src = GST_MYTHTV_SRC(bsrc); renatofilho@754: gboolean ret = TRUE; renatofilho@608: renatofilho@754: GST_LOG_OBJECT(src, renatofilho@754: "Differs from previous content size: %d (max.: %d)", renatofilho@754: abs(src->content_size - src->prev_content_size), renatofilho@754: GMYTHTV_TRANSFER_MAX_BUFFER); renatofilho@608: renatofilho@754: if (src->live_tv) { renatofilho@754: ret = FALSE; renatofilho@754: } else if (src->live_tv && src->enable_timing_position renatofilho@754: && (abs(src->content_size - src->bytes_read) < renatofilho@754: GMYTHTV_TRANSFER_MAX_BUFFER)) { renatofilho@608: renatofilho@754: gint64 new_offset = renatofilho@754: gmyth_recorder_get_file_position(src->spawn_livetv->recorder); renatofilho@754: if (new_offset > 0 && new_offset > src->content_size) { renatofilho@754: src->content_size = new_offset; renatofilho@754: } else if (new_offset < src->content_size) { renatofilho@754: src->update_prog_chain = TRUE; renatofilho@754: } renatofilho@608: renatofilho@754: } renatofilho@608: renatofilho@754: *size = src->content_size; renatofilho@754: GST_LOG_OBJECT(src, "Content size = %lld", src->content_size); renatofilho@608: renatofilho@754: return ret; renatofilho@608: renatofilho@608: } renatofilho@608: renatofilho@754: /* renatofilho@754: * close the socket and associated resources used both to recover from renatofilho@754: * errors and go to NULL state renatofilho@754: */ renatofilho@758: static gboolean renatofilho@751: gst_mythtv_src_stop(GstBaseSrc * bsrc) renatofilho@608: { renatofilho@754: GstMythtvSrc *src = GST_MYTHTV_SRC(bsrc); renatofilho@608: renatofilho@754: gst_mythtv_src_clear(src); renatofilho@754: return TRUE; renatofilho@608: } renatofilho@608: renatofilho@758: static gboolean renatofilho@751: gst_mythtv_src_handle_event(GstPad * pad, GstEvent * event) renatofilho@608: { renatofilho@754: GstMythtvSrc *src = GST_MYTHTV_SRC(GST_PAD_PARENT(pad)); renatofilho@754: gint64 cont_size = 0; renatofilho@758: gboolean ret = TRUE; renatofilho@608: renatofilho@754: switch (GST_EVENT_TYPE(event)) { renatofilho@812: case GST_EVENT_EOS: renatofilho@754: if (src->live_tv) { renatofilho@812: cont_size = gst_mythtv_src_get_position(src); renatofilho@812: if (cont_size > src->content_size) { renatofilho@812: src->content_size = cont_size; renatofilho@812: src->eos = FALSE; renatofilho@812: } else { renatofilho@812: src->eos = TRUE; renatofilho@812: gst_element_set_state(GST_ELEMENT(src), GST_STATE_NULL); renatofilho@812: gst_element_set_locked_state(GST_ELEMENT(src), FALSE); renatofilho@812: } renatofilho@754: } renatofilho@754: break; renatofilho@754: default: renatofilho@754: ret = gst_pad_event_default(pad, event); renatofilho@754: } renatofilho@608: renatofilho@758: GST_DEBUG_OBJECT (src, "HANDLE EVENT %d", ret); renatofilho@754: return ret; renatofilho@608: } renatofilho@608: renatofilho@758: static gboolean renatofilho@751: gst_mythtv_src_is_seekable(GstBaseSrc * push_src) renatofilho@608: { renatofilho@754: return TRUE; renatofilho@608: } renatofilho@608: renatofilho@758: static gboolean renatofilho@751: gst_mythtv_src_handle_query(GstPad * pad, GstQuery * query) renatofilho@608: { renatofilho@754: gboolean res = FALSE; renatofilho@754: GstMythtvSrc *myth = GST_MYTHTV_SRC(gst_pad_get_parent(pad)); renatofilho@754: GstFormat formt; renatofilho@608: renatofilho@674: renatofilho@754: switch (GST_QUERY_TYPE(query)) { renatofilho@812: case GST_QUERY_POSITION: renatofilho@812: gst_query_parse_position(query, &formt, NULL); renatofilho@812: if (formt == GST_FORMAT_BYTES) { renatofilho@812: gst_query_set_position(query, formt, myth->read_offset); renatofilho@812: GST_DEBUG_OBJECT(myth, "POS %" G_GINT64_FORMAT, renatofilho@812: myth->read_offset); renatofilho@812: res = TRUE; renatofilho@812: } else if (formt == GST_FORMAT_TIME) { renatofilho@812: res = gst_pad_query_default(pad, query); renatofilho@754: } renatofilho@812: break; renatofilho@812: case GST_QUERY_DURATION: renatofilho@812: gst_query_parse_duration(query, &formt, NULL); renatofilho@812: if (formt == GST_FORMAT_BYTES) { renatofilho@812: gint64 size = myth->content_size; renatofilho@812: renatofilho@812: gst_query_set_duration(query, GST_FORMAT_BYTES, 10); renatofilho@812: GST_DEBUG_OBJECT(myth, "SIZE %" G_GINT64_FORMAT, size); renatofilho@812: res = TRUE; renatofilho@812: } else if (formt == GST_FORMAT_TIME) { renatofilho@812: res = gst_pad_query_default(pad, query); renatofilho@754: } renatofilho@812: break; renatofilho@812: default: renatofilho@812: res = gst_pad_query_default(pad, query); renatofilho@812: break; renatofilho@754: } renatofilho@608: renatofilho@754: gst_object_unref(myth); renatofilho@608: renatofilho@754: return res; renatofilho@608: } renatofilho@608: renatofilho@758: static GstStateChangeReturn renatofilho@754: gst_mythtv_src_change_state(GstElement * element, renatofilho@754: GstStateChange transition) renatofilho@608: { renatofilho@754: GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE; renatofilho@758: GstMythtvSrc *src = GST_MYTHTV_SRC(element); renatofilho@608: renatofilho@754: switch (transition) { renatofilho@754: case GST_STATE_CHANGE_NULL_TO_READY: renatofilho@754: break; renatofilho@754: case GST_STATE_CHANGE_READY_TO_PAUSED: renatofilho@812: if (!src->uri_name) { renatofilho@812: GST_WARNING_OBJECT (src, "Invalid location"); renatofilho@812: return ret; renatofilho@812: } renatofilho@812: break; renatofilho@754: case GST_STATE_CHANGE_PAUSED_TO_PLAYING: renatofilho@812: if (src->live_tv) { renatofilho@812: if (!gmyth_recorder_send_frontend_ready_command renatofilho@812: (src->spawn_livetv->recorder)) renatofilho@812: GST_WARNING_OBJECT(src, renatofilho@812: "Couldn't send the FRONTEND_READY message to the backend!"); renatofilho@812: else renatofilho@812: GST_DEBUG_OBJECT(src, "FRONTEND_READY was sent to the backend"); renatofilho@812: } renatofilho@812: break; renatofilho@754: default: renatofilho@754: break; renatofilho@754: } renatofilho@692: renatofilho@758: ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); renatofilho@758: if (ret == GST_STATE_CHANGE_FAILURE) { renatofilho@754: return ret; renatofilho@758: } renatofilho@692: renatofilho@692: renatofilho@754: switch (transition) { renatofilho@754: case GST_STATE_CHANGE_READY_TO_NULL: renatofilho@754: gst_mythtv_src_clear(src); renatofilho@754: break; renatofilho@754: case GST_STATE_CHANGE_PLAYING_TO_PAUSED: renatofilho@754: case GST_STATE_CHANGE_PAUSED_TO_READY: renatofilho@754: break; renatofilho@754: default: renatofilho@754: break; renatofilho@754: } renatofilho@608: renatofilho@754: return ret; renatofilho@608: } renatofilho@608: renatofilho@608: static void renatofilho@751: gst_mythtv_src_set_property(GObject * object, guint prop_id, renatofilho@754: const GValue * value, GParamSpec * pspec) renatofilho@608: { renatofilho@754: GstMythtvSrc *mythtvsrc = GST_MYTHTV_SRC(object); renatofilho@608: renatofilho@754: GST_OBJECT_LOCK(mythtvsrc); renatofilho@754: switch (prop_id) { renatofilho@812: case PROP_LOCATION: renatofilho@812: if (!g_value_get_string(value)) { renatofilho@812: GST_WARNING("location property cannot be NULL"); renatofilho@812: break; renatofilho@812: } renatofilho@608: renatofilho@812: if (mythtvsrc->uri_name != NULL) { renatofilho@812: g_free(mythtvsrc->uri_name); renatofilho@812: mythtvsrc->uri_name = NULL; renatofilho@812: } renatofilho@812: mythtvsrc->uri_name = g_value_dup_string(value); renatofilho@812: break; renatofilho@812: case PROP_GMYTHTV_VERSION: renatofilho@812: mythtvsrc->mythtv_version = g_value_get_int(value); renatofilho@812: break; renatofilho@812: case PROP_GMYTHTV_LIVEID: renatofilho@812: mythtvsrc->live_tv_id = g_value_get_int(value); renatofilho@812: break; renatofilho@812: case PROP_GMYTHTV_LIVE: renatofilho@812: mythtvsrc->live_tv = g_value_get_boolean(value); renatofilho@812: break; renatofilho@812: case PROP_GMYTHTV_ENABLE_TIMING_POSITION: renatofilho@812: mythtvsrc->enable_timing_position = g_value_get_boolean(value); renatofilho@812: break; renatofilho@812: case PROP_GMYTHTV_LIVE_CHAINID: renatofilho@812: if (!g_value_get_string(value)) { renatofilho@812: GST_WARNING_OBJECT(object, "MythTV Live chainid property cannot be NULL"); renatofilho@812: break; renatofilho@812: } renatofilho@608: renatofilho@812: if (mythtvsrc->live_chain_id != NULL) { renatofilho@812: g_free(mythtvsrc->live_chain_id); renatofilho@812: mythtvsrc->live_chain_id = NULL; renatofilho@812: } renatofilho@812: mythtvsrc->live_chain_id = g_value_dup_string(value); renatofilho@812: break; renatofilho@812: case PROP_GMYTHTV_CHANNEL_NUM: renatofilho@812: mythtvsrc->channel_name = g_value_dup_string(value); renatofilho@812: break; renatofilho@812: default: renatofilho@754: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); renatofilho@754: break; renatofilho@754: } renatofilho@608: renatofilho@754: GST_OBJECT_UNLOCK(mythtvsrc); renatofilho@608: } renatofilho@608: renatofilho@608: static void renatofilho@751: gst_mythtv_src_get_property(GObject * object, guint prop_id, renatofilho@754: GValue * value, GParamSpec * pspec) renatofilho@608: { renatofilho@754: GstMythtvSrc *mythtvsrc = GST_MYTHTV_SRC(object); renatofilho@608: renatofilho@754: GST_OBJECT_LOCK(mythtvsrc); renatofilho@754: switch (prop_id) { renatofilho@812: case PROP_LOCATION: renatofilho@812: g_value_set_string(value, mythtvsrc->uri_name); renatofilho@754: break; renatofilho@812: case PROP_GMYTHTV_VERSION: renatofilho@812: g_value_set_int(value, mythtvsrc->mythtv_version); renatofilho@812: break; renatofilho@812: case PROP_GMYTHTV_LIVEID: renatofilho@812: g_value_set_int(value, mythtvsrc->live_tv_id); renatofilho@812: break; renatofilho@812: case PROP_GMYTHTV_LIVE: renatofilho@754: g_value_set_boolean(value, mythtvsrc->live_tv); renatofilho@754: break; renatofilho@812: case PROP_GMYTHTV_ENABLE_TIMING_POSITION: renatofilho@754: g_value_set_boolean(value, mythtvsrc->enable_timing_position); renatofilho@754: break; renatofilho@812: case PROP_GMYTHTV_LIVE_CHAINID: renatofilho@812: g_value_set_string(value, mythtvsrc->live_chain_id); renatofilho@812: break; renatofilho@812: case PROP_GMYTHTV_CHANNEL_NUM: renatofilho@812: g_value_set_string(value, mythtvsrc->channel_name); renatofilho@812: break; renatofilho@754: default: renatofilho@754: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); renatofilho@754: break; renatofilho@754: } renatofilho@754: GST_OBJECT_UNLOCK(mythtvsrc); renatofilho@608: } renatofilho@608: renatofilho@758: static gboolean renatofilho@751: plugin_init(GstPlugin * plugin) renatofilho@608: { renatofilho@754: return gst_element_register(plugin, "mythtvsrc", GST_RANK_NONE, renatofilho@754: GST_TYPE_MYTHTV_SRC); renatofilho@608: } renatofilho@608: renatofilho@751: GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, renatofilho@754: GST_VERSION_MINOR, renatofilho@754: "mythtv", renatofilho@754: "lib MythTV src", renatofilho@754: plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, renatofilho@754: GST_PACKAGE_ORIGIN); renatofilho@608: renatofilho@608: renatofilho@608: /*** GSTURIHANDLER INTERFACE *************************************************/ renatofilho@758: static guint renatofilho@751: gst_mythtv_src_uri_get_type(void) renatofilho@608: { renatofilho@754: return GST_URI_SRC; renatofilho@608: } renatofilho@608: renatofilho@754: static gchar ** renatofilho@751: gst_mythtv_src_uri_get_protocols(void) renatofilho@608: { renatofilho@754: static gchar *protocols[] = { "myth", "myths", NULL }; renatofilho@608: renatofilho@754: return protocols; renatofilho@608: } renatofilho@608: renatofilho@608: static const gchar * renatofilho@751: gst_mythtv_src_uri_get_uri(GstURIHandler * handler) renatofilho@608: { renatofilho@754: GstMythtvSrc *src = GST_MYTHTV_SRC(handler); renatofilho@608: renatofilho@754: return src->uri_name; renatofilho@608: } renatofilho@608: renatofilho@758: static gboolean renatofilho@751: gst_mythtv_src_uri_set_uri(GstURIHandler * handler, const gchar * uri) renatofilho@608: { renatofilho@754: GstMythtvSrc *src = GST_MYTHTV_SRC(handler); renatofilho@608: renatofilho@754: gchar *protocol; renatofilho@608: renatofilho@754: protocol = gst_uri_get_protocol(uri); renatofilho@754: if ((strcmp(protocol, "myth") != 0) renatofilho@754: && (strcmp(protocol, "myths") != 0)) { renatofilho@754: g_free(protocol); renatofilho@754: return FALSE; renatofilho@754: } renatofilho@754: g_free(protocol); renatofilho@754: g_object_set(src, "location", uri, NULL); renatofilho@608: renatofilho@754: return TRUE; renatofilho@608: } renatofilho@608: renatofilho@608: static void renatofilho@751: gst_mythtv_src_uri_handler_init(gpointer g_iface, gpointer iface_data) renatofilho@608: { renatofilho@754: GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; renatofilho@608: renatofilho@754: iface->get_type = gst_mythtv_src_uri_get_type; renatofilho@754: iface->get_protocols = gst_mythtv_src_uri_get_protocols; renatofilho@754: iface->get_uri = gst_mythtv_src_uri_get_uri; renatofilho@754: iface->set_uri = gst_mythtv_src_uri_set_uri; renatofilho@608: } renatofilho@608: renatofilho@608: void renatofilho@751: size_header_handler(void *userdata, const char *value) renatofilho@608: { renatofilho@754: GstMythtvSrc *src = GST_MYTHTV_SRC(userdata); renatofilho@608: renatofilho@754: GST_DEBUG_OBJECT(src, "content size = %lld bytes", src->content_size); renatofilho@608: }