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