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