renatofilho@608: /* GStreamer MythTV Plug-in renatofilho@608: * Copyright (C) <2006> Rosfran Borges renatofilho@608: * renatofilho@608: * This library is free software; you can redistribute it and/or renatofilho@608: * modify it under the terms of the GNU Library General Public renatofilho@608: * License as published by the Free Software Foundation; either renatofilho@608: * version 2 of the License, or (at your option) any later version. renatofilho@608: * renatofilho@608: * This library is distributed in the hope that it will be useful, renatofilho@608: * but WITHOUT ANY WARRANTY; without even the implied warranty of renatofilho@608: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU renatofilho@608: * Library General Public License for more details. renatofilho@608: * renatofilho@608: * You should have received a copy of the GNU Library General Public renatofilho@608: * License along with this library; if not, write to the renatofilho@608: * Free Software Foundation, Inc., 59 Temple Place - Suite 330, renatofilho@608: * 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_CAT_DEFAULT mythtvsrc_debug renatofilho@608: renatofilho@608: #define GST_GMYTHTV_ID_NUM 1 renatofilho@608: renatofilho@608: #define GST_GMYTHTV_CHANNEL_DEFAULT_NUM (-1) renatofilho@608: renatofilho@608: #define GMYTHTV_VERSION_DEFAULT 30 renatofilho@608: renatofilho@608: #define GMYTHTV_TRANSFER_MAX_WAITS 100 renatofilho@608: renatofilho@608: #define GMYTHTV_TRANSFER_MAX_RESENDS 2 renatofilho@608: renatofilho@608: #define GMYTHTV_TRANSFER_MAX_BUFFER (128*1024) renatofilho@608: renatofilho@608: #define MAX_READ_SIZE (4*1024) renatofilho@608: renatofilho@608: #define GST_FLOW_ERROR_NO_DATA (-101) renatofilho@608: renatofilho@608: #define REQUEST_MAX_SIZE (64*1024) renatofilho@608: renatofilho@608: #define INTERNAL_BUFFER_SIZE (90*1024) renatofilho@608: renatofilho@608: static const GstElementDetails gst_mythtv_src_details = renatofilho@751: GST_ELEMENT_DETAILS("MythTV client source", renatofilho@751: "Source/Network", renatofilho@751: "Control and receive data as a client over the network " renatofilho@751: "via raw socket connections using the MythTV protocol", renatofilho@751: "Rosfran Borges "); renatofilho@608: renatofilho@751: static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE("src", renatofilho@751: GST_PAD_SRC, renatofilho@751: GST_PAD_ALWAYS, renatofilho@751: GST_STATIC_CAPS_ANY); renatofilho@751: /* GST_STATIC_CAPS ("video/x-nuv")); */ renatofilho@608: renatofilho@608: enum renatofilho@608: { renatofilho@608: PROP_0, renatofilho@608: PROP_LOCATION, renatofilho@608: #ifndef GST_DISABLE_GST_DEBUG renatofilho@608: PROP_GMYTHTV_DBG, renatofilho@608: #endif renatofilho@608: PROP_GMYTHTV_VERSION, renatofilho@608: PROP_GMYTHTV_LIVE, renatofilho@608: PROP_GMYTHTV_LIVEID, renatofilho@608: PROP_GMYTHTV_LIVE_CHAINID, renatofilho@608: PROP_GMYTHTV_ENABLE_TIMING_POSITION, renatofilho@608: PROP_GMYTHTV_CHANNEL_NUM, renatofilho@608: PROP_GMYTHTV_MAX_TRY renatofilho@608: }; renatofilho@608: renatofilho@751: static void gst_mythtv_src_clear(GstMythtvSrc * mythtv_src); renatofilho@608: renatofilho@751: static void gst_mythtv_src_finalize(GObject * gobject); renatofilho@608: renatofilho@751: static GstFlowReturn gst_mythtv_src_create(GstPushSrc * psrc, renatofilho@751: 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@751: GstSegment * segment); renatofilho@608: renatofilho@608: static GstStateChangeReturn renatofilho@751: gst_mythtv_src_change_state(GstElement * element, GstStateChange transition); renatofilho@608: renatofilho@751: static void gst_mythtv_src_set_property(GObject * object, guint prop_id, renatofilho@751: const GValue * value, renatofilho@751: GParamSpec * pspec); renatofilho@751: static void gst_mythtv_src_get_property(GObject * object, guint prop_id, renatofilho@751: GValue * value, GParamSpec * pspec); renatofilho@608: renatofilho@751: static void gst_mythtv_src_uri_handler_init(gpointer g_iface, renatofilho@751: gpointer iface_data); renatofilho@608: renatofilho@751: static gboolean gst_mythtv_src_handle_query(GstPad * pad, GstQuery * query); renatofilho@608: renatofilho@751: static gboolean gst_mythtv_src_handle_event(GstPad * pad, GstEvent * event); renatofilho@608: renatofilho@751: static GMythFileReadResult do_read_request_response(GstMythtvSrc * src, renatofilho@751: guint size, renatofilho@751: GByteArray * data_ptr); renatofilho@608: renatofilho@608: static void renatofilho@751: _urihandler_init(GType type) renatofilho@608: { renatofilho@608: static const GInterfaceInfo urihandler_info = { renatofilho@751: gst_mythtv_src_uri_handler_init, renatofilho@751: NULL, renatofilho@751: NULL renatofilho@608: }; renatofilho@608: renatofilho@751: g_type_add_interface_static(type, GST_TYPE_URI_HANDLER, &urihandler_info); renatofilho@608: renatofilho@751: 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@751: GST_TYPE_PUSH_SRC, _urihandler_init) renatofilho@751: static void gst_mythtv_src_base_init(gpointer g_class) renatofilho@751: { renatofilho@751: GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); renatofilho@608: renatofilho@751: gst_element_class_add_pad_template(element_class, renatofilho@751: gst_static_pad_template_get renatofilho@751: (&srctemplate)); renatofilho@608: renatofilho@751: gst_element_class_set_details(element_class, &gst_mythtv_src_details); renatofilho@608: renatofilho@608: 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@608: GObjectClass *gobject_class; renatofilho@608: GstPushSrcClass *gstpushsrc_class; renatofilho@608: GstBaseSrcClass *gstbasesrc_class; renatofilho@608: renatofilho@608: gobject_class = (GObjectClass *) klass; renatofilho@608: gstbasesrc_class = (GstBaseSrcClass *) klass; renatofilho@608: gstpushsrc_class = (GstPushSrcClass *) klass; renatofilho@608: renatofilho@608: gobject_class->set_property = gst_mythtv_src_set_property; renatofilho@608: gobject_class->get_property = gst_mythtv_src_get_property; renatofilho@608: gobject_class->finalize = gst_mythtv_src_finalize; renatofilho@608: renatofilho@608: g_object_class_install_property renatofilho@751: (gobject_class, PROP_LOCATION, renatofilho@751: g_param_spec_string("location", "Location", renatofilho@751: "The location. In the form:" renatofilho@751: "\n\t\t\tmyth://a.com/file.nuv" renatofilho@751: "\n\t\t\tmyth://a.com:23223/file.nuv " renatofilho@751: "\n\t\t\ta.com/file.nuv - default scheme 'myth'", renatofilho@751: "", G_PARAM_READWRITE)); renatofilho@608: renatofilho@608: g_object_class_install_property renatofilho@751: (gobject_class, PROP_GMYTHTV_VERSION, renatofilho@751: g_param_spec_int("mythtv-version", "mythtv-version", renatofilho@751: "Change MythTV version", 26, 30, 26, renatofilho@751: G_PARAM_READWRITE)); renatofilho@608: renatofilho@608: g_object_class_install_property renatofilho@751: (gobject_class, PROP_GMYTHTV_LIVEID, renatofilho@751: g_param_spec_int("mythtv-live-id", "mythtv-live-id", renatofilho@751: "Change MythTV version", renatofilho@751: 0, 200, GST_GMYTHTV_ID_NUM, G_PARAM_READWRITE)); renatofilho@608: renatofilho@608: g_object_class_install_property renatofilho@751: (gobject_class, PROP_GMYTHTV_LIVE_CHAINID, renatofilho@751: g_param_spec_string("mythtv-live-chainid", "mythtv-live-chainid", renatofilho@751: "Sets the MythTV chain ID (from TV Chain)", "", renatofilho@751: G_PARAM_READWRITE)); renatofilho@608: renatofilho@608: g_object_class_install_property renatofilho@751: (gobject_class, PROP_GMYTHTV_LIVE, renatofilho@751: g_param_spec_boolean("mythtv-live", "mythtv-live", renatofilho@751: "Enable MythTV Live TV content streaming", FALSE, renatofilho@751: G_PARAM_READWRITE)); renatofilho@608: renatofilho@608: g_object_class_install_property renatofilho@751: (gobject_class, PROP_GMYTHTV_ENABLE_TIMING_POSITION, renatofilho@751: g_param_spec_boolean("mythtv-enable-timing-position", renatofilho@751: "mythtv-enable-timing-position", renatofilho@751: "Enable MythTV Live TV content size continuous updating", renatofilho@751: FALSE, G_PARAM_READWRITE)); renatofilho@608: renatofilho@608: g_object_class_install_property renatofilho@751: (gobject_class, PROP_GMYTHTV_CHANNEL_NUM, renatofilho@751: g_param_spec_string("mythtv-channel", "mythtv-channel", renatofilho@751: "Change MythTV channel number", renatofilho@751: "", G_PARAM_READWRITE)); renatofilho@608: renatofilho@608: g_object_class_install_property renatofilho@751: (gobject_class, PROP_GMYTHTV_MAX_TRY, renatofilho@751: g_param_spec_int("max-try", "max-try", renatofilho@751: "Set the max try for get MythTV free recorder", renatofilho@751: 0, G_MAXINT, 10, G_PARAM_READWRITE)); renatofilho@608: renatofilho@608: renatofilho@608: #ifndef GST_DISABLE_GST_DEBUG renatofilho@608: g_object_class_install_property renatofilho@751: (gobject_class, PROP_GMYTHTV_DBG, renatofilho@751: g_param_spec_boolean("mythtv-debug", "mythtv-debug", renatofilho@751: "Enable MythTV debug messages", FALSE, renatofilho@751: G_PARAM_READWRITE)); renatofilho@608: #endif renatofilho@608: renatofilho@608: gstbasesrc_class->start = gst_mythtv_src_start; renatofilho@608: gstbasesrc_class->stop = gst_mythtv_src_stop; renatofilho@608: gstbasesrc_class->get_size = gst_mythtv_src_get_size; renatofilho@608: gstbasesrc_class->is_seekable = gst_mythtv_src_is_seekable; renatofilho@608: renatofilho@608: gstbasesrc_class->do_seek = gst_mythtv_src_do_seek; renatofilho@608: gstpushsrc_class->create = gst_mythtv_src_create; renatofilho@608: renatofilho@751: GST_DEBUG_CATEGORY_INIT(mythtvsrc_debug, "mythtvsrc", 0, renatofilho@751: "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@608: this->file = NULL; renatofilho@608: renatofilho@608: this->unique_setup = FALSE; renatofilho@608: renatofilho@608: this->mythtv_version = GMYTHTV_VERSION_DEFAULT; renatofilho@608: renatofilho@608: this->state = GST_MYTHTV_SRC_FILE_TRANSFER; renatofilho@608: renatofilho@608: this->bytes_read = 0; renatofilho@608: renatofilho@608: this->prev_content_size = 0; renatofilho@608: renatofilho@608: this->content_size = 0; renatofilho@608: this->read_offset = 0; renatofilho@608: renatofilho@608: this->content_size_last = 0; renatofilho@608: renatofilho@608: this->live_tv = FALSE; renatofilho@608: renatofilho@608: this->enable_timing_position = FALSE; renatofilho@608: this->update_prog_chain = FALSE; renatofilho@608: renatofilho@751: this->user_agent = g_strdup("mythtvsrc"); renatofilho@608: this->update_prog_chain = FALSE; renatofilho@608: renatofilho@608: this->channel_name = NULL; renatofilho@608: renatofilho@608: this->eos = FALSE; renatofilho@608: renatofilho@608: this->bytes_queue = NULL; renatofilho@608: renatofilho@608: this->wait_to_transfer = 0; renatofilho@608: renatofilho@751: gst_base_src_set_format(GST_BASE_SRC(this), GST_FORMAT_BYTES); renatofilho@608: renatofilho@751: gst_pad_set_event_function(GST_BASE_SRC_PAD(GST_BASE_SRC(this)), renatofilho@751: gst_mythtv_src_handle_event); renatofilho@751: gst_pad_set_query_function(GST_BASE_SRC_PAD(GST_BASE_SRC(this)), renatofilho@751: 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@608: mythtv_src->unique_setup = FALSE; renatofilho@608: renatofilho@751: if (mythtv_src->spawn_livetv) renatofilho@751: { renatofilho@751: g_object_unref(mythtv_src->spawn_livetv); renatofilho@751: mythtv_src->spawn_livetv = NULL; renatofilho@751: } renatofilho@608: renatofilho@751: if (mythtv_src->file) renatofilho@751: { renatofilho@751: g_object_unref(mythtv_src->file); renatofilho@751: mythtv_src->file = NULL; renatofilho@751: } renatofilho@608: renatofilho@751: if (mythtv_src->backend_info) renatofilho@751: { renatofilho@751: g_object_unref(mythtv_src->backend_info); renatofilho@751: mythtv_src->backend_info = NULL; renatofilho@751: } renatofilho@608: renatofilho@751: if (mythtv_src->bytes_queue) renatofilho@751: { renatofilho@751: g_byte_array_free(mythtv_src->bytes_queue, TRUE); renatofilho@751: mythtv_src->bytes_queue = NULL; renatofilho@751: } renatofilho@608: } renatofilho@608: renatofilho@608: static void renatofilho@751: gst_mythtv_src_finalize(GObject * gobject) renatofilho@608: { renatofilho@751: GstMythtvSrc *this = GST_MYTHTV_SRC(gobject); renatofilho@608: renatofilho@751: gst_mythtv_src_clear(this); renatofilho@608: renatofilho@751: if (this->uri_name) renatofilho@751: { renatofilho@751: g_free(this->uri_name); renatofilho@751: this->uri_name = NULL; renatofilho@751: } renatofilho@608: renatofilho@751: if (this->user_agent) renatofilho@751: { renatofilho@751: g_free(this->user_agent); renatofilho@751: this->user_agent = NULL; renatofilho@751: } renatofilho@608: renatofilho@751: G_OBJECT_CLASS(parent_class)->finalize(gobject); renatofilho@608: } renatofilho@608: renatofilho@608: static GMythFileReadResult renatofilho@751: do_read_request_response(GstMythtvSrc * src, guint size, renatofilho@751: GByteArray * data_ptr) renatofilho@608: { renatofilho@608: gint read = 0; renatofilho@608: guint sizetoread = size; renatofilho@608: gint max_iters = GMYTHTV_TRANSFER_MAX_RESENDS; renatofilho@608: GMythFileReadResult result; renatofilho@608: renatofilho@751: GST_LOG_OBJECT(src, "Starting: Reading %d bytes...", sizetoread); renatofilho@608: renatofilho@751: /* Loop sending the Myth File Transfer request: renatofilho@751: * Retry whilst authentication fails and we supply it. */ renatofilho@608: renatofilho@751: while (sizetoread == size && --max_iters > 0) renatofilho@751: { renatofilho@751: /* if ( gmyth_backend_info_is_local_file(src->backend_info) ) */ renatofilho@751: if (IS_GMYTH_FILE_LOCAL(src->file)) renatofilho@751: result = gmyth_file_local_read(GMYTH_FILE_LOCAL(src->file), renatofilho@751: data_ptr, sizetoread, src->live_tv); renatofilho@751: else if (IS_GMYTH_FILE_TRANSFER(src->file)) renatofilho@751: result = gmyth_file_transfer_read(GMYTH_FILE_TRANSFER(src->file), renatofilho@751: data_ptr, sizetoread, src->live_tv); renatofilho@608: renatofilho@751: if (data_ptr->len > 0) renatofilho@751: { renatofilho@751: read += data_ptr->len; renatofilho@751: sizetoread -= data_ptr->len; renatofilho@751: } renatofilho@751: else if (data_ptr->len <= 0) renatofilho@751: { renatofilho@751: if (src->live_tv == FALSE) renatofilho@751: { renatofilho@751: result = GMYTH_FILE_READ_EOF; renatofilho@751: goto eos; renatofilho@751: } renatofilho@751: else renatofilho@751: { renatofilho@751: if (result == GMYTH_FILE_READ_ERROR) renatofilho@751: { /* -314 */ renatofilho@751: GST_INFO_OBJECT(src, "[LiveTV] FileTransfer READ_ERROR!"); renatofilho@751: goto done; renatofilho@751: } renatofilho@751: else if (result == GMYTH_FILE_READ_NEXT_PROG_CHAIN) renatofilho@751: { /* -315 */ renatofilho@751: GST_INFO_OBJECT(src, renatofilho@751: "[LiveTV] FileTransfer - Go to athe next program chain!"); renatofilho@751: src->update_prog_chain = TRUE; renatofilho@751: continue; renatofilho@751: } renatofilho@751: goto done; renatofilho@751: } renatofilho@608: renatofilho@751: } /* else if (data_ptr->len == 0) renatofilho@751: goto done; */ renatofilho@608: renatofilho@751: if (read == sizetoread) renatofilho@751: goto done; renatofilho@751: } renatofilho@608: renatofilho@751: if ((read < 0 && !src->live_tv) || max_iters == 0) renatofilho@751: { renatofilho@751: result = GMYTH_FILE_READ_EOF; renatofilho@751: goto eos; renatofilho@751: } renatofilho@608: renatofilho@608: goto done; renatofilho@608: renatofilho@608: eos: renatofilho@608: src->eos = TRUE; renatofilho@608: renatofilho@608: done: renatofilho@608: return result; renatofilho@608: } renatofilho@608: renatofilho@608: static GstFlowReturn renatofilho@751: gst_mythtv_src_create(GstPushSrc * psrc, GstBuffer ** outbuf) renatofilho@608: { renatofilho@608: GstMythtvSrc *src; renatofilho@608: GstFlowReturn ret = GST_FLOW_OK; renatofilho@608: guint buffer_size_inter = 0; renatofilho@608: renatofilho@751: src = GST_MYTHTV_SRC(psrc); renatofilho@608: renatofilho@751: /* The caller should know the number of bytes and not read beyond EOS. */ renatofilho@751: if (G_UNLIKELY(src->eos)) renatofilho@751: goto eos; renatofilho@751: GST_DEBUG_OBJECT(src, "offset = %" G_GUINT64_FORMAT ", size = %d...", renatofilho@751: src->read_offset, MAX_READ_SIZE); renatofilho@608: renatofilho@751: GST_DEBUG_OBJECT(src, "Create: buffer_remain: %d, buffer_size = %d.", renatofilho@751: (gint) src->buffer_remain, src->bytes_queue->len); renatofilho@608: renatofilho@608: program_chain_changed: renatofilho@751: /* just get from the byte array, no network effort... */ renatofilho@751: if ((src->buffer_remain = src->bytes_queue->len) < MAX_READ_SIZE) renatofilho@751: { renatofilho@751: GByteArray *buffer; renatofilho@751: GMythFileReadResult result = GMYTH_FILE_READ_OK; renatofilho@608: renatofilho@751: buffer = NULL; renatofilho@751: buffer_size_inter = (INTERNAL_BUFFER_SIZE - src->buffer_remain); renatofilho@608: renatofilho@751: if (buffer_size_inter > REQUEST_MAX_SIZE) renatofilho@751: buffer_size_inter = REQUEST_MAX_SIZE; renatofilho@608: renatofilho@751: buffer = g_byte_array_new(); renatofilho@608: renatofilho@751: result = do_read_request_response(src, buffer_size_inter, buffer); renatofilho@751: renatofilho@751: /* got the next program info? */ renatofilho@751: if (G_UNLIKELY(src->update_prog_chain) renatofilho@751: || (result == GMYTH_FILE_READ_NEXT_PROG_CHAIN)) renatofilho@751: { renatofilho@751: GST_DEBUG_OBJECT(src, "Update PROGRAM CHAIN!!! buffer_size = %d.", renatofilho@751: src->bytes_queue->len); renatofilho@751: gst_pad_push_event(GST_BASE_SRC_PAD(GST_BASE_SRC(psrc)), renatofilho@751: gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM, renatofilho@751: NULL)); rosfran@725: /* rosfran@725: gst_pad_push_event (GST_BASE_SRC_PAD (GST_BASE_SRC (psrc)), rosfran@695: gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES, 0, -1, 0)); rosfran@725: */ renatofilho@751: src->update_prog_chain = FALSE; renatofilho@751: src->eos = FALSE; rosfran@695: renatofilho@751: if (result == GMYTH_FILE_READ_NEXT_PROG_CHAIN) renatofilho@751: { renatofilho@751: /* renatofilho@751: if (buffer != NULL) { renatofilho@751: g_byte_array_free (buffer, TRUE); renatofilho@751: buffer = NULL; renatofilho@751: } renatofilho@751: goto program_chain_changed; */ renatofilho@751: } renatofilho@751: else if (result == GMYTH_FILE_READ_OK) renatofilho@751: { renatofilho@751: /* remove wasteful, NUV file header data */ renatofilho@751: /* buffer = g_byte_array_remove_range( buffer, 0, 512 ); */ renatofilho@751: /* TODO: need to send a new segment event to NUVDemux? */ renatofilho@751: //gst_pad_push_event (GST_BASE_SRC_PAD (GST_BASE_SRC (psrc)), renatofilho@751: // gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES, 0, -1, 0)); rosfran@693: renatofilho@751: /* goto change_progchain; */ renatofilho@751: } rosfran@695: renatofilho@751: } /* */ rosfran@693: renatofilho@751: if (G_UNLIKELY(buffer->len < 0)) renatofilho@751: { renatofilho@608: renatofilho@751: if (buffer != NULL) renatofilho@751: { renatofilho@751: g_byte_array_free(buffer, TRUE); renatofilho@751: buffer = NULL; renatofilho@751: } renatofilho@608: renatofilho@751: if (src->live_tv || (result == GMYTH_FILE_READ_NEXT_PROG_CHAIN)) renatofilho@751: goto change_progchain; renatofilho@751: else renatofilho@751: goto read_error; renatofilho@751: } renatofilho@751: else if (G_UNLIKELY(buffer->len == 0)) renatofilho@751: { renatofilho@608: renatofilho@751: if (buffer != NULL) renatofilho@751: { renatofilho@751: g_byte_array_free(buffer, TRUE); renatofilho@751: buffer = NULL; renatofilho@751: } renatofilho@608: renatofilho@751: if (!src->live_tv) renatofilho@751: goto done; renatofilho@751: else renatofilho@751: goto program_chain_changed; renatofilho@751: } renatofilho@751: renatofilho@751: src->bytes_queue = renatofilho@751: g_byte_array_append(src->bytes_queue, buffer->data, buffer->len); renatofilho@751: if (buffer->len > buffer_size_inter) renatofilho@751: GST_WARNING_OBJECT(src, renatofilho@751: "INCREASED buffer size! Backend sent more than we ask him... (%d)", renatofilho@751: abs(buffer->len - buffer_size_inter)); renatofilho@751: renatofilho@751: src->buffer_remain += buffer->len; renatofilho@751: renatofilho@751: if (buffer != NULL) renatofilho@751: { renatofilho@751: g_byte_array_free(buffer, TRUE); renatofilho@751: buffer = NULL; renatofilho@751: } renatofilho@751: renatofilho@751: /* renatofilho@751: GST_DEBUG_OBJECT (src, renatofilho@751: "BYTES READ (actual) = %d, BYTES READ (cumulative) = %llu, " renatofilho@751: "OFFSET = %llu, CONTENT SIZE = %llu.", read, renatofilho@751: src->bytes_read, src->read_offset, src->content_size); renatofilho@751: */ renatofilho@751: } renatofilho@608: renatofilho@608: guint buffer_size = renatofilho@751: (src->buffer_remain < MAX_READ_SIZE) ? src->buffer_remain : MAX_READ_SIZE; renatofilho@608: renatofilho@751: *outbuf = gst_buffer_new(); renatofilho@608: renatofilho@751: /* renatofilho@751: GST_DEBUG_OBJECT (src, "read from network? %s!, buffer_remain = %d", renatofilho@751: (buffer_size_inter == renatofilho@751: 0) ? "NO, got from buffer" : "YES, go see the backend's log file", renatofilho@751: src->buffer_remain); renatofilho@751: */ renatofilho@608: renatofilho@751: GST_BUFFER_SIZE(*outbuf) = buffer_size; renatofilho@751: GST_BUFFER_MALLOCDATA(*outbuf) = g_malloc0(GST_BUFFER_SIZE(*outbuf)); renatofilho@751: GST_BUFFER_DATA(*outbuf) = GST_BUFFER_MALLOCDATA(*outbuf); renatofilho@751: g_memmove(GST_BUFFER_DATA((*outbuf)), src->bytes_queue->data, renatofilho@751: GST_BUFFER_SIZE(*outbuf)); renatofilho@751: GST_BUFFER_OFFSET(*outbuf) = src->read_offset; renatofilho@751: GST_BUFFER_OFFSET_END(*outbuf) = renatofilho@751: src->read_offset + GST_BUFFER_SIZE(*outbuf); renatofilho@608: renatofilho@751: src->buffer_remain -= GST_BUFFER_SIZE(*outbuf); renatofilho@608: renatofilho@751: src->read_offset += GST_BUFFER_SIZE(*outbuf); renatofilho@751: src->bytes_read += GST_BUFFER_SIZE(*outbuf); renatofilho@751: //GST_DEBUG_OBJECT (src, "Buffer output with size: %d", renatofilho@751: // GST_BUFFER_SIZE (*outbuf)); renatofilho@608: renatofilho@751: /* flushs the newly buffer got from byte array */ renatofilho@608: src->bytes_queue = renatofilho@751: g_byte_array_remove_range(src->bytes_queue, 0, buffer_size); renatofilho@608: renatofilho@751: if (G_UNLIKELY(src->eos) || (!src->live_tv renatofilho@751: && (src->bytes_read >= src->content_size))) renatofilho@751: goto eos; renatofilho@608: renatofilho@608: done: renatofilho@608: { renatofilho@751: const gchar *reason = gst_flow_get_name(ret); renatofilho@751: return ret; renatofilho@608: } renatofilho@608: eos: renatofilho@608: { renatofilho@751: const gchar *reason = gst_flow_get_name(ret); renatofilho@608: renatofilho@751: GST_DEBUG_OBJECT(src, "pausing task, reason %s", reason); renatofilho@751: return GST_FLOW_UNEXPECTED; renatofilho@608: } renatofilho@751: /* ERRORS */ renatofilho@608: read_error: renatofilho@608: { renatofilho@751: GST_ELEMENT_ERROR(src, RESOURCE, READ, renatofilho@751: (NULL), ("Could not read any bytes (%i, %s)", read, renatofilho@751: src->uri_name)); renatofilho@751: return GST_FLOW_ERROR; renatofilho@608: } renatofilho@608: change_progchain: renatofilho@608: { renatofilho@751: GST_ELEMENT_ERROR(src, RESOURCE, READ, renatofilho@751: (NULL), renatofilho@751: ("Seek failed, go to the next program info... (%i, %s)", renatofilho@751: read, src->uri_name)); renatofilho@608: rosfran@693: /* TODO: need to send a new segment event to NUVDemux? */ renatofilho@751: gst_pad_push_event(GST_BASE_SRC_PAD(GST_BASE_SRC(psrc)), renatofilho@751: gst_event_new_new_segment(TRUE, 1.0, GST_FORMAT_TIME, renatofilho@751: 0, -1, 0)); renatofilho@608: renatofilho@751: goto program_chain_changed; renatofilho@608: } renatofilho@608: renatofilho@608: } renatofilho@608: renatofilho@608: gint64 renatofilho@751: gst_mythtv_src_get_position(GstMythtvSrc * src) renatofilho@608: { renatofilho@608: renatofilho@608: gint64 size_tmp = 0; renatofilho@608: guint max_tries = 2; renatofilho@608: renatofilho@751: if (src->live_tv == TRUE && (abs(src->content_size - src->bytes_read) < renatofilho@751: GMYTHTV_TRANSFER_MAX_BUFFER)) renatofilho@751: { renatofilho@608: renatofilho@751: get_file_pos: renatofilho@751: g_usleep(10); renatofilho@751: size_tmp = renatofilho@751: gmyth_recorder_get_file_position(src->spawn_livetv->recorder); renatofilho@751: if (size_tmp > (src->content_size + GMYTHTV_TRANSFER_MAX_BUFFER)) renatofilho@751: src->content_size = size_tmp; renatofilho@751: else if (size_tmp > 0 && --max_tries > 0) renatofilho@751: goto get_file_pos; renatofilho@751: GST_LOG_OBJECT(src, "GET_POSITION: file_position = %lld", size_tmp); renatofilho@751: /* sets the last content size amount before it can be updated */ renatofilho@751: src->prev_content_size = src->content_size; renatofilho@751: } renatofilho@608: renatofilho@608: return src->content_size; renatofilho@608: renatofilho@608: } renatofilho@608: renatofilho@608: static gboolean renatofilho@751: gst_mythtv_src_do_seek(GstBaseSrc * base, GstSegment * segment) renatofilho@608: { renatofilho@751: GstMythtvSrc *src = GST_MYTHTV_SRC(base); renatofilho@608: gint64 new_offset = -1; renatofilho@608: gint64 actual_seek = segment->start; renatofilho@608: gboolean ret = TRUE; renatofilho@608: renatofilho@751: GST_LOG_OBJECT(src, "seek, segment: %" GST_SEGMENT_FORMAT, segment); renatofilho@608: renatofilho@751: if (segment->format == GST_FORMAT_TIME) renatofilho@751: { renatofilho@751: goto done; renatofilho@751: } renatofilho@751: GST_LOG_OBJECT(src, renatofilho@751: "Trying to seek at the value (actual_seek = %lld, read_offset = %lld)", renatofilho@751: actual_seek, src->read_offset); renatofilho@751: /* verify if it needs to seek */ renatofilho@751: if (src->read_offset != actual_seek) renatofilho@751: { renatofilho@608: renatofilho@751: /* if ( gmyth_backend_info_is_local_file(src->backend_info) ) */ renatofilho@751: if (IS_GMYTH_FILE_LOCAL(src->file)) renatofilho@751: new_offset = renatofilho@751: gmyth_file_local_seek(GMYTH_FILE_LOCAL(src->file), segment->start, renatofilho@751: G_SEEK_SET); renatofilho@751: else if (IS_GMYTH_FILE_TRANSFER(src->file)) renatofilho@751: new_offset = renatofilho@751: gmyth_file_transfer_seek(GMYTH_FILE_TRANSFER(src->file), renatofilho@751: segment->start, SEEK_SET); renatofilho@608: renatofilho@751: GST_LOG_OBJECT(src, renatofilho@751: "Segment offset start = %lld, SRC Offset = %lld, NEW actual backend SEEK Offset = %lld.", renatofilho@751: segment->start, src->read_offset, new_offset); renatofilho@751: if (G_UNLIKELY(new_offset < 0)) renatofilho@751: { renatofilho@751: ret = FALSE; renatofilho@751: if (!src->live_tv) renatofilho@751: goto eos; renatofilho@751: } renatofilho@608: renatofilho@751: src->read_offset = new_offset; renatofilho@608: renatofilho@751: if (ret == FALSE) renatofilho@751: { renatofilho@751: GST_INFO_OBJECT(src, "Failed to set the SEEK on segment!"); renatofilho@751: } renatofilho@751: renatofilho@751: } renatofilho@608: renatofilho@608: done: renatofilho@608: return ret; renatofilho@608: renatofilho@608: eos: renatofilho@608: { renatofilho@751: GST_DEBUG_OBJECT(src, "EOS found on seeking!!!"); renatofilho@751: return FALSE; renatofilho@608: } renatofilho@608: renatofilho@608: } renatofilho@608: renatofilho@608: /* create a socket for connecting to remote server */ renatofilho@608: static gboolean renatofilho@751: gst_mythtv_src_start(GstBaseSrc * bsrc) renatofilho@608: { renatofilho@751: GstMythtvSrc *src = GST_MYTHTV_SRC(bsrc); renatofilho@608: renatofilho@751: GString *chain_id_local = NULL; renatofilho@608: GMythURI *gmyth_uri = NULL; renatofilho@608: gboolean ret = TRUE; renatofilho@674: GstMessage *msg; renatofilho@608: renatofilho@751: if (src->unique_setup == FALSE) renatofilho@751: { renatofilho@751: src->unique_setup = TRUE; renatofilho@751: } renatofilho@751: else renatofilho@751: { renatofilho@751: goto done; renatofilho@751: } renatofilho@679: renatofilho@751: gmyth_uri = gmyth_uri_new_with_value(src->uri_name); renatofilho@751: src->backend_info = gmyth_backend_info_new_with_uri(src->uri_name); renatofilho@751: src->live_tv = gmyth_uri_is_livetv(gmyth_uri); renatofilho@751: /* testing UPnP... */ renatofilho@751: /* gmyth_backend_info_set_hostname( src->backend_info, NULL ); */ renatofilho@751: if (src->live_tv) renatofilho@751: { renatofilho@751: src->spawn_livetv = gmyth_livetv_new(src->backend_info); renatofilho@608: renatofilho@751: gchar *ch = gmyth_uri_get_channel_name(gmyth_uri); renatofilho@751: if (ch != NULL) renatofilho@751: src->channel_name = ch; renatofilho@608: renatofilho@751: if (src->channel_name != NULL) renatofilho@751: { renatofilho@751: gboolean result; renatofilho@751: result = renatofilho@751: gmyth_livetv_channel_name_setup(src->spawn_livetv, renatofilho@751: src->channel_name); renatofilho@751: if (result == FALSE) renatofilho@751: { renatofilho@751: GST_INFO_OBJECT(src, "LiveTV setup felt down on error"); renatofilho@751: ret = FALSE; renatofilho@751: goto init_failed; renatofilho@751: } renatofilho@608: renatofilho@751: } renatofilho@751: else renatofilho@751: { renatofilho@751: if (gmyth_livetv_setup(src->spawn_livetv) == FALSE) renatofilho@751: { renatofilho@751: GST_INFO_OBJECT(src, "LiveTV setup felt down on error"); renatofilho@751: ret = FALSE; renatofilho@751: goto init_failed; renatofilho@751: } renatofilho@751: } renatofilho@608: renatofilho@751: /* testing change channel... */ renatofilho@751: /* gmyth_recorder_change_channel( src->spawn_livetv->recorder, CHANNEL_DIRECTION_UP ); */ renatofilho@751: renatofilho@751: src->file = renatofilho@751: GMYTH_FILE(gmyth_livetv_create_file_transfer(src->spawn_livetv)); renatofilho@751: renatofilho@751: if (NULL == src->file) renatofilho@608: { renatofilho@751: GST_INFO_OBJECT(src, "[LiveTV] FileTransfer equals to NULL"); renatofilho@751: ret = FALSE; renatofilho@751: goto init_failed; renatofilho@608: } renatofilho@608: renatofilho@751: /* Check if the file is local to this specific client renderer */ renatofilho@751: if (gmyth_uri_is_local_file(gmyth_uri)) renatofilho@751: ret = gmyth_file_local_open(GMYTH_FILE_LOCAL(src->file)); renatofilho@751: else renatofilho@751: ret = renatofilho@751: gmyth_file_transfer_open(GMYTH_FILE_TRANSFER(src->file), renatofilho@751: src->spawn_livetv->uri != renatofilho@751: NULL ? gmyth_uri_get_path(src-> renatofilho@751: spawn_livetv-> renatofilho@751: uri) : src-> renatofilho@751: spawn_livetv->proginfo->pathname->str); renatofilho@608: renatofilho@751: /* sets the mythtvsrc "location" property */ renatofilho@751: g_object_set(src, "location", gmyth_file_get_uri(src->file), NULL); renatofilho@608: renatofilho@751: if (!ret) renatofilho@751: { renatofilho@751: GST_INFO_OBJECT(src, renatofilho@751: "Error: couldn't open the FileTransfer from LiveTV source!"); renatofilho@751: g_object_unref(src->file); renatofilho@751: src->file = NULL; renatofilho@751: goto init_failed; renatofilho@751: } renatofilho@751: } renatofilho@751: else renatofilho@751: { renatofilho@751: renatofilho@751: /* Check if the file is local to this specific client renderer, and tries to open renatofilho@751: * a local connection renatofilho@751: */ renatofilho@751: if (gmyth_uri_is_local_file(gmyth_uri)) renatofilho@751: { renatofilho@751: src->file = GMYTH_FILE(gmyth_file_local_new(src->backend_info)); renatofilho@751: ret = gmyth_file_local_open(GMYTH_FILE_LOCAL(src->file)); renatofilho@751: } renatofilho@751: else renatofilho@751: { renatofilho@751: src->file = GMYTH_FILE(gmyth_file_transfer_new(src->backend_info)); renatofilho@751: ret = renatofilho@751: gmyth_file_transfer_open(GMYTH_FILE_TRANSFER(src->file), renatofilho@751: src->uri_name); renatofilho@751: } renatofilho@751: renatofilho@751: } /* if (else) - recorded FileTransfer */ renatofilho@751: renatofilho@751: if (NULL == src->file) renatofilho@751: { renatofilho@751: GST_INFO_OBJECT(src, "FileTransfer is NULL"); renatofilho@751: goto init_failed; renatofilho@751: } renatofilho@751: /*GST_INFO_OBJECT( src, "uri = %s", src->spawn_livetv->file); */ renatofilho@751: renatofilho@751: if (ret == FALSE) renatofilho@751: { renatofilho@608: #ifndef GST_DISABLE_GST_DEBUG renatofilho@751: if (src->mythtv_msgs_dbg) renatofilho@751: GST_INFO_OBJECT(src, renatofilho@751: "MythTV FileTransfer request failed when setting up socket connection!"); renatofilho@608: #endif renatofilho@751: goto begin_req_failed; renatofilho@751: } renatofilho@608: renatofilho@751: GST_INFO_OBJECT(src, renatofilho@751: "MythTV FileTransfer filesize = %lld, content_size = %lld!", renatofilho@751: gmyth_file_get_filesize(src->file), src->content_size); renatofilho@608: renatofilho@751: src->content_size = gmyth_file_get_filesize(src->file); renatofilho@608: renatofilho@751: msg = renatofilho@751: gst_message_new_duration(GST_OBJECT(src), GST_FORMAT_BYTES, renatofilho@751: src->content_size); renatofilho@751: gst_element_post_message(GST_ELEMENT(src), msg); renatofilho@674: renatofilho@674: renatofilho@608: src->do_start = FALSE; renatofilho@608: renatofilho@751: /* this is used for the buffer cache */ renatofilho@751: src->bytes_queue = g_byte_array_sized_new(INTERNAL_BUFFER_SIZE); renatofilho@608: src->buffer_remain = 0; renatofilho@751: renatofilho@751: gst_pad_push_event(GST_BASE_SRC_PAD(GST_BASE_SRC(src)), renatofilho@751: gst_event_new_new_segment(TRUE, 1.0, GST_FORMAT_TIME, 0, renatofilho@751: src->content_size, 0)); renatofilho@608: renatofilho@608: done: renatofilho@751: if (gmyth_uri != NULL) renatofilho@608: { renatofilho@751: g_object_unref(gmyth_uri); renatofilho@751: gmyth_uri = NULL; renatofilho@608: } renatofilho@608: renatofilho@751: if (chain_id_local != NULL) renatofilho@608: { renatofilho@751: g_string_free(chain_id_local, TRUE); renatofilho@751: chain_id_local = NULL; renatofilho@608: } renatofilho@608: renatofilho@751: return TRUE; renatofilho@679: renatofilho@751: /* ERRORS */ renatofilho@751: init_failed: renatofilho@751: if (gmyth_uri != NULL) renatofilho@608: { renatofilho@751: g_object_unref(gmyth_uri); renatofilho@751: gmyth_uri = NULL; renatofilho@608: } renatofilho@608: renatofilho@751: if (src->spawn_livetv != NULL) renatofilho@751: { renatofilho@751: g_object_unref(src->spawn_livetv); renatofilho@751: src->spawn_livetv = NULL; renatofilho@751: } renatofilho@751: renatofilho@751: GST_ELEMENT_ERROR(src, LIBRARY, INIT, renatofilho@751: (NULL), ("Could not initialize MythTV library (%i, %s)", renatofilho@751: ret, src->uri_name)); renatofilho@751: renatofilho@751: renatofilho@751: gst_mythtv_src_clear(src); renatofilho@751: renatofilho@751: return FALSE; renatofilho@751: begin_req_failed: renatofilho@751: if (gmyth_uri != NULL) renatofilho@751: { renatofilho@751: g_object_unref(gmyth_uri); renatofilho@751: gmyth_uri = NULL; renatofilho@751: } renatofilho@751: renatofilho@751: GST_ELEMENT_ERROR(src, LIBRARY, INIT, renatofilho@751: (NULL), renatofilho@751: ("Could not begin request sent to MythTV server (%i, %s)", renatofilho@751: ret, src->uri_name)); renatofilho@751: return FALSE; renatofilho@608: renatofilho@608: } renatofilho@608: renatofilho@608: static gboolean renatofilho@751: gst_mythtv_src_get_size(GstBaseSrc * bsrc, guint64 * size) renatofilho@608: { renatofilho@751: GstMythtvSrc *src = GST_MYTHTV_SRC(bsrc); renatofilho@608: gboolean ret = TRUE; renatofilho@608: renatofilho@751: GST_LOG_OBJECT(src, "Differs from previous content size: %d (max.: %d)", renatofilho@751: abs(src->content_size - src->prev_content_size), renatofilho@751: GMYTHTV_TRANSFER_MAX_BUFFER); renatofilho@608: renatofilho@751: if (src->live_tv) renatofilho@751: { renatofilho@751: ret = FALSE; renatofilho@751: } renatofilho@751: else if (src->live_tv && src->enable_timing_position renatofilho@751: && (abs(src->content_size - src->bytes_read) < renatofilho@751: GMYTHTV_TRANSFER_MAX_BUFFER)) renatofilho@751: { renatofilho@608: renatofilho@751: gint64 new_offset = renatofilho@751: gmyth_recorder_get_file_position(src->spawn_livetv->recorder); renatofilho@751: if (new_offset > 0 && new_offset > src->content_size) renatofilho@751: { renatofilho@751: src->content_size = new_offset; renatofilho@751: } renatofilho@751: else if (new_offset < src->content_size) renatofilho@751: { renatofilho@751: src->update_prog_chain = TRUE; renatofilho@751: } renatofilho@608: renatofilho@751: } renatofilho@608: renatofilho@608: *size = src->content_size; renatofilho@751: GST_LOG_OBJECT(src, "Content size = %lld", src->content_size); renatofilho@608: renatofilho@608: return ret; renatofilho@608: renatofilho@608: } renatofilho@608: renatofilho@608: /* close the socket and associated resources renatofilho@608: * used both to recover from errors and go to NULL state */ renatofilho@608: static gboolean renatofilho@751: gst_mythtv_src_stop(GstBaseSrc * bsrc) renatofilho@608: { renatofilho@751: GstMythtvSrc *src = GST_MYTHTV_SRC(bsrc); renatofilho@608: renatofilho@751: gst_mythtv_src_clear(src); renatofilho@751: renatofilho@751: /* src->eos = FALSE; */ renatofilho@608: renatofilho@608: return TRUE; renatofilho@608: } renatofilho@608: renatofilho@608: static gboolean renatofilho@751: gst_mythtv_src_handle_event(GstPad * pad, GstEvent * event) renatofilho@608: { renatofilho@751: GstMythtvSrc *src = GST_MYTHTV_SRC(GST_PAD_PARENT(pad)); renatofilho@608: gint64 cont_size = 0; renatofilho@608: gboolean ret = FALSE; renatofilho@608: renatofilho@751: switch (GST_EVENT_TYPE(event)) renatofilho@751: { renatofilho@751: case GST_EVENT_EOS: renatofilho@751: GST_WARNING_OBJECT(src, "Got EOS event"); renatofilho@608: renatofilho@751: if (src->live_tv) renatofilho@751: { renatofilho@751: cont_size = gst_mythtv_src_get_position(src); renatofilho@751: if (cont_size > src->content_size) renatofilho@751: { renatofilho@751: src->content_size = cont_size; renatofilho@751: src->eos = FALSE; renatofilho@751: } renatofilho@751: else renatofilho@751: { renatofilho@751: src->eos = TRUE; renatofilho@751: gst_element_set_state(GST_ELEMENT(src), GST_STATE_NULL); renatofilho@751: gst_element_set_locked_state(GST_ELEMENT(src), FALSE); renatofilho@751: } renatofilho@751: } renatofilho@751: break; renatofilho@751: default: renatofilho@751: ret = gst_pad_event_default(pad, event); renatofilho@751: } renatofilho@608: renatofilho@608: return ret; renatofilho@608: } renatofilho@608: renatofilho@608: static gboolean renatofilho@751: gst_mythtv_src_is_seekable(GstBaseSrc * push_src) renatofilho@608: { renatofilho@608: return TRUE; renatofilho@608: } renatofilho@608: renatofilho@608: static gboolean renatofilho@751: gst_mythtv_src_handle_query(GstPad * pad, GstQuery * query) renatofilho@608: { renatofilho@608: gboolean res = FALSE; renatofilho@751: GstMythtvSrc *myth = GST_MYTHTV_SRC(gst_pad_get_parent(pad)); renatofilho@608: GstFormat formt; renatofilho@608: renatofilho@674: renatofilho@751: switch (GST_QUERY_TYPE(query)) renatofilho@751: { renatofilho@751: case GST_QUERY_POSITION: renatofilho@751: { renatofilho@751: gst_query_parse_position(query, &formt, NULL); renatofilho@751: if (formt == GST_FORMAT_BYTES) renatofilho@751: { renatofilho@751: gst_query_set_position(query, formt, myth->read_offset); renatofilho@751: GST_DEBUG_OBJECT(myth, "POS %" G_GINT64_FORMAT, renatofilho@751: myth->read_offset); renatofilho@751: res = TRUE; renatofilho@751: } renatofilho@751: else if (formt == GST_FORMAT_TIME) renatofilho@751: { renatofilho@751: res = gst_pad_query_default(pad, query); renatofilho@751: } renatofilho@751: break; renatofilho@751: } renatofilho@751: case GST_QUERY_DURATION: renatofilho@751: { renatofilho@751: gst_query_parse_duration(query, &formt, NULL); renatofilho@751: if (formt == GST_FORMAT_BYTES) renatofilho@751: { renatofilho@751: gint64 size = myth->content_size; renatofilho@751: gst_query_set_duration(query, GST_FORMAT_BYTES, 10); renatofilho@751: GST_DEBUG_OBJECT(myth, "SIZE %" G_GINT64_FORMAT, size); renatofilho@751: res = TRUE; renatofilho@751: } renatofilho@751: else if (formt == GST_FORMAT_TIME) renatofilho@751: { renatofilho@751: res = gst_pad_query_default(pad, query); renatofilho@751: } renatofilho@751: break; renatofilho@751: } renatofilho@751: default: renatofilho@751: { renatofilho@751: res = gst_pad_query_default(pad, query); renatofilho@751: break; renatofilho@751: } renatofilho@751: } renatofilho@608: renatofilho@751: gst_object_unref(myth); renatofilho@608: renatofilho@608: return res; renatofilho@608: } renatofilho@608: renatofilho@608: static GstStateChangeReturn renatofilho@751: gst_mythtv_src_change_state(GstElement * element, GstStateChange transition) renatofilho@608: { renatofilho@608: GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE; renatofilho@751: GstMythtvSrc *src = GST_MYTHTV_SRC(element); renatofilho@608: renatofilho@751: switch (transition) renatofilho@751: { renatofilho@751: case GST_STATE_CHANGE_NULL_TO_READY: renatofilho@751: break; renatofilho@751: case GST_STATE_CHANGE_READY_TO_PAUSED: renatofilho@751: case GST_STATE_CHANGE_PAUSED_TO_PLAYING: renatofilho@751: if (src->live_tv) renatofilho@751: { renatofilho@751: if (!gmyth_recorder_send_frontend_ready_command(src->spawn_livetv-> renatofilho@751: recorder)) renatofilho@751: GST_WARNING_OBJECT(src, renatofilho@751: "Couldn't send the FRONTEND_READY message to the backend!"); renatofilho@751: else renatofilho@751: GST_DEBUG_OBJECT(src, "FRONTEND_READY was sent to the backend"); renatofilho@751: } renatofilho@751: break; renatofilho@751: default: renatofilho@751: break; renatofilho@751: } renatofilho@692: renatofilho@608: renatofilho@751: ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); renatofilho@751: if (ret == GST_STATE_CHANGE_FAILURE) renatofilho@751: return ret; renatofilho@692: renatofilho@692: renatofilho@751: switch (transition) renatofilho@751: { renatofilho@751: case GST_STATE_CHANGE_READY_TO_NULL: renatofilho@751: gst_mythtv_src_clear(src); renatofilho@751: break; renatofilho@751: case GST_STATE_CHANGE_PLAYING_TO_PAUSED: renatofilho@751: case GST_STATE_CHANGE_PAUSED_TO_READY: renatofilho@751: break; renatofilho@751: default: renatofilho@751: break; renatofilho@751: } renatofilho@608: renatofilho@608: return ret; renatofilho@608: } renatofilho@608: renatofilho@608: static void renatofilho@751: gst_mythtv_src_set_property(GObject * object, guint prop_id, renatofilho@751: const GValue * value, GParamSpec * pspec) renatofilho@608: { renatofilho@751: GstMythtvSrc *mythtvsrc = GST_MYTHTV_SRC(object); renatofilho@608: renatofilho@751: GST_OBJECT_LOCK(mythtvsrc); renatofilho@751: switch (prop_id) renatofilho@751: { renatofilho@751: case PROP_LOCATION: renatofilho@751: { renatofilho@751: if (!g_value_get_string(value)) renatofilho@751: { renatofilho@751: GST_WARNING("location property cannot be NULL"); renatofilho@751: break; renatofilho@751: } renatofilho@608: renatofilho@751: if (mythtvsrc->uri_name != NULL) renatofilho@751: { renatofilho@751: g_free(mythtvsrc->uri_name); renatofilho@751: mythtvsrc->uri_name = NULL; renatofilho@751: } renatofilho@751: mythtvsrc->uri_name = g_value_dup_string(value); renatofilho@608: renatofilho@751: break; renatofilho@751: } renatofilho@608: #ifndef GST_DISABLE_GST_DEBUG renatofilho@751: case PROP_GMYTHTV_DBG: renatofilho@751: { renatofilho@751: mythtvsrc->mythtv_msgs_dbg = g_value_get_boolean(value); renatofilho@751: break; renatofilho@751: } renatofilho@608: #endif renatofilho@751: case PROP_GMYTHTV_VERSION: renatofilho@751: { renatofilho@751: mythtvsrc->mythtv_version = g_value_get_int(value); renatofilho@751: break; renatofilho@751: } renatofilho@751: case PROP_GMYTHTV_LIVEID: renatofilho@751: { renatofilho@751: mythtvsrc->live_tv_id = g_value_get_int(value); renatofilho@751: break; renatofilho@751: } renatofilho@751: case PROP_GMYTHTV_LIVE: renatofilho@751: { renatofilho@751: mythtvsrc->live_tv = g_value_get_boolean(value); renatofilho@751: break; renatofilho@751: } renatofilho@751: case PROP_GMYTHTV_ENABLE_TIMING_POSITION: renatofilho@751: { renatofilho@751: mythtvsrc->enable_timing_position = g_value_get_boolean(value); renatofilho@751: break; renatofilho@751: } renatofilho@751: case PROP_GMYTHTV_LIVE_CHAINID: renatofilho@751: { renatofilho@751: if (!g_value_get_string(value)) renatofilho@751: { renatofilho@751: GST_WARNING("MythTV Live chainid property cannot be NULL"); renatofilho@751: break; renatofilho@751: } renatofilho@608: renatofilho@751: if (mythtvsrc->live_chain_id != NULL) renatofilho@751: { renatofilho@751: g_free(mythtvsrc->live_chain_id); renatofilho@751: mythtvsrc->live_chain_id = NULL; renatofilho@751: } renatofilho@751: mythtvsrc->live_chain_id = g_value_dup_string(value); renatofilho@751: break; renatofilho@751: } renatofilho@751: case PROP_GMYTHTV_CHANNEL_NUM: renatofilho@751: { renatofilho@751: mythtvsrc->channel_name = g_value_dup_string(value); renatofilho@751: break; renatofilho@751: } renatofilho@751: case PROP_GMYTHTV_MAX_TRY: renatofilho@751: { renatofilho@751: mythtvsrc->max_try = g_value_get_int(value); renatofilho@751: break; renatofilho@751: } renatofilho@751: default: renatofilho@751: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); renatofilho@751: break; renatofilho@751: } renatofilho@608: renatofilho@751: 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@751: GValue * value, GParamSpec * pspec) renatofilho@608: { renatofilho@751: GstMythtvSrc *mythtvsrc = GST_MYTHTV_SRC(object); renatofilho@608: renatofilho@751: GST_OBJECT_LOCK(mythtvsrc); renatofilho@751: switch (prop_id) renatofilho@751: { renatofilho@751: case PROP_LOCATION: renatofilho@751: { renatofilho@751: g_value_set_string(value, mythtvsrc->uri_name); renatofilho@751: break; renatofilho@751: } renatofilho@608: #ifndef GST_DISABLE_GST_DEBUG renatofilho@751: case PROP_GMYTHTV_DBG: renatofilho@751: g_value_set_boolean(value, mythtvsrc->mythtv_msgs_dbg); renatofilho@751: break; renatofilho@608: #endif renatofilho@751: case PROP_GMYTHTV_VERSION: renatofilho@751: { renatofilho@751: g_value_set_int(value, mythtvsrc->mythtv_version); renatofilho@751: break; renatofilho@751: } renatofilho@751: case PROP_GMYTHTV_LIVEID: renatofilho@751: { renatofilho@751: g_value_set_int(value, mythtvsrc->live_tv_id); renatofilho@751: break; renatofilho@751: } renatofilho@751: case PROP_GMYTHTV_LIVE: renatofilho@751: g_value_set_boolean(value, mythtvsrc->live_tv); renatofilho@751: break; renatofilho@751: case PROP_GMYTHTV_ENABLE_TIMING_POSITION: renatofilho@751: g_value_set_boolean(value, mythtvsrc->enable_timing_position); renatofilho@751: break; renatofilho@751: case PROP_GMYTHTV_LIVE_CHAINID: renatofilho@751: { renatofilho@751: g_value_set_string(value, mythtvsrc->live_chain_id); renatofilho@751: break; renatofilho@751: } renatofilho@751: case PROP_GMYTHTV_CHANNEL_NUM: renatofilho@751: { renatofilho@751: g_value_set_string(value, mythtvsrc->channel_name); renatofilho@751: break; renatofilho@751: } renatofilho@751: case PROP_GMYTHTV_MAX_TRY: renatofilho@751: { renatofilho@751: g_value_set_int(value, mythtvsrc->max_try); renatofilho@751: break; renatofilho@751: } renatofilho@608: renatofilho@751: default: renatofilho@751: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); renatofilho@751: break; renatofilho@751: } renatofilho@751: GST_OBJECT_UNLOCK(mythtvsrc); renatofilho@608: } renatofilho@608: renatofilho@608: static gboolean renatofilho@751: plugin_init(GstPlugin * plugin) renatofilho@608: { renatofilho@751: return gst_element_register(plugin, "mythtvsrc", GST_RANK_NONE, renatofilho@751: GST_TYPE_MYTHTV_SRC); renatofilho@608: } renatofilho@608: renatofilho@751: GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, renatofilho@751: GST_VERSION_MINOR, renatofilho@751: "mythtv", renatofilho@751: "lib MythTV src", renatofilho@751: plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, renatofilho@751: GST_PACKAGE_ORIGIN); renatofilho@608: renatofilho@608: renatofilho@608: /*** GSTURIHANDLER INTERFACE *************************************************/ renatofilho@608: static guint renatofilho@751: gst_mythtv_src_uri_get_type(void) renatofilho@608: { renatofilho@608: return GST_URI_SRC; renatofilho@608: } renatofilho@608: renatofilho@608: static gchar ** renatofilho@751: gst_mythtv_src_uri_get_protocols(void) renatofilho@608: { renatofilho@608: static gchar *protocols[] = { "myth", "myths", NULL }; renatofilho@608: renatofilho@608: return protocols; renatofilho@608: } renatofilho@608: renatofilho@608: static const gchar * renatofilho@751: gst_mythtv_src_uri_get_uri(GstURIHandler * handler) renatofilho@608: { renatofilho@751: GstMythtvSrc *src = GST_MYTHTV_SRC(handler); renatofilho@608: renatofilho@608: return src->uri_name; renatofilho@608: } renatofilho@608: renatofilho@608: static gboolean renatofilho@751: gst_mythtv_src_uri_set_uri(GstURIHandler * handler, const gchar * uri) renatofilho@608: { renatofilho@751: GstMythtvSrc *src = GST_MYTHTV_SRC(handler); renatofilho@608: renatofilho@608: gchar *protocol; renatofilho@608: renatofilho@751: protocol = gst_uri_get_protocol(uri); renatofilho@751: if ((strcmp(protocol, "myth") != 0) && (strcmp(protocol, "myths") != 0)) renatofilho@751: { renatofilho@751: g_free(protocol); renatofilho@751: return FALSE; renatofilho@751: } renatofilho@751: g_free(protocol); renatofilho@751: g_object_set(src, "location", uri, NULL); renatofilho@608: renatofilho@608: 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@608: GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface; renatofilho@608: renatofilho@608: iface->get_type = gst_mythtv_src_uri_get_type; renatofilho@608: iface->get_protocols = gst_mythtv_src_uri_get_protocols; renatofilho@608: iface->get_uri = gst_mythtv_src_uri_get_uri; renatofilho@608: 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@751: GstMythtvSrc *src = GST_MYTHTV_SRC(userdata); renatofilho@608: renatofilho@751: GST_DEBUG_OBJECT(src, "content size = %lld bytes", src->content_size); renatofilho@608: }