gst-gmyth/mythsrc/gstmythtvsrc.c
author morphbr
Wed Aug 29 15:33:31 2007 +0100 (2007-08-29)
branchtrunk
changeset 833 7d4d75351461
parent 829 0a4e6b811acc
child 834 56d4eb2b5514
permissions -rwxr-xr-x
[svn r839] - Update size of buffer for livetv inside gstmythtvsrc
     1 /*
     2  * GStreamer MythTV Plug-in Copyright (C) <2006> Rosfran Borges
     3  * <rosfran.borges@indt.org.br> This library is free software; you can
     4  * redistribute it and/or modify it under the terms of the GNU Library
     5  * General Public License as published by the Free Software Foundation;
     6  * either version 2 of the License, or (at your option) any later version.
     7  * This library is distributed in the hope that it will be useful, but
     8  * WITHOUT ANY WARRANTY; without even the implied warranty of
     9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library 
    10  * General Public License for more details. You should have received a copy 
    11  * of the GNU Library General Public License along with this library; if
    12  * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 
    13  * 330, Boston, MA 02111-1307, USA. 
    14  */
    15 /**
    16  * SECTION:element-mythtvsrc
    17  *
    18  * <refsect2>
    19  * <para>
    20  * MythTVSrc allows to access a remote MythTV backend streaming Video/Audio server,
    21  * and to render audio and video content through a TCP/IP connection to a specific
    22  * port on this server, and based on a known MythTV protocol that is based on 
    23  * some message passing, such as REQUEST_BLOCK on a specified number of bytes, to get
    24  * some chunk of remote file data.
    25  * You should pass the information aboute the remote MythTV backend server 
    26  * through the <link linkend="GstMythTVSrc--location">location</link> property.
    27  * </para>
    28  * <title>Examples</title>
    29  * <para>
    30  * If you want to get the LiveTV content (set channel, TV tuner, RemoteEncoder, 
    31  * Recorder),
    32  * put the following URI:
    33  * 
    34  * <programlisting> 
    35  *  myth://xxx.xxx.xxx.xxx:6543/livetv?channel=BBC
    36  * </programlisting>
    37  *
    38  * This URI will say to the gmyth library to configure the Recorder instance (used to
    39  * change the channel, start the TV multimedia content transmition, etc.), using
    40  * the IP address (xxx.xxx.xxx.xxx) and port number (6543) of the MythTV backend 
    41  * server, and setting the channel name to "BBC". 
    42  * 
    43  * To get a already recorded the MythTV NUV file, put the following URI:
    44  * 
    45  * <programlisting>
    46  *  myth://xxx.xxx.xxx.xxx:6543/filename.nuv
    47  * </programlisting>
    48  *
    49  * This URI will say to the gmyth library to configure the Recorder instance (used to
    50  * change the channel, start the TV multimedia content transmition, etc.), using
    51  * the IP address (xxx.xxx.xxx.xxx) and port number (6543) of the MythTV backend 
    52  * server, and setting the channel name to "BBC".
    53  * 
    54  * Another possible way to use the LiveTV content, and just in the case you want to 
    55  * use the mysql database, put the location URI in the following format:
    56  * 
    57  * <programlisting> 
    58  *  myth://mythtv:mythtv@xxx.xxx.xxx.xxx:6543/?mythconverg&channel=9
    59  * </programlisting>
    60  * 
    61  * Where the first field is the protocol (myth), the second and third are user 
    62  * name (mythtv) and password (mythtv), then backend host name and port number, 
    63  * and the last field is the database name (mythconverg).
    64  * </para>
    65  * </refsect2>
    66  */
    67 #ifdef HAVE_CONFIG_H
    68 #include "config.h"
    69 #endif
    70 
    71 #include "gstmythtvsrc.h"
    72 #include <gmyth/gmyth_file.h>
    73 #include <gmyth/gmyth_file_transfer.h>
    74 #include <gmyth/gmyth_file_local.h>
    75 #include <gmyth/gmyth_livetv.h>
    76 
    77 #include <gmyth/gmyth_socket.h>
    78 #include <gmyth/gmyth_tvchain.h>
    79 
    80 #include <string.h>
    81 #include <unistd.h>
    82 
    83 GST_DEBUG_CATEGORY_STATIC(mythtvsrc_debug);
    84 #define GST_GMYTHTV_ID_NUM                  1
    85 #define GST_GMYTHTV_CHANNEL_DEFAULT_NUM     (-1)
    86 #define GMYTHTV_VERSION_DEFAULT			    30
    87 #define GMYTHTV_TRANSFER_MAX_WAITS          100
    88 #define GMYTHTV_TRANSFER_MAX_RESENDS        2
    89 #define GMYTHTV_TRANSFER_MAX_BUFFER         (128*1024)
    90 #define READ_SIZE                           (8*1024)
    91 #define READ_SIZE_LIVETV                    (50*1024)
    92 #define GST_FLOW_ERROR_NO_DATA              (-101)
    93 
    94 static const GstElementDetails gst_mythtv_src_details =
    95 GST_ELEMENT_DETAILS("MythTV client source",
    96                     "Source/Network",
    97                     "Control and receive data as a client over the network "
    98                     "via raw socket connections using the MythTV protocol",
    99                     "Rosfran Borges <rosfran.borges@indt.org.br>");
   100 
   101 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE("src",
   102                                                                   GST_PAD_SRC,
   103                                                                   GST_PAD_ALWAYS,
   104                                                                   GST_STATIC_CAPS_ANY);
   105     /*
   106      * GST_STATIC_CAPS ("video/x-nuv")); 
   107      */
   108 
   109 enum {
   110     PROP_0,
   111     PROP_LOCATION,
   112     PROP_GMYTHTV_VERSION,
   113     PROP_GMYTHTV_LIVE,
   114     PROP_GMYTHTV_LIVEID,
   115     PROP_GMYTHTV_LIVE_CHAINID,
   116     PROP_GMYTHTV_ENABLE_TIMING_POSITION,
   117     PROP_GMYTHTV_CHANNEL_NUM
   118 };
   119 
   120 static void     gst_mythtv_src_clear(GstMythtvSrc * mythtv_src);
   121 
   122 static void     gst_mythtv_src_finalize(GObject * gobject);
   123 
   124 static GstFlowReturn gst_mythtv_src_create(GstPushSrc * psrc,
   125                                            GstBuffer ** outbuf);
   126 
   127 static gboolean gst_mythtv_src_start(GstBaseSrc * bsrc);
   128 static gboolean gst_mythtv_src_stop(GstBaseSrc * bsrc);
   129 static gboolean gst_mythtv_src_get_size(GstBaseSrc * bsrc, guint64 * size);
   130 static gboolean gst_mythtv_src_is_seekable(GstBaseSrc * push_src);
   131 
   132 static gboolean gst_mythtv_src_do_seek(GstBaseSrc * base,
   133                                        GstSegment * segment);
   134 
   135 static          GstStateChangeReturn
   136 gst_mythtv_src_change_state(GstElement * element,
   137                             GstStateChange transition);
   138 
   139 static void     gst_mythtv_src_set_property(GObject * object,
   140                                             guint prop_id,
   141                                             const GValue * value,
   142                                             GParamSpec * pspec);
   143 static void     gst_mythtv_src_get_property(GObject * object,
   144                                             guint prop_id, GValue * value,
   145                                             GParamSpec * pspec);
   146 
   147 static void     gst_mythtv_src_uri_handler_init(gpointer g_iface,
   148                                                 gpointer iface_data);
   149 
   150 static gboolean gst_mythtv_src_handle_query(GstPad * pad,
   151                                             GstQuery * query);
   152 
   153 static gboolean gst_mythtv_src_handle_event(GstPad * pad,
   154                                             GstEvent * event);
   155 
   156 static GMythFileReadResult do_read_request_response(GstMythtvSrc * src,
   157                                                     guint size,
   158                                                     GByteArray * data_ptr);
   159 
   160 static void
   161 _urihandler_init(GType type)
   162 {
   163     static const GInterfaceInfo urihandler_info = {
   164         gst_mythtv_src_uri_handler_init,
   165         NULL,
   166         NULL
   167     };
   168 
   169     g_type_add_interface_static(type, GST_TYPE_URI_HANDLER,
   170                                 &urihandler_info);
   171 
   172     GST_DEBUG_CATEGORY_INIT(mythtvsrc_debug, "mythtvsrc", 0, "MythTV src");
   173 }
   174 
   175 GST_BOILERPLATE_FULL(GstMythtvSrc, gst_mythtv_src, GstPushSrc,
   176                      GST_TYPE_PUSH_SRC, _urihandler_init)
   177     static void     gst_mythtv_src_base_init(gpointer g_class)
   178 {
   179     GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
   180 
   181     gst_element_class_add_pad_template(element_class,
   182                                        gst_static_pad_template_get
   183                                        (&srctemplate));
   184 
   185     gst_element_class_set_details(element_class, &gst_mythtv_src_details);
   186 
   187     element_class->change_state = gst_mythtv_src_change_state;
   188 
   189 }
   190 
   191 static void
   192 gst_mythtv_src_class_init(GstMythtvSrcClass * klass)
   193 {
   194     GObjectClass   *gobject_class;
   195     GstPushSrcClass *gstpushsrc_class;
   196     GstBaseSrcClass *gstbasesrc_class;
   197 
   198     gobject_class = (GObjectClass *) klass;
   199     gstbasesrc_class = (GstBaseSrcClass *) klass;
   200     gstpushsrc_class = (GstPushSrcClass *) klass;
   201 
   202     gobject_class->set_property = gst_mythtv_src_set_property;
   203     gobject_class->get_property = gst_mythtv_src_get_property;
   204     gobject_class->finalize = gst_mythtv_src_finalize;
   205 
   206     g_object_class_install_property
   207         (gobject_class, PROP_LOCATION,
   208          g_param_spec_string("location", "Location",
   209                              "The location. In the form:"
   210                              "\n\t\t\tmyth://a.com/file.nuv"
   211                              "\n\t\t\tmyth://a.com:23223/file.nuv "
   212                              "\n\t\t\ta.com/file.nuv - default scheme 'myth'",
   213                              "", G_PARAM_READWRITE));
   214 
   215     g_object_class_install_property
   216         (gobject_class, PROP_GMYTHTV_VERSION,
   217          g_param_spec_int("mythtv-version", "mythtv-version",
   218                           "Change MythTV version", 26, 30, 26,
   219                           G_PARAM_READWRITE));
   220 
   221     g_object_class_install_property
   222         (gobject_class, PROP_GMYTHTV_LIVEID,
   223          g_param_spec_int("mythtv-live-id", "mythtv-live-id",
   224                           "Change MythTV version",
   225                           0, 200, GST_GMYTHTV_ID_NUM, G_PARAM_READWRITE));
   226 
   227     g_object_class_install_property
   228         (gobject_class, PROP_GMYTHTV_LIVE_CHAINID,
   229          g_param_spec_string("mythtv-live-chainid", "mythtv-live-chainid",
   230                              "Sets the MythTV chain ID (from TV Chain)",
   231                              "", G_PARAM_READWRITE));
   232 
   233     g_object_class_install_property
   234         (gobject_class, PROP_GMYTHTV_LIVE,
   235          g_param_spec_boolean("mythtv-live", "mythtv-live",
   236                               "Enable MythTV Live TV content streaming",
   237                               FALSE, G_PARAM_READWRITE));
   238 
   239     g_object_class_install_property
   240         (gobject_class, PROP_GMYTHTV_ENABLE_TIMING_POSITION,
   241          g_param_spec_boolean("mythtv-enable-timing-position",
   242                               "mythtv-enable-timing-position",
   243                               "Enable MythTV Live TV content size continuous updating",
   244                               FALSE, G_PARAM_READWRITE));
   245 
   246     g_object_class_install_property
   247         (gobject_class, PROP_GMYTHTV_CHANNEL_NUM,
   248          g_param_spec_string("mythtv-channel", "mythtv-channel",
   249                              "Change MythTV channel number",
   250                              "", G_PARAM_READWRITE));
   251 
   252     gstbasesrc_class->start = gst_mythtv_src_start;
   253     gstbasesrc_class->stop = gst_mythtv_src_stop;
   254     gstbasesrc_class->get_size = gst_mythtv_src_get_size;
   255     gstbasesrc_class->is_seekable = gst_mythtv_src_is_seekable;
   256     gstbasesrc_class->do_seek = gst_mythtv_src_do_seek;
   257     gstpushsrc_class->create = gst_mythtv_src_create;
   258 
   259     GST_DEBUG_CATEGORY_INIT(mythtvsrc_debug, "mythtvsrc", 0,
   260                             "MythTV Client Source");
   261 }
   262 
   263 static void
   264 gst_mythtv_src_init(GstMythtvSrc * this, GstMythtvSrcClass * g_class)
   265 {
   266     this->file = NULL;
   267     this->unique_setup = FALSE;
   268     this->mythtv_version = GMYTHTV_VERSION_DEFAULT;
   269     this->state = GST_MYTHTV_SRC_FILE_TRANSFER;
   270     this->bytes_read = 0;
   271     this->prev_content_size = 0;
   272     this->content_size = 0;
   273     this->read_offset = 0;
   274     this->content_size_last = 0;
   275     this->live_tv = FALSE;
   276     this->enable_timing_position = FALSE;
   277     this->update_prog_chain = FALSE;
   278     this->user_agent = g_strdup("mythtvsrc");
   279     this->update_prog_chain = FALSE;
   280     this->channel_name = NULL;
   281     this->eos = FALSE;
   282     this->wait_to_transfer = 0;
   283     gst_base_src_set_format(GST_BASE_SRC(this), GST_FORMAT_BYTES);
   284     gst_pad_set_event_function(GST_BASE_SRC_PAD(GST_BASE_SRC(this)),
   285                                gst_mythtv_src_handle_event);
   286     gst_pad_set_query_function(GST_BASE_SRC_PAD(GST_BASE_SRC(this)),
   287                                gst_mythtv_src_handle_query);
   288 
   289 }
   290 
   291 static void
   292 gst_mythtv_src_clear(GstMythtvSrc * mythtv_src)
   293 {
   294     mythtv_src->unique_setup = FALSE;
   295 
   296     if (mythtv_src->spawn_livetv) {
   297         g_object_unref(mythtv_src->spawn_livetv);
   298         mythtv_src->spawn_livetv = NULL;
   299     }
   300 
   301     if (mythtv_src->file) {
   302         g_object_unref(mythtv_src->file);
   303         mythtv_src->file = NULL;
   304     }
   305 
   306     if (mythtv_src->backend_info) {
   307         g_object_unref(mythtv_src->backend_info);
   308         mythtv_src->backend_info = NULL;
   309     }
   310 }
   311 
   312 static void
   313 gst_mythtv_src_finalize(GObject * gobject)
   314 {
   315     GstMythtvSrc   *this = GST_MYTHTV_SRC(gobject);
   316 
   317     gst_mythtv_src_clear(this);
   318 
   319     if (this->uri_name) {
   320         g_free(this->uri_name);
   321         this->uri_name = NULL;
   322     }
   323 
   324     if (this->user_agent) {
   325         g_free(this->user_agent);
   326         this->user_agent = NULL;
   327     }
   328 
   329     G_OBJECT_CLASS(parent_class)->finalize(gobject);
   330 }
   331 
   332 static GMythFileReadResult
   333 do_read_request_response(GstMythtvSrc * src, guint size,
   334                          GByteArray * data_ptr)
   335 {
   336     gint            read = 0;
   337     guint           sizetoread = size;
   338     gint            max_iters = GMYTHTV_TRANSFER_MAX_RESENDS;
   339     GMythFileReadResult result;
   340 
   341     GST_LOG_OBJECT(src, "Starting: Reading %d bytes...", sizetoread);
   342 
   343     /*
   344      * Loop sending the Myth File Transfer request: Retry whilst
   345      * authentication fails and we supply it. 
   346      */
   347 
   348     while (sizetoread == size && --max_iters > 0) {
   349         /*
   350          * if ( gmyth_backend_info_is_local_file(src->backend_info) ) 
   351          */
   352         if (IS_GMYTH_FILE_LOCAL(src->file))
   353             result = gmyth_file_local_read(GMYTH_FILE_LOCAL(src->file),
   354                                            data_ptr, sizetoread,
   355                                            src->live_tv);
   356         else if (IS_GMYTH_FILE_TRANSFER(src->file))
   357             result =
   358                 gmyth_file_transfer_read(GMYTH_FILE_TRANSFER(src->file),
   359                                          data_ptr, sizetoread,
   360                                          src->live_tv);
   361 
   362         if (data_ptr->len > 0) {
   363             read += data_ptr->len;
   364             sizetoread -= data_ptr->len;
   365         } else if (data_ptr->len <= 0) {
   366             if (src->live_tv == FALSE) {
   367                 result = GMYTH_FILE_READ_EOF;
   368                 goto eos;
   369             } else {
   370                 if (result == GMYTH_FILE_READ_ERROR) {  /* -314 */
   371                     GST_INFO_OBJECT(src,
   372                                     "[LiveTV] FileTransfer READ_ERROR!");
   373                 }
   374                 goto done;
   375             }
   376         }
   377         /*
   378          * else if (data_ptr->len == 0) goto done; 
   379          */
   380         if (read == sizetoread)
   381             goto done;
   382     }
   383 
   384     if ((read < 0 && !src->live_tv) || max_iters == 0) {
   385         result = GMYTH_FILE_READ_EOF;
   386         goto eos;
   387     }
   388 
   389     goto done;
   390 
   391   eos:
   392     src->eos = TRUE;
   393 
   394   done:
   395     return result;
   396 }
   397 
   398 static GstFlowReturn
   399 gst_mythtv_src_create(GstPushSrc * psrc, GstBuffer ** outbuf)
   400 {
   401     GstMythtvSrc  *src;
   402     GstFlowReturn   ret = GST_FLOW_OK;
   403     GByteArray *buffer;
   404     gint buffer_remain = 0;
   405     GMythFileReadResult result = GMYTH_FILE_READ_OK;
   406     gboolean buffering = FALSE;
   407 
   408     src = GST_MYTHTV_SRC(psrc);
   409 
   410     buffer = g_byte_array_new ();
   411     if (src->live_tv)
   412         result = do_read_request_response(src, READ_SIZE_LIVETV, buffer);
   413     else
   414         result = do_read_request_response(src, READ_SIZE, buffer);
   415 
   416     if (result == GMYTH_FILE_READ_ERROR)
   417         goto read_error;
   418 
   419 
   420     *outbuf = gst_buffer_new();
   421     GST_BUFFER_SIZE(*outbuf) = buffer->len;
   422     GST_BUFFER_MALLOCDATA(*outbuf) = g_malloc0(GST_BUFFER_SIZE(*outbuf));
   423     GST_BUFFER_DATA(*outbuf) = GST_BUFFER_MALLOCDATA(*outbuf);
   424     g_memmove(GST_BUFFER_DATA((*outbuf)), buffer->data,
   425               GST_BUFFER_SIZE(*outbuf));
   426     GST_BUFFER_OFFSET(*outbuf) = src->read_offset;
   427     GST_BUFFER_OFFSET_END(*outbuf) =
   428         src->read_offset + GST_BUFFER_SIZE(*outbuf);
   429 
   430     src->read_offset += GST_BUFFER_SIZE(*outbuf);
   431     src->bytes_read += GST_BUFFER_SIZE(*outbuf);
   432 
   433     g_byte_array_free (buffer, TRUE);
   434 
   435     if (result == GMYTH_FILE_READ_NEXT_PROG_CHAIN) {
   436         GstPad *peer;
   437 
   438         peer = gst_pad_get_peer (GST_BASE_SRC_PAD (GST_BASE_SRC (psrc)));
   439         gst_pad_send_event (peer,
   440             gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0));
   441 
   442         gst_object_unref (peer);
   443     }
   444 
   445     if (src->eos  ||
   446         (!src->live_tv && (src->bytes_read >= src->content_size)))
   447         ret = GST_FLOW_UNEXPECTED;
   448 
   449     return ret;
   450 
   451 read_error:
   452     GST_ELEMENT_ERROR(src, RESOURCE, READ,
   453         (NULL), ("Could not read any bytes (%i, %s)",
   454         read, src->uri_name));
   455     return GST_FLOW_ERROR;
   456 }
   457 
   458 gint64
   459 gst_mythtv_src_get_position(GstMythtvSrc * src)
   460 {
   461 
   462     gint64          size_tmp = 0;
   463     guint           max_tries = 2;
   464 
   465     if (src->live_tv == TRUE && (abs(src->content_size - src->bytes_read) <
   466                                  GMYTHTV_TRANSFER_MAX_BUFFER)) {
   467 
   468       get_file_pos:
   469         g_usleep(10);
   470         size_tmp =
   471             gmyth_recorder_get_file_position(src->spawn_livetv->recorder);
   472         if (size_tmp > (src->content_size + GMYTHTV_TRANSFER_MAX_BUFFER))
   473             src->content_size = size_tmp;
   474         else if (size_tmp > 0 && --max_tries > 0)
   475             goto get_file_pos;
   476         GST_LOG_OBJECT(src, "GET_POSITION: file_position = %lld",
   477                        size_tmp);
   478         /*
   479          * sets the last content size amount before it can be updated 
   480          */
   481         src->prev_content_size = src->content_size;
   482     }
   483 
   484     return src->content_size;
   485 
   486 }
   487 
   488 static gboolean
   489 gst_mythtv_src_do_seek(GstBaseSrc * base, GstSegment * segment)
   490 {
   491     GstMythtvSrc   *src = GST_MYTHTV_SRC(base);
   492     gint64          new_offset = -1;
   493     gint64          actual_seek = segment->start;
   494     gboolean        ret = TRUE;
   495 
   496     GST_LOG_OBJECT(src, "seek, segment: %" GST_SEGMENT_FORMAT, segment);
   497 
   498     if (segment->format == GST_FORMAT_TIME) {
   499         goto done;
   500     }
   501     GST_LOG_OBJECT(src,
   502                    "Trying to seek at the value (actual_seek = %lld, read_offset = %lld)",
   503                    actual_seek, src->read_offset);
   504     /*
   505      * verify if it needs to seek 
   506      */
   507     if (src->read_offset != actual_seek) {
   508 
   509         /*
   510          * if ( gmyth_backend_info_is_local_file(src->backend_info) ) 
   511          */
   512         if (IS_GMYTH_FILE_LOCAL(src->file))
   513             new_offset =
   514                 gmyth_file_local_seek(GMYTH_FILE_LOCAL(src->file),
   515                                       segment->start, G_SEEK_SET);
   516         else if (IS_GMYTH_FILE_TRANSFER(src->file))
   517             new_offset =
   518                 gmyth_file_transfer_seek(GMYTH_FILE_TRANSFER(src->file),
   519                                          segment->start, SEEK_SET);
   520 
   521         GST_LOG_OBJECT(src,
   522                        "Segment offset start = %lld, SRC Offset = %lld, NEW actual backend SEEK Offset = %lld.",
   523                        segment->start, src->read_offset, new_offset);
   524         if (G_UNLIKELY(new_offset < 0)) {
   525             ret = FALSE;
   526             if (!src->live_tv)
   527                 goto eos;
   528         }
   529 
   530         src->read_offset = new_offset;
   531 
   532         if (ret == FALSE) {
   533             GST_INFO_OBJECT(src, "Failed to set the SEEK on segment!");
   534         }
   535 
   536     }
   537 
   538   done:
   539     return ret;
   540 
   541   eos:
   542     {
   543         GST_DEBUG_OBJECT(src, "EOS found on seeking!!!");
   544         return FALSE;
   545     }
   546 
   547 }
   548 
   549 /*
   550  * create a socket for connecting to remote server 
   551  */
   552 static gboolean
   553 gst_mythtv_src_start(GstBaseSrc * bsrc)
   554 {
   555     GstMythtvSrc   *src = GST_MYTHTV_SRC(bsrc);
   556 
   557     GString        *chain_id_local = NULL;
   558     GMythURI       *gmyth_uri = NULL;
   559     gboolean        ret = TRUE;
   560     GstMessage     *msg;
   561 
   562     if (src->unique_setup == FALSE) {
   563         src->unique_setup = TRUE;
   564     } else {
   565         goto done;
   566     }
   567 
   568     gmyth_uri = gmyth_uri_new_with_value(src->uri_name);
   569     src->backend_info = gmyth_backend_info_new_with_uri(src->uri_name);
   570     src->live_tv = gmyth_uri_is_livetv(gmyth_uri);
   571 
   572     if (src->live_tv) {
   573         src->spawn_livetv = gmyth_livetv_new(src->backend_info);
   574         gchar *ch = gmyth_uri_get_channel_name(gmyth_uri);
   575         if (ch != NULL)
   576             src->channel_name = ch;
   577 
   578         if (src->channel_name != NULL) {
   579             gboolean        result;
   580             result =
   581                 gmyth_livetv_channel_name_setup(src->spawn_livetv,
   582                                                 src->channel_name);
   583             if (result == FALSE) {
   584                 GST_INFO_OBJECT(src, "LiveTV setup felt down on error");
   585                 ret = FALSE;
   586                 goto init_failed;
   587             }
   588 
   589         } else {
   590             if (gmyth_livetv_setup(src->spawn_livetv) == FALSE) {
   591                 GST_INFO_OBJECT(src, "LiveTV setup felt down on error");
   592                 ret = FALSE;
   593                 goto init_failed;
   594             }
   595         }
   596 
   597         /*
   598          * testing change channel... 
   599          */
   600         /*
   601          * gmyth_recorder_change_channel( src->spawn_livetv->recorder,
   602          * CHANNEL_DIRECTION_UP ); 
   603          */
   604 
   605         src->file =
   606             GMYTH_FILE(gmyth_livetv_create_file_transfer
   607                        (src->spawn_livetv));
   608 
   609 
   610         if (NULL == src->file) {
   611             GST_INFO_OBJECT(src, "[LiveTV] FileTransfer equals to NULL");
   612             ret = FALSE;
   613             goto init_failed;
   614         }
   615 
   616         /*
   617          * Check if the file is local to this specific client renderer 
   618          */
   619         if (gmyth_uri_is_local_file(gmyth_uri))
   620             ret = gmyth_file_local_open(GMYTH_FILE_LOCAL(src->file));
   621         else
   622             ret =
   623                 gmyth_file_transfer_open(GMYTH_FILE_TRANSFER(src->file),
   624                                          src->spawn_livetv->uri !=
   625                                          NULL ? gmyth_uri_get_path(src->
   626                                                                    spawn_livetv->
   627                                                                    uri) :
   628                                          src->spawn_livetv->proginfo->
   629                                          pathname->str);
   630 
   631         /*
   632          * sets the mythtvsrc "location" property 
   633          */
   634         g_object_set(src, "location", gmyth_file_get_uri(src->file), NULL);
   635 
   636         if (!ret) {
   637             GST_INFO_OBJECT(src,
   638                             "Error: couldn't open the FileTransfer from LiveTV source!");
   639             g_object_unref(src->file);
   640             src->file = NULL;
   641             goto init_failed;
   642         }
   643     } else {
   644 
   645         /*
   646          * Check if the file is local to this specific client renderer,
   647          * and tries to open a local connection 
   648          */
   649         if (gmyth_uri_is_local_file(gmyth_uri)) {
   650             src->file =
   651                 GMYTH_FILE(gmyth_file_local_new(src->backend_info));
   652             ret = gmyth_file_local_open(GMYTH_FILE_LOCAL(src->file));
   653         } else {
   654             src->file =
   655                 GMYTH_FILE(gmyth_file_transfer_new(src->backend_info));
   656             ret =
   657                 gmyth_file_transfer_open(GMYTH_FILE_TRANSFER(src->file),
   658                                          src->uri_name);
   659         }
   660 
   661     }                           /* if (else) - recorded FileTransfer */
   662 
   663     if (NULL == src->file) {
   664         GST_INFO_OBJECT(src, "FileTransfer is NULL");
   665         goto init_failed;
   666     }
   667     /*
   668      * GST_INFO_OBJECT( src, "uri = %s", src->spawn_livetv->file); 
   669      */
   670 
   671     if (ret == FALSE) {
   672       GST_INFO_OBJECT(src,
   673         "MythTV FileTransfer request failed when setting up socket connection!");
   674       goto begin_req_failed;
   675     }
   676 
   677     GST_INFO_OBJECT(src,
   678                     "MythTV FileTransfer filesize = %lld, content_size = %lld!",
   679                     gmyth_file_get_filesize(src->file), src->content_size);
   680 
   681     src->content_size = gmyth_file_get_filesize(src->file);
   682 
   683     msg =
   684         gst_message_new_duration(GST_OBJECT(src), GST_FORMAT_BYTES,
   685                                  src->content_size);
   686     gst_element_post_message(GST_ELEMENT(src), msg);
   687 
   688 
   689     src->do_start = FALSE;
   690 
   691     gst_pad_push_event(GST_BASE_SRC_PAD(GST_BASE_SRC(src)),
   692                        gst_event_new_new_segment(TRUE, 1.0,
   693                                                  GST_FORMAT_TIME, 0,
   694                                                  src->content_size, 0));
   695 
   696   done:
   697     if (gmyth_uri != NULL) {
   698         g_object_unref(gmyth_uri);
   699         gmyth_uri = NULL;
   700     }
   701 
   702     if (chain_id_local != NULL) {
   703         g_string_free(chain_id_local, TRUE);
   704         chain_id_local = NULL;
   705     }
   706 
   707     return TRUE;
   708 
   709     /*
   710      * ERRORS 
   711      */
   712   init_failed:
   713     if (gmyth_uri != NULL) {
   714         g_object_unref(gmyth_uri);
   715         gmyth_uri = NULL;
   716     }
   717 
   718     if (src->spawn_livetv != NULL) {
   719         g_object_unref(src->spawn_livetv);
   720         src->spawn_livetv = NULL;
   721     }
   722 
   723     GST_ELEMENT_ERROR(src, LIBRARY, INIT,
   724                       (NULL),
   725                       ("Could not initialize MythTV library (%i, %s)", ret,
   726                        src->uri_name));
   727 
   728 
   729     gst_mythtv_src_clear(src);
   730 
   731     return FALSE;
   732   begin_req_failed:
   733     if (gmyth_uri != NULL) {
   734         g_object_unref(gmyth_uri);
   735         gmyth_uri = NULL;
   736     }
   737 
   738     GST_ELEMENT_ERROR(src, LIBRARY, INIT,
   739                       (NULL),
   740                       ("Could not begin request sent to MythTV server (%i, %s)",
   741                        ret, src->uri_name));
   742     return FALSE;
   743 
   744 }
   745 
   746 static gboolean
   747 gst_mythtv_src_get_size(GstBaseSrc * bsrc, guint64 * size)
   748 {
   749     GstMythtvSrc   *src = GST_MYTHTV_SRC(bsrc);
   750     gboolean        ret = TRUE;
   751 
   752     GST_LOG_OBJECT(src,
   753                    "Differs from previous content size: %d (max.: %d)",
   754                    abs(src->content_size - src->prev_content_size),
   755                    GMYTHTV_TRANSFER_MAX_BUFFER);
   756 
   757     if (src->live_tv) {
   758         ret = FALSE;
   759     } else if (src->live_tv && src->enable_timing_position
   760                && (abs(src->content_size - src->bytes_read) <
   761                    GMYTHTV_TRANSFER_MAX_BUFFER)) {
   762 
   763         gint64          new_offset =
   764             gmyth_recorder_get_file_position(src->spawn_livetv->recorder);
   765         if (new_offset > 0 && new_offset > src->content_size) {
   766             src->content_size = new_offset;
   767         } else if (new_offset < src->content_size) {
   768             src->update_prog_chain = TRUE;
   769         }
   770 
   771     }
   772 
   773     *size = src->content_size;
   774     GST_LOG_OBJECT(src, "Content size = %lld", src->content_size);
   775 
   776     return ret;
   777 
   778 }
   779 
   780 /*
   781  * close the socket and associated resources used both to recover from
   782  * errors and go to NULL state 
   783  */
   784 static gboolean
   785 gst_mythtv_src_stop(GstBaseSrc * bsrc)
   786 {
   787     GstMythtvSrc   *src = GST_MYTHTV_SRC(bsrc);
   788 
   789     gst_mythtv_src_clear(src);
   790     return TRUE;
   791 }
   792 
   793 static gboolean
   794 gst_mythtv_src_handle_event(GstPad * pad, GstEvent * event)
   795 {
   796     GstMythtvSrc   *src = GST_MYTHTV_SRC(GST_PAD_PARENT(pad));
   797     gint64          cont_size = 0;
   798     gboolean        ret = TRUE;
   799 
   800     switch (GST_EVENT_TYPE(event)) {
   801       case GST_EVENT_EOS:
   802         if (src->live_tv) {
   803           cont_size = gst_mythtv_src_get_position(src);
   804           if (cont_size > src->content_size) {
   805             src->content_size = cont_size;
   806             src->eos = FALSE;
   807           } else {
   808             src->eos = TRUE;
   809             gst_element_set_state(GST_ELEMENT(src), GST_STATE_NULL);
   810             gst_element_set_locked_state(GST_ELEMENT(src), FALSE);
   811           }
   812         }
   813         break;
   814     default:
   815         ret = gst_pad_event_default(pad, event);
   816     }
   817 
   818     GST_DEBUG_OBJECT (src, "HANDLE EVENT %d", ret);
   819     return ret;
   820 }
   821 
   822 static gboolean
   823 gst_mythtv_src_is_seekable(GstBaseSrc * push_src)
   824 {
   825     return TRUE;
   826 }
   827 
   828 static gboolean
   829 gst_mythtv_src_handle_query(GstPad * pad, GstQuery * query)
   830 {
   831     gboolean        res = FALSE;
   832     GstMythtvSrc   *myth = GST_MYTHTV_SRC(gst_pad_get_parent(pad));
   833     GstFormat       formt;
   834 
   835 
   836     switch (GST_QUERY_TYPE(query)) {
   837       case GST_QUERY_POSITION:
   838         gst_query_parse_position(query, &formt, NULL);
   839         if (formt == GST_FORMAT_BYTES) {
   840           gst_query_set_position(query, formt, myth->read_offset);
   841           GST_DEBUG_OBJECT(myth, "POS %" G_GINT64_FORMAT,
   842               myth->read_offset);
   843           res = TRUE;
   844         } else if (formt == GST_FORMAT_TIME) {
   845           res = gst_pad_query_default(pad, query);
   846         }
   847         break;
   848       case GST_QUERY_DURATION:
   849         gst_query_parse_duration(query, &formt, NULL);
   850         if (formt == GST_FORMAT_BYTES) {
   851           gint64 size = myth->content_size;
   852 
   853           gst_query_set_duration(query, GST_FORMAT_BYTES, 10);
   854           GST_DEBUG_OBJECT(myth, "SIZE %" G_GINT64_FORMAT, size);
   855           res = TRUE;
   856         } else if (formt == GST_FORMAT_TIME) {
   857           res = gst_pad_query_default(pad, query);
   858         }
   859         break;
   860       default:
   861         res = gst_pad_query_default(pad, query);
   862         break;
   863     }
   864 
   865     gst_object_unref(myth);
   866 
   867     return res;
   868 }
   869 
   870 static GstStateChangeReturn
   871 gst_mythtv_src_change_state(GstElement * element,
   872                             GstStateChange transition)
   873 {
   874     GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE;
   875     GstMythtvSrc  *src = GST_MYTHTV_SRC(element);
   876 
   877     switch (transition) {
   878     case GST_STATE_CHANGE_NULL_TO_READY:
   879         break;
   880     case GST_STATE_CHANGE_READY_TO_PAUSED:
   881       if (!src->uri_name) {
   882         GST_WARNING_OBJECT (src, "Invalid location");
   883         return ret;
   884       }
   885       break;
   886     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
   887       if (src->live_tv) {
   888         if (!gmyth_recorder_send_frontend_ready_command
   889             (src->spawn_livetv->recorder))
   890           GST_WARNING_OBJECT(src,
   891               "Couldn't send the FRONTEND_READY message to the backend!");
   892         else
   893           GST_DEBUG_OBJECT(src, "FRONTEND_READY was sent to the backend");
   894       }
   895       break;
   896     default:
   897         break;
   898     }
   899 
   900     ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
   901     if (ret == GST_STATE_CHANGE_FAILURE) {
   902         return ret;
   903     }
   904 
   905 
   906     switch (transition) {
   907     case GST_STATE_CHANGE_READY_TO_NULL:
   908         gst_mythtv_src_clear(src);
   909         break;
   910     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
   911     case GST_STATE_CHANGE_PAUSED_TO_READY:
   912         break;
   913     default:
   914         break;
   915     }
   916 
   917     return ret;
   918 }
   919 
   920 static void
   921 gst_mythtv_src_set_property(GObject * object, guint prop_id,
   922                             const GValue * value, GParamSpec * pspec)
   923 {
   924     GstMythtvSrc   *mythtvsrc = GST_MYTHTV_SRC(object);
   925 
   926     GST_OBJECT_LOCK(mythtvsrc);
   927     switch (prop_id) {
   928       case PROP_LOCATION:
   929         if (!g_value_get_string(value)) {
   930           GST_WARNING("location property cannot be NULL");
   931           break;
   932         }
   933 
   934         if (mythtvsrc->uri_name != NULL) {
   935           g_free(mythtvsrc->uri_name);
   936           mythtvsrc->uri_name = NULL;
   937         }
   938         mythtvsrc->uri_name = g_value_dup_string(value);
   939         break;
   940       case PROP_GMYTHTV_VERSION:
   941         mythtvsrc->mythtv_version = g_value_get_int(value);
   942         break;
   943       case PROP_GMYTHTV_LIVEID:
   944         mythtvsrc->live_tv_id = g_value_get_int(value);
   945         break;
   946       case PROP_GMYTHTV_LIVE:
   947         mythtvsrc->live_tv = g_value_get_boolean(value);
   948         break;
   949       case PROP_GMYTHTV_ENABLE_TIMING_POSITION:
   950          mythtvsrc->enable_timing_position = g_value_get_boolean(value);
   951          break;
   952       case PROP_GMYTHTV_LIVE_CHAINID:
   953          if (!g_value_get_string(value)) {
   954             GST_WARNING_OBJECT(object, "MythTV Live chainid property cannot be NULL");
   955             break;
   956          }
   957 
   958          if (mythtvsrc->live_chain_id != NULL) {
   959            g_free(mythtvsrc->live_chain_id);
   960            mythtvsrc->live_chain_id = NULL;
   961          }
   962          mythtvsrc->live_chain_id = g_value_dup_string(value);
   963          break;
   964       case PROP_GMYTHTV_CHANNEL_NUM:
   965         mythtvsrc->channel_name = g_value_dup_string(value);
   966         break;
   967       default:
   968         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
   969         break;
   970     }
   971 
   972     GST_OBJECT_UNLOCK(mythtvsrc);
   973 }
   974 
   975 static void
   976 gst_mythtv_src_get_property(GObject * object, guint prop_id,
   977                             GValue * value, GParamSpec * pspec)
   978 {
   979     GstMythtvSrc   *mythtvsrc = GST_MYTHTV_SRC(object);
   980 
   981     GST_OBJECT_LOCK(mythtvsrc);
   982     switch (prop_id) {
   983       case PROP_LOCATION:
   984         g_value_set_string(value, mythtvsrc->uri_name);
   985         break;
   986       case PROP_GMYTHTV_VERSION:
   987         g_value_set_int(value, mythtvsrc->mythtv_version);
   988         break;
   989       case PROP_GMYTHTV_LIVEID:
   990         g_value_set_int(value, mythtvsrc->live_tv_id);
   991         break;
   992       case PROP_GMYTHTV_LIVE:
   993         g_value_set_boolean(value, mythtvsrc->live_tv);
   994         break;
   995       case PROP_GMYTHTV_ENABLE_TIMING_POSITION:
   996         g_value_set_boolean(value, mythtvsrc->enable_timing_position);
   997         break;
   998       case PROP_GMYTHTV_LIVE_CHAINID:
   999         g_value_set_string(value, mythtvsrc->live_chain_id);
  1000         break;
  1001       case PROP_GMYTHTV_CHANNEL_NUM:
  1002         g_value_set_string(value, mythtvsrc->channel_name);
  1003         break;
  1004     default:
  1005         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
  1006         break;
  1007     }
  1008     GST_OBJECT_UNLOCK(mythtvsrc);
  1009 }
  1010 
  1011 static gboolean
  1012 plugin_init(GstPlugin * plugin)
  1013 {
  1014     return gst_element_register(plugin, "mythtvsrc", GST_RANK_NONE,
  1015                                 GST_TYPE_MYTHTV_SRC);
  1016 }
  1017 
  1018 GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
  1019                   GST_VERSION_MINOR,
  1020                   "mythtv",
  1021                   "lib MythTV src",
  1022                   plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
  1023                   GST_PACKAGE_ORIGIN);
  1024 
  1025 
  1026 /*** GSTURIHANDLER INTERFACE *************************************************/
  1027 static guint
  1028 gst_mythtv_src_uri_get_type(void)
  1029 {
  1030     return GST_URI_SRC;
  1031 }
  1032 
  1033 static gchar  **
  1034 gst_mythtv_src_uri_get_protocols(void)
  1035 {
  1036     static gchar   *protocols[] = { "myth", "myths", NULL };
  1037 
  1038     return protocols;
  1039 }
  1040 
  1041 static const gchar *
  1042 gst_mythtv_src_uri_get_uri(GstURIHandler * handler)
  1043 {
  1044     GstMythtvSrc   *src = GST_MYTHTV_SRC(handler);
  1045 
  1046     return src->uri_name;
  1047 }
  1048 
  1049 static gboolean
  1050 gst_mythtv_src_uri_set_uri(GstURIHandler * handler, const gchar * uri)
  1051 {
  1052     GstMythtvSrc   *src = GST_MYTHTV_SRC(handler);
  1053 
  1054     gchar          *protocol;
  1055 
  1056     protocol = gst_uri_get_protocol(uri);
  1057     if ((strcmp(protocol, "myth") != 0)
  1058         && (strcmp(protocol, "myths") != 0)) {
  1059         g_free(protocol);
  1060         return FALSE;
  1061     }
  1062     g_free(protocol);
  1063     g_object_set(src, "location", uri, NULL);
  1064 
  1065     return TRUE;
  1066 }
  1067 
  1068 static void
  1069 gst_mythtv_src_uri_handler_init(gpointer g_iface, gpointer iface_data)
  1070 {
  1071     GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
  1072 
  1073     iface->get_type = gst_mythtv_src_uri_get_type;
  1074     iface->get_protocols = gst_mythtv_src_uri_get_protocols;
  1075     iface->get_uri = gst_mythtv_src_uri_get_uri;
  1076     iface->set_uri = gst_mythtv_src_uri_set_uri;
  1077 }
  1078 
  1079 void
  1080 size_header_handler(void *userdata, const char *value)
  1081 {
  1082     GstMythtvSrc   *src = GST_MYTHTV_SRC(userdata);
  1083 
  1084     GST_DEBUG_OBJECT(src, "content size = %lld bytes", src->content_size);
  1085 }