[svn r656] Added gmyth_ls application to list recorded programs and livetv channels
4 * @file gmyth/mmyth_tvplayer.c
6 * @brief <p> This component provides playback of the remote A/V using
9 * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
10 * @author Hallyson Luiz de Morais Melo <hallyson.melo@indt.org.br>
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "mmyth_tvplayer.h"
33 #include <gmyth/gmyth_remote_util.h>
35 #define MYTHTV_VERSION_DEFAULT 30
37 typedef struct _GstPlayerWindowStateChange
40 GstState old_state, new_state;
41 MMythTVPlayer *tvplayer;
42 } GstPlayerWindowStateChange;
44 typedef struct _GstPlayerWindowTagFound
48 MMythTVPlayer *tvplayer;
49 } GstPlayerWindowTagFound;
52 static gboolean idle_state (gpointer data);
54 static gboolean bus_call (GstBus * bus, GstMessage * msg, gpointer data);
56 static void mmyth_tvplayer_class_init (MMythTVPlayerClass *klass);
57 static void mmyth_tvplayer_init (MMythTVPlayer *object);
59 static void mmyth_tvplayer_dispose (GObject *object);
60 static void mmyth_tvplayer_finalize (GObject *object);
62 G_DEFINE_TYPE(MMythTVPlayer, mmyth_tvplayer, G_TYPE_OBJECT)
64 static gboolean mmyth_tvplayer_create_pipeline (MMythTVPlayer* tvplayer);
65 static void new_pad_cb (GstElement *element,
66 GstPad *pad, gpointer data);
68 static gboolean expose_cb (GtkWidget * widget,
69 GdkEventExpose * event,
73 mmyth_tvplayer_class_init (MMythTVPlayerClass *klass)
75 GObjectClass *gobject_class;
77 gobject_class = (GObjectClass *) klass;
79 gobject_class->dispose = mmyth_tvplayer_dispose;
80 gobject_class->finalize = mmyth_tvplayer_finalize;
84 new_pad_cb (GstElement *element, GstPad *pad, gpointer data)
86 MMythTVPlayer *tvplayer = MMYTH_TVPLAYER (data);
90 s = gst_caps_to_string (pad->caps);
93 ret = gst_pad_link (pad, gst_element_get_pad (tvplayer->audioqueue1, "sink"));
95 ret = gst_pad_link (pad, gst_element_get_pad (tvplayer->videoqueue1, "sink"));
102 expose_cb (GtkWidget * widget, GdkEventExpose * event, gpointer user_data)
104 MMythTVPlayer *tvplayer = MMYTH_TVPLAYER (user_data);
106 if (tvplayer && tvplayer->videow) {
107 gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (tvplayer->gst_videosink),
108 GDK_WINDOW_XWINDOW (widget->window));
112 g_warning ("MMythTVPlayer expose called before setting video window\n");
118 mmyth_tvplayer_init (MMythTVPlayer *tvplayer)
120 tvplayer->gst_pipeline = NULL;
121 tvplayer->gst_source = NULL;
122 tvplayer->gst_videodec = NULL;
123 tvplayer->gst_videosink = NULL;
124 tvplayer->gst_videocolortrs = NULL;
125 tvplayer->videoqueue1 = NULL;
126 tvplayer->videoqueue2 = NULL;
127 tvplayer->audioqueue1 = NULL;
128 tvplayer->audioqueue2 = NULL;
130 /* GTKWidget for rendering the video */
131 tvplayer->videow = NULL;
132 tvplayer->expose_handler = 0;
134 tvplayer->backend_hostname = NULL;
135 tvplayer->backend_port = 0;
136 tvplayer->local_hostname = NULL;
138 tvplayer->recorder = NULL;
139 tvplayer->tvchain = NULL;
140 tvplayer->proginfo = NULL;
144 mmyth_tvplayer_dispose (GObject *object)
147 G_OBJECT_CLASS (mmyth_tvplayer_parent_class)->dispose (object);
151 mmyth_tvplayer_finalize (GObject *object)
153 g_signal_handlers_destroy (object);
155 MMythTVPlayer *tvplayer = MMYTH_TVPLAYER (object);
157 g_debug ("[%s] Finalizing tvplayer", __FUNCTION__);
159 if (tvplayer->videow != NULL) {
160 if (g_signal_handler_is_connected (tvplayer->videow,
161 tvplayer->expose_handler)) {
162 g_signal_handler_disconnect (tvplayer->videow,
163 tvplayer->expose_handler);
165 g_object_unref (tvplayer->videow);
168 if ( tvplayer->recorder != NULL )
169 g_object_unref (tvplayer->recorder);
170 if ( tvplayer->tvchain != NULL )
171 g_object_unref (tvplayer->tvchain);
172 if ( tvplayer->proginfo != NULL )
173 g_object_unref (tvplayer->proginfo);
175 // Release Gstreamer elements
176 if ( tvplayer->gst_pipeline != NULL )
177 g_object_unref (tvplayer->gst_pipeline);
178 if ( tvplayer->gst_source != NULL )
179 g_object_unref (tvplayer->gst_source);
180 if ( tvplayer->gst_videodec != NULL )
181 g_object_unref (tvplayer->gst_videodec);
182 if ( tvplayer->gst_videocolortrs != NULL )
183 g_object_unref (tvplayer->gst_videocolortrs);
184 if ( tvplayer->gst_videosink != NULL )
185 g_object_unref (tvplayer->gst_videosink);
186 if ( tvplayer->videoqueue1 != NULL )
187 g_object_unref (tvplayer->videoqueue1);
188 if ( tvplayer->videoqueue2 != NULL )
189 g_object_unref (tvplayer->videoqueue2);
190 if ( tvplayer->audioqueue1 != NULL )
191 g_object_unref (tvplayer->audioqueue1);
192 if ( tvplayer->audioqueue2 != NULL )
193 g_object_unref (tvplayer->audioqueue2);
195 G_OBJECT_CLASS (mmyth_tvplayer_parent_class)->finalize (object);
198 /** Creates a new instance of MMythTVPlayer.
200 * @return a new instance of MMythTVPlayer.
203 mmyth_tvplayer_new ()
205 MMythTVPlayer *tvplayer =
206 MMYTH_TVPLAYER (g_object_new(MMYTH_TVPLAYER_TYPE, NULL));
211 /** Initializes the tv player.
213 * @param tvplayer the object instance.
214 * @return gboolean TRUE if the pipeline was created
215 * successfully, FALSE otherwise.
218 mmyth_tvplayer_initialize (MMythTVPlayer *tvplayer, GMythBackendInfo *backend_info)
220 tvplayer->backend_info = backend_info;
222 if (!mmyth_tvplayer_create_pipeline (tvplayer)) {
223 g_warning ("[%s] Error while creating pipeline. TV Player not initialized", __FUNCTION__);
226 g_debug ("[%s] GStreamer pipeline created", __FUNCTION__);
232 /** Creates the GStreamer pipeline used by the player.
234 * @param tvplayer the object instance.
235 * @return gboolean TRUE if the pipeline was created
236 * successfully, FALSE otherwise.
239 mmyth_tvplayer_create_pipeline (MMythTVPlayer* tvplayer)
241 GstElement *pipeline;
242 GstElement *source, *parser;
243 GstElement *videodec, *videosink;
244 GstElement *videocolortrs;
245 #ifndef MAEMO_PLATFORM
246 GstElement *audiodec, *audioconv, *audioqueue2;
248 GstElement *audiosink;
249 GstElement *videoqueue1, *videoqueue2, *audioqueue1;
251 g_debug ("MMythTVPlayer: Setting the Gstreamer pipeline\n");
253 pipeline = gst_pipeline_new ("video-player");
254 source = gst_element_factory_make ("mythtvsrc", "myth-source");
255 parser = gst_element_factory_make ("nuvdemux", "nuv-demux");
257 /* Gstreamer Video elements */
258 videoqueue1 = gst_element_factory_make ("queue", "video-queue1");
259 videodec = gst_element_factory_make ("ffdec_mpeg4", "video-decoder");
260 videoqueue2 = gst_element_factory_make ("queue", "video-queue2");
261 videocolortrs = gst_element_factory_make ("ffmpegcolorspace", "image-color-transforms");
263 #ifdef MAEMO_PLATFORM
264 videosink = gst_element_factory_make ("sdlvideosink", "image-output");
266 videosink = gst_element_factory_make ("xvimagesink", "image-output");
269 /* Gstreamer Audio elements */
270 audioqueue1 = gst_element_factory_make ("queue", "audio-queue1");
271 #ifdef MAEMO_PLATFORM
272 audiosink = gst_element_factory_make ("dspmp3sink", "audio-output");
274 audioqueue2 = gst_element_factory_make ("queue", "audio-queue2");
275 audiodec = gst_element_factory_make ("mad", "audio-decoder");
276 audioconv = gst_element_factory_make ("audioconvert", "audio-converter");
277 audiosink = gst_element_factory_make ("alsasink", "audio-output");
280 if (!(pipeline && source && parser && videodec && videosink) ||
281 !(videoqueue1 && videoqueue2 && audioqueue1 && audiosink)) {
282 /* FIXME: hanlde the error correctly */
283 /* video_alignment is not being created (below)
284 and is causing problems to the ui */
286 tvplayer->gst_pipeline = NULL;
287 tvplayer->gst_videodec = NULL;
288 tvplayer->gst_videosink = NULL;
289 tvplayer->gst_videocolortrs = NULL;
291 g_warning ("GstElement creation error!\n");
295 #ifndef MAEMO_PLATFORM
296 if (!(audiodec && audioconv)) {
297 g_warning ("GstElement for audio stream creation error!");
302 tvplayer->gst_pipeline = pipeline;
303 tvplayer->gst_source = source;
304 tvplayer->gst_videodec = videodec;
305 tvplayer->gst_videosink = videosink;
306 tvplayer->gst_videocolortrs = videocolortrs;
307 g_object_ref (tvplayer->gst_pipeline);
308 g_object_ref (tvplayer->gst_source);
309 g_object_ref (tvplayer->gst_videodec);
310 g_object_ref (tvplayer->gst_videosink);
311 g_object_ref (tvplayer->gst_videocolortrs);
313 tvplayer->videoqueue1 = videoqueue1;
314 tvplayer->videoqueue2 = videoqueue2;
315 tvplayer->audioqueue1 = audioqueue1;
316 g_object_ref (tvplayer->videoqueue1);
317 g_object_ref (tvplayer->videoqueue2);
318 g_object_ref (tvplayer->audioqueue1);
320 #ifndef MAEMO_PLATFORM
321 tvplayer->audioqueue2 = audioqueue2;
322 g_object_ref (tvplayer->audioqueue2);
325 //g_object_set (G_OBJECT (videosink), "sync", TRUE, NULL);
326 g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
328 gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (tvplayer->gst_pipeline)),
331 gst_bin_add_many (GST_BIN (pipeline), source, parser, videoqueue1,
332 videodec, videoqueue2, videocolortrs, videosink, NULL );
334 #ifndef MAEMO_PLATFORM
335 gst_bin_add_many ( GST_BIN(pipeline), audioqueue1, audiodec, audioconv, audioqueue2, audiosink, NULL );
337 gst_bin_add_many ( GST_BIN(pipeline), audioqueue1, audiosink, NULL);
341 // GstCaps *rtpcaps = gst_caps_new_simple ("application/x-rtp", NULL);
342 // gst_element_link_filtered(source, parser, rtpcaps);
345 gst_element_link (source, parser);
346 gst_element_link_many (videoqueue1, videodec, videoqueue2, videocolortrs, videosink, NULL);
348 #ifndef MAEMO_PLATFORM
349 gst_element_link_many (videosink, audioqueue1, audiodec, audioconv, audioqueue2, audiosink, NULL);
351 gst_element_link_many (videosink, audioqueue1, audiosink, NULL);
354 g_signal_connect (parser, "pad-added", G_CALLBACK (new_pad_cb), tvplayer);
359 /** Configures the backend and the tv player
360 * for playing the recorded content A/V.
362 * FIXME: Change filename to program info or other structure about the recorded
364 * @param tvplayer the object instance.
365 * @param filename the file uri of the recorded content to be played.
366 * @return TRUE if successfull, FALSE if any error happens.
369 mmyth_tvplayer_record_setup (MMythTVPlayer *tvplayer, const gchar *filename)
371 // FIXME: we should receive the uri instead of filename
372 const gchar *hostname = gmyth_backend_info_get_hostname (tvplayer->backend_info);
373 const gint port = gmyth_backend_info_get_port(tvplayer->backend_info);
375 GString *fullpath = g_string_new ("myth://");
376 g_string_append_printf (fullpath, "%s:%d/%s", hostname, port, filename);
378 tvplayer->is_livetv = FALSE;
380 g_debug ("[%s] Setting record uri to gstreamer pipeline to %s", __FUNCTION__, fullpath->str);
382 g_object_set (G_OBJECT (tvplayer->gst_source), "location",
383 fullpath->str, NULL);
388 /** Configures the backend and the tv player
389 * for playing the live tv.
391 * @param tvplayer the object instance.
392 * @return TRUE if successfull, FALSE if any error happens.
395 mmyth_tvplayer_livetv_setup (MMythTVPlayer *tvplayer)
399 tvplayer->livetv = gmyth_livetv_new ();
401 if ( !gmyth_livetv_setup( tvplayer->livetv, tvplayer->backend_info ) )
408 if ( tvplayer->livetv != NULL ) {
409 g_object_unref( tvplayer->livetv );
412 if ( tvplayer->local_hostname != NULL ) {
413 g_string_free( tvplayer->local_hostname, TRUE );
416 if ( tvplayer->recorder != NULL ) {
417 g_object_unref (tvplayer->recorder);
418 tvplayer->recorder = NULL;
421 if ( tvplayer->tvchain != NULL ) {
422 g_object_unref (tvplayer->tvchain);
423 tvplayer->tvchain = NULL;
426 if ( tvplayer->proginfo != NULL ) {
427 g_object_unref (tvplayer->proginfo);
428 tvplayer->proginfo = NULL;
435 /** Sets the GTK video widget for the tv player.
437 * @param tvplayer the object instance.
438 * @param videow the GTK video window.
439 * @return TRUE if successfull, FALSE if any error happens.
442 mmyth_tvplayer_set_widget (MMythTVPlayer *tvplayer, GtkWidget *videow)
444 tvplayer->videow = videow;
445 g_object_ref (videow);
447 g_debug ("[%s] Setting widget for tv player render", __FUNCTION__);
449 tvplayer->expose_handler = g_signal_connect (tvplayer->videow, "expose-event",
450 G_CALLBACK (expose_cb), tvplayer);
452 //g_signal_connect(miptv_ui->videow, "size_request", G_CALLBACK(cb_preferred_video_size), miptv_ui);
458 bus_call (GstBus * bus, GstMessage * msg, gpointer data)
460 //MMythTVPlayer *tvplayer = MMYTH_TVPLAYER ( data );
461 //GMainLoop *loop = tvplayer->loop;
463 switch (GST_MESSAGE_TYPE (msg)) {
464 case GST_MESSAGE_EOS:
465 printf ("End of stream\n");
466 //g_idle_add ((GSourceFunc) idle_eos, data);
467 gst_element_set_state ( GST_ELEMENT (GST_MESSAGE_SRC (msg)), GST_STATE_NULL );
468 gst_element_set_locked_state ( GST_ELEMENT (GST_MESSAGE_SRC (msg)), TRUE );
470 case GST_MESSAGE_ERROR:
475 gst_message_parse_error (msg, &err, &debug);
478 printf ("Error: %s\n", err->message);
481 //g_main_loop_quit (loop);
485 printf (gst_message_type_get_name (GST_MESSAGE_TYPE (msg)));
496 idle_state (gpointer data)
498 GstPlayerWindowStateChange *st = data;
500 if (st->old_state == GST_STATE_PLAYING) {
501 if (st->miptv_ui->idle_id != 0) {
502 g_source_remove (st->miptv_ui->idle_id);
503 st->miptv_ui->idle_id = 0;
506 else if (st->new_state == GST_STATE_PLAYING) {
507 if (st->miptv_ui->idle_id != 0)
508 g_source_remove (st->miptv_ui->idle_id);
510 st->miptv_ui->idle_id = g_idle_add (cb_iterate, st->miptv_ui);
513 /* new movie loaded? */
514 if (st->old_state == GST_STATE_READY && st->new_state > GST_STATE_READY) {
516 /* gboolean have_video = FALSE; */
518 gtk_widget_show (st->miptv_ui->videow);
520 gtk_window_resize (GTK_WINDOW (st->miptv_ui->main_window), 1, 1);
524 /* discarded movie? */
525 if (st->old_state > GST_STATE_READY && st->new_state == GST_STATE_READY) {
527 if (st->miptv_ui->tagcache) {
528 gst_tag_list_free (st->miptv_ui->tagcache);
529 st->miptv_ui->tagcache = NULL;
533 gst_object_unref (GST_OBJECT (st->play));
534 //g_object_unref (G_OBJECT (st->win));
543 /** Stops playing the current A/V.
545 * FIXME: How to proceed differently between livetv
546 * and recorded content?
548 * @param tvplayer the object instance.
552 mmyth_tvplayer_stop_playing (MMythTVPlayer *tvplayer)
554 g_debug ("[%s] Setting gstreamer pipeline state to NULL", __FUNCTION__);
556 gst_element_set_state (tvplayer->gst_pipeline, GST_STATE_NULL);
558 if (tvplayer->is_livetv) {
559 if (!gmyth_recorder_stop_livetv (tvplayer->recorder)) {
560 g_warning ("[%s] Error while stoping remote encoder", __FUNCTION__);
565 /** Queries if the tvplayer is active playing A/V content.
567 * @param tvplayer the object instance.
568 * @return TRUE if the tvplayer is active, FALSE otherwise.
571 mmyth_tvplayer_is_playing (MMythTVPlayer *tvplayer)
573 return (GST_STATE (tvplayer->gst_pipeline) == GST_STATE_PLAYING);
576 /** Static function that sets the tvplayer state to PLAYING.
578 * @param tvplayer the object instance.
579 * @return TRUE if the tvplayer is active, FALSE otherwise.
582 idle_play (gpointer data)
584 MMythTVPlayer *tvplayer = MMYTH_TVPLAYER (data);
586 g_debug ("MMythTVPlayer: Setting pipeline state to PLAYING\n");
588 gst_element_set_state (tvplayer->gst_pipeline, GST_STATE_PLAYING);
593 /** Start playing A/V with the tvplayer attributes.
595 * @param tvplayer the object instance.
598 mmyth_tvplayer_start_playing (MMythTVPlayer *tvplayer)
601 // FIXME: Move this to livetv_setup??
602 if (tvplayer->is_livetv) {
605 if (!tvplayer || !(tvplayer->proginfo) || !(tvplayer->local_hostname)
606 || !(tvplayer->gst_source)) {
607 g_warning ("GMythtvPlayer not ready to start playing\n");
610 if (!(tvplayer->proginfo->pathname)) {
611 g_warning ("[%s] Playback url is null, could not play the myth content", __FUNCTION__);
615 g_debug ("MMythTVPlayer: Start playing %s", tvplayer->proginfo->pathname->str);
617 g_object_set (G_OBJECT (tvplayer->gst_source), "mythtv-live",
620 if ( tvplayer->tvchain != NULL ) {
621 GString *str_chainid = gmyth_tvchain_get_id(tvplayer->tvchain);
622 g_print( "[%s]\tCHAIN ID: %s\n", __FUNCTION__, str_chainid->str );
624 g_object_set (G_OBJECT (tvplayer->gst_source), "mythtv-live-chainid",
625 g_strdup( str_chainid->str ), NULL);
626 if ( str_chainid!=NULL)
627 g_string_free( str_chainid, FALSE );
630 if ( tvplayer->recorder != NULL )
631 g_object_set (G_OBJECT (tvplayer->gst_source), "mythtv-live-id",
632 tvplayer->recorder->recorder_num, NULL );
633 g_debug ("[%s] Setting location to %s", __FUNCTION__,
634 tvplayer->proginfo->pathname->str);
636 /* Sets the gstreamer properties acording to the service access address */
637 g_object_set (G_OBJECT (tvplayer->gst_source), "location",
638 tvplayer->proginfo->pathname->str, NULL);
642 g_object_set (G_OBJECT (tvplayer->gst_source), "mythtv-version",
643 MYTHTV_VERSION_DEFAULT, NULL);
645 g_object_set (G_OBJECT (tvplayer->gst_source), "mythtv-debug",
648 g_idle_add (idle_play, tvplayer);