[svn r71] Added some new functions: set channel, change program info improvements, faster live TV content rendering.
1 /* vim: set sw=2: -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2; c-indent-level: 2 -*- */
2 /* GStreamer MythTV Plug-in
3 * Copyright (C) <2006> Rosfran Borges <rosfran.borges@indt.org.br>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library Lesser General
7 * Public License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more
20 #include "gstmythtvsrc.h"
21 #include <gmyth/gmyth_file_transfer.h>
22 #include <gmyth/gmyth_livetv.h>
24 #include <gmyth/gmyth_socket.h>
25 #include <gmyth/gmyth_tvchain.h>
30 GST_DEBUG_CATEGORY_STATIC (mythtvsrc_debug);
31 #define GST_CAT_DEFAULT mythtvsrc_debug
33 #define GST_GMYTHTV_ID_NUM 1
35 #define GST_GMYTHTV_CHANNEL_NUM 1000
37 #define GMYTHTV_VERSION_DEFAULT 30
39 #define GMYTHTV_TRANSFER_MAX_WAITS 100
41 #define GMYTHTV_TRANSFER_MAX_BUFFER 64*1024
45 #define MAX_READ_SIZE 4*1024
48 #define GST_FLOW_ERROR_NO_DATA -101
50 /* stablish a maximum iteration value to the IS_RECORDING message */
51 static guint wait_to_transfer = 0;
53 static const GstElementDetails gst_mythtv_src_details =
54 GST_ELEMENT_DETAILS ( "MythTV client source",
56 "Control and receive data as a client over the network via raw socket connections using the MythTV protocol",
57 "Rosfran Borges <rosfran.borges@indt.org.br>" );
59 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
62 GST_STATIC_CAPS ("video/x-nuv") );
64 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
67 GST_STATIC_CAPS ("video/x-nuv") );
74 #ifndef GST_DISABLE_GST_DEBUG
80 PROP_GMYTHTV_LIVE_CHAINID,
81 PROP_GMYTHTV_ENABLE_TIMING_POSITION,
82 PROP_GMYTHTV_CHANNEL_NUM
85 static void gst_mythtv_src_finalize (GObject * gobject);
87 static GstFlowReturn gst_mythtv_src_create (GstBaseSrc * psrc, guint64 offset,
88 guint size, GstBuffer ** outbuf);
90 static gboolean gst_mythtv_src_start (GstBaseSrc * bsrc);
91 static gboolean gst_mythtv_src_stop (GstBaseSrc * bsrc);
92 static gboolean gst_mythtv_src_get_size (GstBaseSrc * bsrc, guint64 * size);
93 static gboolean gst_mythtv_src_is_seekable( GstBaseSrc *push_src );
95 static gboolean gst_mythtv_src_next_program_chain ( GstMythtvSrc *src );
97 static GstStateChangeReturn
98 gst_mythtv_src_change_state (GstElement * element, GstStateChange transition);
100 static void gst_mythtv_src_set_property (GObject * object, guint prop_id,
101 const GValue * value, GParamSpec * pspec);
102 static void gst_mythtv_src_get_property (GObject * object, guint prop_id,
103 GValue * value, GParamSpec * pspec);
105 static void gst_mythtv_src_uri_handler_init (gpointer g_iface, gpointer iface_data);
107 static gboolean gst_mythtv_src_handle_event (GstPad * pad, GstEvent * event);
108 //static gboolean gst_mythtv_src_query ( GstPad * pad, GstQuery * query );
110 static gint do_read_request_response (GstMythtvSrc *src, guint64 offset,
111 guint size, GstBuffer **outbuf);
114 _urihandler_init (GType type)
116 static const GInterfaceInfo urihandler_info = {
117 gst_mythtv_src_uri_handler_init,
122 g_type_add_interface_static (type, GST_TYPE_URI_HANDLER, &urihandler_info);
124 GST_DEBUG_CATEGORY_INIT (mythtvsrc_debug, "mythtvsrc", 0,
128 GST_BOILERPLATE_FULL (GstMythtvSrc, gst_mythtv_src, GstBaseSrc,
129 GST_TYPE_BASE_SRC, _urihandler_init)
131 //GST_BOILERPLATE_FULL (GstMythtvSrc, gst_mythtv_src, GstPushSrc,
132 // GST_TYPE_PUSH_SRC, _urihandler_init)
135 gst_mythtv_src_base_init (gpointer g_class)
137 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
139 gst_element_class_add_pad_template (element_class,
140 gst_static_pad_template_get (&srctemplate));
142 gst_element_class_set_details (element_class, &gst_mythtv_src_details);
144 element_class->change_state = gst_mythtv_src_change_state;
148 gst_mythtv_src_class_init (GstMythtvSrcClass * klass)
150 GObjectClass *gobject_class;
151 //GstPushSrcClass *gstpushsrc_class;
152 GstBaseSrcClass *gstbasesrc_class;
154 gobject_class = (GObjectClass *) klass;
155 gstbasesrc_class = (GstBaseSrcClass *) klass;
157 gobject_class->set_property = gst_mythtv_src_set_property;
158 gobject_class->get_property = gst_mythtv_src_get_property;
159 gobject_class->finalize = gst_mythtv_src_finalize;
161 g_object_class_install_property
162 (gobject_class, PROP_LOCATION,
163 g_param_spec_string ("location", "Location",
164 "The location. In the form:"
165 "\n\t\t\tmyth://a.com/file.nuv"
166 "\n\t\t\tmyth://a.com:23223/file.nuv "
167 "\n\t\t\ta.com/file.nuv - default scheme 'myth'",
168 "", G_PARAM_READWRITE));
170 g_object_class_install_property
171 (gobject_class, PROP_URI,
172 g_param_spec_string ("uri", "Uri",
173 "The location in form of a URI (deprecated; use location)",
174 "", G_PARAM_READWRITE));
176 g_object_class_install_property
177 (gobject_class, PROP_GMYTHTV_VERSION,
178 g_param_spec_int ("mythtv-version", "mythtv-version",
179 "Change MythTV version",
180 26, 30, 26, G_PARAM_READWRITE));
182 g_object_class_install_property
183 (gobject_class, PROP_GMYTHTV_LIVEID,
184 g_param_spec_int ("mythtv-live-id", "mythtv-live-id",
185 "Change MythTV version",
186 0, 200, GST_GMYTHTV_ID_NUM, G_PARAM_READWRITE));
188 g_object_class_install_property
189 (gobject_class, PROP_GMYTHTV_LIVE_CHAINID,
190 g_param_spec_string ("mythtv-live-chainid", "mythtv-live-chainid",
191 "Sets the MythTV chain ID (from TV Chain)",
192 "", G_PARAM_READWRITE));
194 g_object_class_install_property
195 (gobject_class, PROP_GMYTHTV_LIVE,
196 g_param_spec_boolean ("mythtv-live", "mythtv-live",
197 "Enable MythTV Live TV content streaming",
198 FALSE, G_PARAM_READWRITE));
200 g_object_class_install_property
201 (gobject_class, PROP_GMYTHTV_ENABLE_TIMING_POSITION,
202 g_param_spec_boolean ("mythtv-enable-timing-position", "mythtv-enable-timing-position",
203 "Enable MythTV Live TV content size continuous updating",
204 FALSE, G_PARAM_READWRITE));
206 g_object_class_install_property
207 (gobject_class, PROP_GMYTHTV_CHANNEL_NUM,
208 g_param_spec_int ("mythtv-channel", "mythtv-channel",
209 "Change MythTV channel number",
210 0, 200, GST_GMYTHTV_CHANNEL_NUM, G_PARAM_READWRITE));
212 #ifndef GST_DISABLE_GST_DEBUG
213 g_object_class_install_property
214 (gobject_class, PROP_GMYTHTV_DBG,
215 g_param_spec_boolean ("mythtv-debug", "mythtv-debug",
216 "Enable MythTV debug messages",
217 FALSE, G_PARAM_READWRITE));
220 gstbasesrc_class->start = gst_mythtv_src_start;
221 gstbasesrc_class->stop = gst_mythtv_src_stop;
222 gstbasesrc_class->get_size = gst_mythtv_src_get_size;
223 gstbasesrc_class->is_seekable = gst_mythtv_src_is_seekable;
225 gstbasesrc_class->create = gst_mythtv_src_create;
227 GST_DEBUG_CATEGORY_INIT (mythtvsrc_debug, "mythtvsrc", 0,
228 "MythTV Client Source");
232 gst_mythtv_src_init (GstMythtvSrc * this, GstMythtvSrcClass * g_class)
234 this->file_transfer = NULL;
236 this->unique_setup = FALSE;
238 this->mythtv_version = GMYTHTV_VERSION_DEFAULT;
240 this->state = GST_MYTHTV_SRC_FILE_TRANSFER;
242 this->bytes_read = 0;
244 this->prev_content_size = 0;
246 this->content_size = 0;
247 this->read_offset = 0;
249 this->content_size_last = 0;
251 this->live_tv = FALSE;
253 this->enable_timing_position = FALSE;
254 this->update_prog_chain = FALSE;
256 this->user_agent = g_strdup ("mythtvsrc");
257 this->mythtv_caps = NULL;
258 this->update_prog_chain = FALSE;
260 this->channel_num = 0;
264 this->adapter = NULL;
266 this->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
267 gst_element_add_pad (GST_ELEMENT (this), this->sinkpad);
270 this->srcpad = gst_pad_new_from_static_template (&srctemplate, "src");
271 gst_element_add_pad (GST_ELEMENT (this), this->srcpad);
274 gst_base_src_set_format( GST_BASE_SRC( this ), GST_FORMAT_BYTES );
276 gst_base_src_set_live ( GST_BASE_SRC( this ), TRUE );
278 gst_pad_set_event_function ( GST_BASE_SRC_PAD(GST_BASE_SRC(this)),
279 gst_mythtv_src_handle_event );
281 gst_pad_set_query_function ( GST_BASE_SRC_PAD(GST_BASE_SRC(this)),
282 gst_mythtv_src_query );
288 gst_mythtv_src_finalize (GObject * gobject)
290 GstMythtvSrc *this = GST_MYTHTV_SRC (gobject);
292 if (this->mythtv_caps) {
293 gst_caps_unref (this->mythtv_caps);
294 this->mythtv_caps = NULL;
297 if (this->file_transfer) {
298 g_object_unref (this->file_transfer);
299 this->file_transfer = NULL;
302 if (this->spawn_livetv) {
303 g_object_unref (this->spawn_livetv);
304 this->spawn_livetv = NULL;
307 if (this->uri_name) {
308 g_free (this->uri_name);
311 if (this->user_agent) {
312 g_free (this->user_agent);
315 G_OBJECT_CLASS (parent_class)->finalize (gobject);
319 do_read_request_response (GstMythtvSrc * src, guint64 offset, guint size, GstBuffer **outbuf)
322 guint sizetoread = size;
324 g_print( "Starting: [%s] Reading %d bytes...\n", __FUNCTION__, sizetoread );
326 /* Loop sending the Myth File Transfer request:
327 * Retry whilst authentication fails and we supply it. */
330 /* max number of buffer resend interactions */
331 //guint max_tries = 3;
333 GST_OBJECT_LOCK(src);
335 while ( sizetoread > 0 ) {
337 len = gmyth_file_transfer_read( src->file_transfer,
338 GST_BUFFER_DATA( *outbuf ) + read, sizetoread, TRUE );
348 if ( src->live_tv == FALSE )
354 if ( len == GMYTHTV_FILE_TRANSFER_READ_ERROR ) { /* -314 */
355 src->update_prog_chain = TRUE;
358 if ( abs( src->content_size - src->bytes_read ) < GMYTHTV_TRANSFER_MAX_BUFFER ) {
359 src->update_prog_chain = TRUE;
360 if ( src->enable_timing_position ) {
363 size_tmp = gmyth_file_transfer_get_file_position( src->file_transfer );
364 if ( size_tmp > ( src->content_size + GMYTHTV_TRANSFER_MAX_BUFFER ) )
365 src->content_size = size_tmp;
366 else if ( size_tmp > 0 )
368 g_print( "\t[%s]\tGET_POSITION: file_position = %lld\n",
369 __FUNCTION__, size_tmp );
370 } else if ( abs( src->content_size - src->bytes_read ) < GMYTHTV_TRANSFER_MAX_BUFFER ) {
371 src->prev_content_size = src->content_size;
372 gint64 new_offset = gmyth_file_transfer_get_file_position( src->file_transfer );
373 if ( new_offset > 0 && src->content_size <= new_offset ) {
374 src->content_size = new_offset;
376 src->update_prog_chain = TRUE;
386 if ( read == sizetoread )
391 src->read_offset += read;
392 src->bytes_read += read;
393 //src->content_size += src->bytes_read;
395 g_print( "[%s]\tBYTES READ (actual) = %d, BYTES READ (cumulative) = %llu, "\
396 "OFFSET = %llu, CONTENT SIZE = %llu.\n", __FUNCTION__, read, src->bytes_read,
397 src->read_offset, src->content_size );
399 GST_BUFFER_SIZE (*outbuf) = read; //GST_BUFFER_SIZE (buffer) = read;
400 GST_BUFFER_OFFSET (*outbuf) = offset; //GST_BUFFER_OFFSET (buffer) = offset;
401 GST_BUFFER_OFFSET_END (*outbuf) = offset + read;//GST_BUFFER_OFFSET_END (buffer) = offset + read;
403 g_print( "Got buffer: [%s]\t\tBUFFER --->SIZE = %d, OFFSET = %llu, "\
404 "OFFSET_END = %llu.\n\n", __FUNCTION__, GST_BUFFER_SIZE (*outbuf),
405 GST_BUFFER_OFFSET (*outbuf), GST_BUFFER_OFFSET_END (*outbuf) );
407 } else if ( !src->live_tv )
416 GST_OBJECT_UNLOCK(src);
422 gst_mythtv_src_create ( GstBaseSrc * psrc, guint64 offset, guint size, GstBuffer **outbuf)
425 GstFlowReturn ret = GST_FLOW_OK;
428 src = GST_MYTHTV_SRC (psrc);
429 /* The caller should know the number of bytes and not read beyond EOS. */
430 if (G_UNLIKELY (src->eos))
432 if ( G_UNLIKELY (src->update_prog_chain) )
433 goto change_progchain;
435 GST_OBJECT_LOCK(src);
437 if (G_UNLIKELY (src->read_offset != offset)) {
438 gint64 new_offset = gmyth_file_transfer_seek(src->file_transfer, offset - src->content_size_last, SEEK_SET);
439 g_print( "[%s] SRC Offset = %lld, NEW actual backend SEEK Offset = %lld.\n",
440 __FUNCTION__, src->read_offset, new_offset );
441 if (G_UNLIKELY (new_offset < 0 ) )//|| new_offset != src->read_offset)) {
443 GST_OBJECT_UNLOCK(src);
445 goto change_progchain;
450 src->read_offset = offset;
452 GST_OBJECT_UNLOCK(src);
454 /* Create the buffer. */
455 ret = gst_pad_alloc_buffer (GST_BASE_SRC_PAD (GST_BASE_SRC (psrc)),
456 src->read_offset, size,
457 //src->icy_caps ? src->icy_caps :
458 GST_PAD_CAPS (GST_BASE_SRC_PAD (GST_BASE_SRC (psrc))), outbuf);
460 if (G_UNLIKELY (ret != GST_FLOW_OK)) {
462 goto change_progchain;
467 read = do_read_request_response ( src, src->read_offset, size, outbuf );
469 if (G_UNLIKELY (src->update_prog_chain) )
470 goto change_progchain;
472 if (G_UNLIKELY (read <= 0) || *outbuf == NULL) {
474 goto change_progchain;
481 const gchar *reason = gst_flow_get_name (ret);
483 GST_DEBUG_OBJECT (src, "DONE task, reason %s", reason);
488 const gchar *reason = gst_flow_get_name (ret);
490 GST_DEBUG_OBJECT (src, "pausing task, reason %s", reason);
491 return GST_FLOW_UNEXPECTED;
496 GST_ELEMENT_ERROR (src, RESOURCE, READ,
497 (NULL), ("Could not read any bytes (%i, %s)", read,
499 return GST_FLOW_ERROR;
503 GST_ELEMENT_ERROR (src, RESOURCE, READ,
504 (NULL), ("Seek failed, go to the next program info... (%i, %s)", read,
506 // go to the next program chain
507 src->unique_setup = FALSE;
508 src->update_prog_chain = TRUE;
510 //GST_OBJECT_LOCK(src);
511 gst_mythtv_src_next_program_chain( src );
512 //GST_OBJECT_UNLOCK(src);
514 return GST_FLOW_ERROR_NO_DATA;
520 gst_mythtv_src_get_position ( GstMythtvSrc* src )
525 if (src->live_tv == TRUE && ( abs( src->content_size - src->prev_content_size ) <
526 GMYTHTV_TRANSFER_MAX_BUFFER ) ) {
527 /* sets the last content size amount before it can be updated */
528 src->prev_content_size = src->content_size;
531 size_tmp = gmyth_file_transfer_get_file_position( src->file_transfer );
532 if ( size_tmp > ( src->content_size + GMYTHTV_TRANSFER_MAX_BUFFER ) )
533 src->content_size = size_tmp;
534 else if ( size_tmp > 0 && --max_tries > 0 )
536 g_print( "\t[%s]\tGET_POSITION: file_position = %lld\n",
537 __FUNCTION__, size_tmp );
540 return src->content_size;
544 /* create a socket for connecting to remote server */
546 gst_mythtv_src_start ( GstBaseSrc * bsrc )
548 GstMythtvSrc *src = GST_MYTHTV_SRC (bsrc);
550 GString *chain_id_local = NULL;
554 if ( !src->do_start )
557 if (src->unique_setup == FALSE) {
558 src->unique_setup = TRUE;
563 GST_OBJECT_LOCK(src);
565 if ( src->live_tv ) {
566 src->spawn_livetv = gmyth_livetv_new( );
567 if ( gmyth_livetv_setup( src->spawn_livetv ) == FALSE ) {
569 GST_OBJECT_UNLOCK( src );
573 /* set up the uri variable */
574 src->uri_name = g_strdup( src->spawn_livetv->proginfo->pathname->str );
575 chain_id_local = gmyth_tvchain_get_id( src->spawn_livetv->tvchain );
576 if ( chain_id_local != NULL ) {
577 src->live_chain_id = g_strdup( chain_id_local->str );
578 g_print( "\t[%s]\tLocal chain ID = %s.\n", __FUNCTION__, src->live_chain_id );
580 src->live_tv_id = src->spawn_livetv->recorder->recorder_num;
581 g_print ( "[%s] LiveTV id = %d, URI path = %s.\n", __FUNCTION__, src->live_tv_id, src->uri_name );
584 src->file_transfer = gmyth_file_transfer_new( src->live_tv_id,
585 g_string_new( src->uri_name ), -1, src->mythtv_version );
587 if ( src->file_transfer == NULL ) {
588 GST_OBJECT_UNLOCK(src);
593 /* sets the Playback monitor connection */
594 ret = gmyth_file_transfer_playback_setup( &(src->file_transfer), src->live_tv );
596 if ( src->live_tv == TRUE && ret == TRUE ) {
597 /* loop finished, set the max tries variable to zero again... */
598 wait_to_transfer = 0;
600 while ( wait_to_transfer++ < GMYTHTV_TRANSFER_MAX_WAITS &&
601 ( gmyth_file_transfer_is_recording( src->file_transfer ) == FALSE
602 /*|| ( gmyth_file_transfer_get_file_position( src->file_transfer ) < ( src->content_size + 327680 ) )*/ ) )
606 /* sets the FileTransfer instance connection (video/audio download) */
607 ret = gmyth_file_transfer_setup( &(src->file_transfer), src->live_tv );
609 if ( ret == FALSE ) {
610 GST_OBJECT_UNLOCK(src);
611 #ifndef GST_DISABLE_GST_DEBUG
612 if ( src->mythtv_msgs_dbg )
613 g_printerr( "MythTV FileTransfer request failed when setting up socket connection!\n" );
615 goto begin_req_failed;
618 src->content_size = src->file_transfer->filesize;
620 GST_OBJECT_UNLOCK(src);
622 src->do_start = FALSE;
630 if (src->spawn_livetv != NULL )
631 g_object_unref( src->spawn_livetv );
633 GST_ELEMENT_ERROR (src, LIBRARY, INIT,
634 (NULL), ("Could not initialize MythTV library (%i, %s)", ret, src->uri_name));
639 GST_ELEMENT_ERROR (src, LIBRARY, INIT,
640 (NULL), ("Could not begin request sent to MythTV server (%i, %s)", ret, src->uri_name));
645 /* create a new socket for connecting to the next program chain */
647 gst_mythtv_src_next_program_chain ( GstMythtvSrc *src )
649 GString *chain_id_local = NULL;
656 if (src->unique_setup == FALSE) {
657 src->unique_setup = TRUE;
662 if (src->file_transfer) {
663 g_object_unref (src->file_transfer);
664 src->file_transfer = NULL;
668 g_free (src->uri_name);
671 if ( src->live_tv ) {
672 if ( gmyth_livetv_next_program_chain( src->spawn_livetv ) == FALSE ) {
673 g_print( "\n\n[%s]\t\tFailed to go to the next program chain!!!\n\n", __FUNCTION__ );
677 /* set up the uri variable */
678 src->uri_name = g_strdup( src->spawn_livetv->proginfo->pathname->str );
679 chain_id_local = gmyth_tvchain_get_id( src->spawn_livetv->tvchain );
680 if ( chain_id_local != NULL ) {
681 src->live_chain_id = g_strdup( chain_id_local->str );
682 g_print( "\t[%s]\tLocal chain ID = %s.\n", __FUNCTION__, src->live_chain_id );
684 src->live_tv_id = src->spawn_livetv->recorder->recorder_num;
685 g_print ( "[%s] LiveTV id = %d, URI path = %s.\n", __FUNCTION__, src->live_tv_id, src->uri_name );
688 src->file_transfer = gmyth_file_transfer_new( src->live_tv_id,
689 g_string_new( src->uri_name ), -1, src->mythtv_version );
691 if ( src->file_transfer == NULL ) {
695 /* sets the Playback monitor connection */
696 ret = gmyth_file_transfer_playback_setup( &(src->file_transfer), src->live_tv );
698 if ( src->live_tv == TRUE && ret == TRUE ) {
699 /* loop finished, set the max tries variable to zero again... */
700 wait_to_transfer = 0;
704 while ( wait_to_transfer++ < GMYTHTV_TRANSFER_MAX_WAITS &&
705 ( gmyth_file_transfer_is_recording( src->file_transfer ) == FALSE ) )
709 /* sets the FileTransfer instance connection (video/audio download) */
710 ret = gmyth_file_transfer_setup( &(src->file_transfer), src->live_tv );
712 if ( ret == FALSE ) {
713 #ifndef GST_DISABLE_GST_DEBUG
714 if ( src->mythtv_msgs_dbg )
715 g_printerr( "MythTV FileTransfer request failed when setting up socket connection!\n" );
717 goto begin_req_failed;
719 src->content_size_last = src->content_size;
722 if ( src->content_size < src->file_transfer->filesize ) {
723 src->content_size = src->file_transfer->filesize;
725 //gint64 pos = gst_mythtv_src_get_position(src);
726 //if ( pos > src->file_transfer->filesize )
727 // src->content_size = pos;
732 src->content_size = src->file_transfer->filesize;
733 while ( src->content_size < GMYTHTV_TRANSFER_MAX_BUFFER*4 )
734 src->content_size = gst_mythtv_src_get_position( src );
736 src->read_offset = 0;
738 src->update_prog_chain = FALSE;
740 gst_pad_push_event ( src->sinkpad,
741 gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES, 0, src->content_size, 0 ) );
749 if (src->spawn_livetv != NULL )
750 g_object_unref( src->spawn_livetv );
752 GST_ELEMENT_ERROR (src, LIBRARY, INIT,
753 (NULL), ("Could not initialize MythTV library (%i, %s)", ret, src->uri_name));
758 GST_ELEMENT_ERROR (src, LIBRARY, INIT,
759 (NULL), ("Could not begin request sent to MythTV server (%i, %s)", ret, src->uri_name));
766 /* handles queries for location in the stream in the requested format */
768 gst_mythtv_src_query ( GstPad * pad, GstQuery * query )
771 GstMythtvSrc *mythtv;
775 mythtv = GST_GMYTHTV_SRC( GST_PAD_PARENT (pad) );
777 size = gst_mythtv_src_get_position (mythtv);
779 switch (GST_QUERY_TYPE (query)) {
781 case GST_QUERY_POSITION:
787 /* save requested format */
788 gst_query_parse_position (query, NULL, &cur);
790 /* query peer for current position in time */
791 g_print( "[%s] Actual size is %s than current size from sink. [ %lld, %lld ]\n", __FUNCTION__,
792 ( size > cur ) ? "greater" : "lower", size, cur );
793 gst_query_set_position (query, GST_FORMAT_BYTES, size);
800 case GST_QUERY_DURATION:
805 /* save requested format */
806 gst_query_parse_position (query, NULL, &cur);
808 /* query peer for current position in time */
809 g_print( "[%s] Actual size is %s than current size from sink. [ %lld, %lld ]\n", __FUNCTION__,
810 ( size * GST_SECOND > cur * GST_SECOND ) ? "greater" : "lower", size * GST_SECOND,
812 gst_query_set_position (query, GST_FORMAT_TIME, size * GST_SECOND );
814 if ( size * GST_SECOND < cur * GST_SECOND )
834 gst_mythtv_src_get_size (GstBaseSrc * bsrc, guint64 * size)
836 GstMythtvSrc *src = GST_MYTHTV_SRC (bsrc);
838 g_print( "[%s] Differs from previous content size: %d (max.: %d)\n", __FUNCTION__,
839 abs( src->content_size - src->prev_content_size ), GMYTHTV_TRANSFER_MAX_BUFFER );
841 if (src->content_size <= 0) {
843 } else if ( src->live_tv && ( abs( src->content_size - src->bytes_read ) <
844 GMYTHTV_TRANSFER_MAX_BUFFER ) ) {
845 //g_static_mutex_lock( &update_size_mutex );
846 //GST_OBJECT_LOCK(src);
848 gint64 new_offset = gmyth_file_transfer_get_file_position( src->file_transfer );
849 if ( new_offset > 0 && new_offset > src->content_size ) {
850 src->content_size = new_offset;
851 } else if ( new_offset < src->content_size ) {
852 src->update_prog_chain = TRUE;
855 if ( src->enable_timing_position ) {
857 if (src->live_tv == TRUE) {
860 size_tmp = gmyth_file_transfer_get_file_position( src->file_transfer );
861 if ( size_tmp > ( src->content_size + GMYTHTV_TRANSFER_MAX_BUFFER ) )
862 src->content_size = size_tmp;
863 else if ( size_tmp > 0 )
865 g_print( "\t[%s]\tGET_POSITION: file_position = %lld\n",
866 __FUNCTION__, size_tmp );
870 src->prev_content_size = src->content_size;
872 //GST_OBJECT_UNLOCK(src);
873 //g_static_mutex_unlock( &update_size_mutex );
876 *size = src->content_size;
877 g_print( "[%s] Content size = %lld\n", __FUNCTION__, src->content_size );
883 /* close the socket and associated resources
884 * used both to recover from errors and go to NULL state */
886 gst_mythtv_src_stop (GstBaseSrc * bsrc)
890 src = GST_MYTHTV_SRC (bsrc);
893 g_free (src->uri_name);
894 src->uri_name = NULL;
897 if (src->mythtv_caps) {
898 gst_caps_unref (src->mythtv_caps);
899 src->mythtv_caps = NULL;
908 gst_mythtv_src_handle_event (GstPad * pad, GstEvent * event)
910 GstMythtvSrc *src = GST_MYTHTV_SRC (GST_PAD_PARENT (pad));
911 gint64 cont_size = 0;
913 switch (GST_EVENT_TYPE (event)) {
915 case GST_EVENT_FLUSH_START:
917 g_print( "\n\n\n[%s]\t\tGot FLUSH_START event!!!\n\n\n", __FUNCTION__ );
918 cont_size = gst_mythtv_src_get_position (src);
919 if ( !src->live_tv ) {
920 if ( cont_size > src->content_size ) {
921 src->content_size = cont_size;
925 gst_element_set_state ( GST_ELEMENT (src), GST_STATE_NULL );
926 gst_element_set_locked_state ( GST_ELEMENT (src), FALSE );
929 if ( cont_size <= 0 ) {
930 src->update_prog_chain = TRUE;
932 src->unique_setup = FALSE;
933 src->do_start = TRUE;
937 case GST_EVENT_FLUSH_STOP:
938 src->do_start = TRUE;
940 gst_element_set_state (GST_ELEMENT(src), GST_STATE_NULL);
941 //gst_element_set_locked_state (GST_ELEMENT(src), TRUE);
945 g_print( "[%s] Got EOS event!!!\n", __FUNCTION__ );
947 if ( src->live_tv ) {
948 cont_size = gst_mythtv_src_get_position (src);
949 if ( cont_size > src->content_size ) {
950 src->content_size = cont_size;
954 gst_element_set_state ( GST_ELEMENT (src), GST_STATE_NULL );
955 gst_element_set_locked_state ( GST_ELEMENT (src), FALSE );
961 case GST_EVENT_NEWSEGMENT:
962 g_print( "[%s] Got NEWSEGMENT!!!\n", __FUNCTION__ );
967 g_print( "[%s] Got EVENT_SEEK!!!\n", __FUNCTION__ );
969 //gboolean update = TRUE;
971 GstSeekType cur_type, stop_type;
973 gint64 cur = 0, stop = 0;
974 gst_event_parse_seek ( event, &rate, &format,
975 &flags, &cur_type, &cur,
978 g_print( "[%s] Got EVENT_SEEK.\n", __FUNCTION__ );
979 if ( !( flags & GST_SEEK_FLAG_FLUSH ) ) {
980 g_print( "[%s] Could get the FLAG_FLUSH message.\n", __FUNCTION__ );
982 //gboolean ret = gst_event_parse_new_segment ( event,
983 // &update, &rate, &format, &start, &stop,
985 //GstFlowReturn flow_ret = gst_mythtv_src_create (GST_BASE_SRC( GST_PAD_PARENT( psrc ) ),
986 // cur, stop - cur + 1, GstBuffer)
990 return gst_pad_event_default (pad, event);
993 return gst_pad_event_default (pad, event);
997 gst_mythtv_src_is_seekable( GstBaseSrc *push_src )
1003 static GstFlowReturn
1004 gst_mythtv_src_file_transfer( GstMythtvSrc *src )
1006 GstFlowReturn ret = GST_FLOW_OK;
1007 GstBuffer *buf = NULL;
1010 /* The caller should know the number of bytes and not read beyond EOS. */
1011 if ( G_UNLIKELY (src->eos) )
1013 if ( G_UNLIKELY (src->update_prog_chain) )
1014 goto change_progchain;
1018 /* Create the buffer. */
1019 buf = gst_buffer_new_and_alloc (4096);
1021 ret = gst_mythtv_src_read_bytes( src, GST_BUFFER_SIZE(buf), TRUE, &buf );
1022 if ( ret != GST_FLOW_OK )
1025 read = do_read_request_response ( src, src->read_offset, GST_BUFFER_SIZE(buf), &buf );
1027 if (G_UNLIKELY (src->update_prog_chain) )
1028 goto change_progchain;
1030 if (G_UNLIKELY (read <= 0) || buf == NULL) {
1032 goto change_progchain;
1037 if ( GST_FLOW_OK != ( ret = gst_pad_push ( src->sinkpad, buf ) ) ) {
1045 const gchar *reason = gst_flow_get_name (ret);
1047 GST_DEBUG_OBJECT (src, "DONE task, reason %s", reason);
1052 const gchar *reason = gst_flow_get_name (ret);
1054 GST_DEBUG_OBJECT (src, "pausing task, reason %s", reason);
1055 return GST_FLOW_UNEXPECTED;
1060 GST_ELEMENT_ERROR (src, RESOURCE, READ,
1061 (NULL), ("Could not read any bytes (%i, %s)", read,
1063 return GST_FLOW_ERROR;
1067 GST_ELEMENT_ERROR (src, RESOURCE, READ,
1068 (NULL), ("Seek failed, go to the next program info... (%i, %s)", read,
1070 // go to the next program chain
1071 src->unique_setup = FALSE;
1072 src->update_prog_chain = TRUE;
1073 src->mode = GST_MYTHTV_SRC_NEXT_PROGRAM_CHAIN;
1075 //GST_OBJECT_LOCK(src);
1076 //gst_mythtv_src_next_program_chain( src );
1077 //GST_OBJECT_UNLOCK(src);
1078 return GST_FLOW_OK;//GST_FLOW_ERROR_NO_DATA;
1082 static GstFlowReturn
1083 gst_mythtv_src_play (GstPad * pad)
1085 GstFlowReturn res = GST_FLOW_OK;
1086 GstMythtvSrc *src = GST_MYTHTV_SRC (GST_PAD_PARENT (pad));
1088 switch ( src->state ) {
1089 case GST_MYTHTV_SRC_FILE_TRANSFER:
1090 res = gst_mythtv_src_file_transfer( src );
1091 if ((res != GST_FLOW_OK) && (res != GST_FLOW_ERROR_NO_DATA)) {
1095 case GST_MYTHTV_SRC_NEXT_PROGRAM_CHAIN:
1096 src->read_offset = 0;
1097 src->bytes_read = 0;
1098 src->unique_setup = FALSE;
1099 if ( !gst_mythtv_src_next_program_chain( src ) ) {
1102 src->state = GST_MYTHTV_SRC_FILE_TRANSFER;
1104 case GST_MYTHTV_SRC_INVALID_DATA:
1108 g_assert_not_reached ();
1111 GST_DEBUG_OBJECT (src, "state: %d res:%s", src->state,
1112 gst_flow_get_name (res));
1117 GST_LOG_OBJECT (src, "pausing task, reason %s", gst_flow_get_name (res));
1118 gst_pad_pause_task (src->srcpad);
1119 if (GST_FLOW_IS_FATAL (res)) {
1120 GST_ELEMENT_ERROR (src, STREAM, FAILED,
1121 ("Internal data stream error."),
1122 ("streaming stopped, reason %s", gst_flow_get_name (res)));
1123 gst_pad_send_event( src->sinkpad, gst_event_new_eos() );
1129 static GstStateChangeReturn
1130 gst_mythtv_src_change_state (GstElement * element, GstStateChange transition)
1132 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; //GST_STATE_CHANGE_NO_PREROLL;
1133 GstMythtvSrc *src = GST_MYTHTV_SRC (element);
1135 switch (transition) {
1136 case GST_STATE_CHANGE_NULL_TO_READY:
1137 src->do_start = TRUE;
1138 src->unique_setup = FALSE;
1140 case GST_STATE_CHANGE_READY_TO_PAUSED:
1141 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1148 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1149 if (ret == GST_STATE_CHANGE_FAILURE)
1152 switch (transition) {
1153 case GST_STATE_CHANGE_READY_TO_NULL:
1154 g_print( "[%s] READY to NULL called!\n", __FUNCTION__ );
1155 if ( src->live_tv && src->update_prog_chain ) {
1156 gst_pad_push_event ( src->sinkpad,
1157 gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES, 0, src->content_size, 0 ) );
1158 gst_element_set_state ( GST_ELEMENT (src), GST_STATE_READY );
1161 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1162 g_print( "[%s] PLAYING to PAUSED called!\n", __FUNCTION__ );
1163 case GST_STATE_CHANGE_PAUSED_TO_READY:
1164 g_print( "[%s] PAUSED to READY called!\n", __FUNCTION__ );
1165 if ( src->live_tv && src->update_prog_chain ) {
1166 src->read_offset = 0;
1167 src->bytes_read = 0;
1168 src->unique_setup = FALSE;
1169 //GST_OBJECT_LOCK( src );
1170 gst_mythtv_src_next_program_chain( src );
1171 //GST_OBJECT_UNLOCK( src );
1182 gst_mythtv_src_set_property (GObject * object, guint prop_id,
1183 const GValue * value, GParamSpec * pspec)
1185 GstMythtvSrc *mythtvsrc = GST_MYTHTV_SRC (object);
1187 GST_OBJECT_LOCK (mythtvsrc);
1192 if (!g_value_get_string (value)) {
1193 GST_WARNING ("location property cannot be NULL");
1197 if (mythtvsrc->uri_name != NULL) {
1198 g_free (mythtvsrc->uri_name);
1199 mythtvsrc->uri_name = NULL;
1201 mythtvsrc->uri_name = g_value_dup_string (value);
1205 #ifndef GST_DISABLE_GST_DEBUG
1206 case PROP_GMYTHTV_DBG:
1208 mythtvsrc->mythtv_msgs_dbg = g_value_get_boolean (value);
1212 case PROP_GMYTHTV_VERSION:
1214 mythtvsrc->mythtv_version = g_value_get_int (value);
1217 case PROP_GMYTHTV_LIVEID:
1219 mythtvsrc->live_tv_id = g_value_get_int (value);
1222 case PROP_GMYTHTV_LIVE:
1224 mythtvsrc->live_tv = g_value_get_boolean (value);
1227 case PROP_GMYTHTV_ENABLE_TIMING_POSITION:
1229 mythtvsrc->enable_timing_position = g_value_get_boolean (value);
1232 case PROP_GMYTHTV_LIVE_CHAINID:
1234 if (!g_value_get_string (value)) {
1235 GST_WARNING ("MythTV Live chainid property cannot be NULL");
1239 if (mythtvsrc->live_chain_id != NULL) {
1240 g_free (mythtvsrc->live_chain_id);
1241 mythtvsrc->live_chain_id = NULL;
1243 mythtvsrc->live_chain_id = g_value_dup_string (value);
1246 case PROP_GMYTHTV_CHANNEL_NUM:
1248 mythtvsrc->channel_num = g_value_get_int (value);
1252 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1255 GST_OBJECT_UNLOCK (mythtvsrc);
1261 gst_mythtv_src_get_property (GObject * object, guint prop_id,
1262 GValue * value, GParamSpec * pspec)
1264 GstMythtvSrc *mythtvsrc = GST_MYTHTV_SRC (object);
1266 GST_OBJECT_LOCK (mythtvsrc);
1271 gchar *str = g_strdup( "" );
1273 if ( mythtvsrc->uri_name == NULL ) {
1274 g_free (mythtvsrc->uri_name);
1275 mythtvsrc->uri_name = NULL;
1277 str = g_strdup( mythtvsrc->uri_name );
1279 g_value_set_string ( value, str );
1282 #ifndef GST_DISABLE_GST_DEBUG
1283 case PROP_GMYTHTV_DBG:
1284 g_value_set_boolean ( value, mythtvsrc->mythtv_msgs_dbg );
1287 case PROP_GMYTHTV_VERSION:
1289 g_value_set_int ( value, mythtvsrc->mythtv_version );
1292 case PROP_GMYTHTV_LIVEID:
1294 g_value_set_int ( value, mythtvsrc->live_tv_id );
1297 case PROP_GMYTHTV_LIVE:
1298 g_value_set_boolean ( value, mythtvsrc->live_tv );
1300 case PROP_GMYTHTV_ENABLE_TIMING_POSITION:
1301 g_value_set_boolean ( value, mythtvsrc->enable_timing_position );
1303 case PROP_GMYTHTV_LIVE_CHAINID:
1305 gchar *str = g_strdup( "" );
1307 if ( mythtvsrc->live_chain_id == NULL ) {
1308 g_free (mythtvsrc->live_chain_id);
1309 mythtvsrc->live_chain_id = NULL;
1311 str = g_strdup( mythtvsrc->live_chain_id );
1313 g_value_set_string ( value, str );
1316 case PROP_GMYTHTV_CHANNEL_NUM:
1318 g_value_set_int ( value, mythtvsrc->channel_num );
1322 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1325 GST_OBJECT_UNLOCK (mythtvsrc);
1328 /* entry point to initialize the plug-in
1329 * initialize the plug-in itself
1330 * register the element factories and pad templates
1331 * register the features
1334 plugin_init (GstPlugin * plugin)
1336 return gst_element_register (plugin, "mythtvsrc", GST_RANK_NONE,
1337 GST_TYPE_MYTHTV_SRC);
1340 /* this is the structure that gst-register looks for
1341 * so keep the name plugin_desc, or you cannot get your plug-in registered */
1342 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1346 plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")
1349 /*** GSTURIHANDLER INTERFACE *************************************************/
1351 gst_mythtv_src_uri_get_type (void)
1357 gst_mythtv_src_uri_get_protocols (void)
1359 static gchar *protocols[] = { "myth", "myths", NULL };
1364 static const gchar *
1365 gst_mythtv_src_uri_get_uri (GstURIHandler * handler)
1367 GstMythtvSrc *src = GST_MYTHTV_SRC (handler);
1369 return src->uri_name;
1373 gst_mythtv_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
1375 GstMythtvSrc *src = GST_MYTHTV_SRC (handler);
1379 protocol = gst_uri_get_protocol (uri);
1380 if ((strcmp (protocol, "myth") != 0) && (strcmp (protocol, "myths") != 0)) {
1385 g_object_set (src, "location", uri, NULL);
1391 gst_mythtv_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
1393 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
1395 iface->get_type = gst_mythtv_src_uri_get_type;
1396 iface->get_protocols = gst_mythtv_src_uri_get_protocols;
1397 iface->get_uri = gst_mythtv_src_uri_get_uri;
1398 iface->set_uri = gst_mythtv_src_uri_set_uri;
1402 size_header_handler (void *userdata, const char *value)
1404 GstMythtvSrc *src = GST_MYTHTV_SRC (userdata);
1406 //src->content_size = g_ascii_strtoull (value, NULL, 10);
1408 GST_DEBUG_OBJECT (src, "content size = %lld bytes", src->content_size);