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