4 * @file gmyth/gmyth_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 "gmyth_tvplayer.h"
33 #include "gmyth_context.h"
34 #include "gmyth_remote_util.h"
36 typedef struct _GstPlayerWindowStateChange
39 GstState old_state, new_state;
40 GMythTVPlayer *tvplayer;
41 } GstPlayerWindowStateChange;
43 typedef struct _GstPlayerWindowTagFound
47 GMythTVPlayer *tvplayer;
48 } GstPlayerWindowTagFound;
51 static gboolean idle_state (gpointer data);
53 static gboolean bus_call (GstBus * bus, GstMessage * msg, gpointer data);
55 static void gmyth_tvplayer_class_init (GMythTVPlayerClass *klass);
56 static void gmyth_tvplayer_init (GMythTVPlayer *object);
58 static void gmyth_tvplayer_dispose (GObject *object);
59 static void gmyth_tvplayer_finalize (GObject *object);
61 G_DEFINE_TYPE(GMythTVPlayer, gmyth_tvplayer, G_TYPE_OBJECT)
63 static gboolean gmyth_tvplayer_create_pipeline (GMythTVPlayer* tvplayer);
64 static void new_pad_cb (GstElement *element,
65 GstPad *pad, gpointer data);
67 static gboolean expose_cb (GtkWidget * widget,
68 GdkEventExpose * event,
72 gmyth_tvplayer_class_init (GMythTVPlayerClass *klass)
74 GObjectClass *gobject_class;
76 gobject_class = (GObjectClass *) klass;
78 gobject_class->dispose = gmyth_tvplayer_dispose;
79 gobject_class->finalize = gmyth_tvplayer_finalize;
83 new_pad_cb (GstElement *element, GstPad *pad, gpointer data)
85 GMythTVPlayer *tvplayer = GMYTH_TVPLAYER (data);
89 s = gst_caps_to_string (pad->caps);
92 ret = gst_pad_link (pad, gst_element_get_pad (tvplayer->audioqueue, "sink"));
94 ret = gst_pad_link (pad, gst_element_get_pad (tvplayer->videoqueue, "sink"));
101 expose_cb (GtkWidget * widget, GdkEventExpose * event, gpointer user_data)
103 GMythTVPlayer *tvplayer = GMYTH_TVPLAYER (user_data);
105 if (tvplayer && tvplayer->videow) {
106 gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (tvplayer->gst_videosink),
107 GDK_WINDOW_XWINDOW (widget->window));
111 g_warning ("GMythTVPlayer expose called before setting video window\n");
117 gmyth_tvplayer_init (GMythTVPlayer *tvplayer)
119 tvplayer->gst_pipeline = NULL;
120 tvplayer->gst_source = NULL;
121 tvplayer->gst_videodec = NULL;
122 tvplayer->gst_videosink = NULL;
123 tvplayer->videoqueue = NULL;
124 tvplayer->audioqueue = NULL;
126 /* GTKWidget for rendering the video */
127 tvplayer->videow = NULL;
128 tvplayer->expose_handler = 0;
130 tvplayer->backend_hostname = NULL;
131 tvplayer->backend_port = 0;
132 tvplayer->local_hostname = NULL;
134 tvplayer->remote_encoder = NULL;
135 tvplayer->tvchain = NULL;
136 tvplayer->proginfo = NULL;
140 gmyth_tvplayer_dispose (GObject *object)
143 G_OBJECT_CLASS (gmyth_tvplayer_parent_class)->dispose (object);
147 gmyth_tvplayer_finalize (GObject *object)
149 g_signal_handlers_destroy (object);
151 GMythTVPlayer *tvplayer = GMYTH_TVPLAYER (object);
153 g_debug ("[%s] Finalizing tvplayer", __FUNCTION__);
155 if (tvplayer->videow != NULL) {
156 if (g_signal_handler_is_connected (tvplayer->videow,
157 tvplayer->expose_handler)) {
158 g_signal_handler_disconnect (tvplayer->videow,
159 tvplayer->expose_handler);
161 g_object_unref (tvplayer->videow);
164 if ( tvplayer->remote_encoder != NULL )
165 g_object_unref (tvplayer->remote_encoder);
166 if ( tvplayer->tvchain != NULL )
167 g_object_unref (tvplayer->tvchain);
168 if ( tvplayer->proginfo != NULL )
169 g_object_unref (tvplayer->proginfo);
171 // Release Gstreamer elements
172 if ( tvplayer->gst_pipeline != NULL )
173 g_object_unref (tvplayer->gst_pipeline);
174 if ( tvplayer->gst_source != NULL )
175 g_object_unref (tvplayer->gst_source);
176 if ( tvplayer->gst_videodec != NULL )
177 g_object_unref (tvplayer->gst_videodec);
178 if ( tvplayer->gst_videosink != NULL )
179 g_object_unref (tvplayer->gst_videosink);
180 if ( tvplayer->videoqueue != NULL )
181 g_object_unref (tvplayer->videoqueue);
182 if ( tvplayer->audioqueue != NULL )
183 g_object_unref (tvplayer->audioqueue);
185 G_OBJECT_CLASS (gmyth_tvplayer_parent_class)->finalize (object);
188 /** Creates a new instance of GMythTVPlayer.
190 * @return a new instance of GMythTVPlayer.
193 gmyth_tvplayer_new ()
195 GMythTVPlayer *tvplayer =
196 GMYTH_TVPLAYER (g_object_new(GMYTH_TVPLAYER_TYPE, NULL));
201 /** Initializes the tv player.
203 * @param tvplayer the object instance.
204 * @return gboolean TRUE if the pipeline was created
205 * successfully, FALSE otherwise.
208 gmyth_tvplayer_initialize (GMythTVPlayer *tvplayer)
211 if (!gmyth_tvplayer_create_pipeline (tvplayer)) {
212 g_warning ("[%s] Error while creating pipeline. TV Player not initialized", __FUNCTION__);
215 g_debug ("[%s] GStreamer pipeline created", __FUNCTION__);
221 /** Creates the GStreamer pipeline used by the player.
223 * @param tvplayer the object instance.
224 * @return gboolean TRUE if the pipeline was created
225 * successfully, FALSE otherwise.
228 gmyth_tvplayer_create_pipeline (GMythTVPlayer* tvplayer)
230 GstElement *pipeline;
231 GstElement *source, *parser;
232 GstElement *videodec, *videosink;
233 #ifndef MAEMO_PLATFORM
234 GstElement *audiodec, *audioconv;
236 GstElement *audiosink;
237 GstElement *videoqueue, *audioqueue;
239 g_debug ("GMythTVPlayer: Setting the Gstreamer pipeline\n");
241 pipeline = gst_pipeline_new ("video-player");
242 source = gst_element_factory_make ("mythtvsrc", "myth-source");
243 parser = gst_element_factory_make ("ffdemux_nuv", "nuv-demux");
245 /* Gstreamer Video elements */
246 videoqueue = gst_element_factory_make ("queue", "video-queue");
247 videodec = gst_element_factory_make ("ffdec_mpeg4", "video-decoder");
248 #ifdef MAEMO_PLATFORM
249 videosink = gst_element_factory_make ("sdlvideosink", "image-output");
251 videosink = gst_element_factory_make ("xvimagesink", "image-output");
254 /* Gstreamer Audio elements */
255 audioqueue = gst_element_factory_make ("queue", "audio-queue");
256 #ifdef MAEMO_PLATFORM
257 audiosink = gst_element_factory_make ("dspmp3sink", "audio-output");
259 audiodec = gst_element_factory_make ("ffdec_mp3", "audio-decoder");
260 audioconv = gst_element_factory_make ("audioconvert", "audio-converter");
261 audiosink = gst_element_factory_make ("alsasink", "audio-output");
264 if (!(pipeline && source && parser && videodec && videosink) ||
265 !(videoqueue && audioqueue && audiosink)) {
266 /* FIXME: hanlde the error correctly */
267 /* video_alignment is not being created (below)
268 and is causing problems to the ui */
270 tvplayer->gst_pipeline = NULL;
271 tvplayer->gst_videodec = NULL;
272 tvplayer->gst_videosink = NULL;
274 g_warning ("GstElement creation error!\n");
278 #ifndef MAEMO_PLATFORM
279 if (!(audiodec && audioconv)) {
280 g_warning ("GstElement for audio stream creation error!");
286 tvplayer->gst_pipeline = pipeline;
287 tvplayer->gst_source = source;
288 tvplayer->gst_videodec = videodec;
289 tvplayer->gst_videosink = videosink;
290 g_object_ref (tvplayer->gst_pipeline);
291 g_object_ref (tvplayer->gst_source);
292 g_object_ref (tvplayer->gst_videodec);
293 g_object_ref (tvplayer->gst_videosink);
295 tvplayer->videoqueue = videoqueue;
296 tvplayer->audioqueue = audioqueue;
297 g_object_ref (tvplayer->videoqueue);
298 g_object_ref (tvplayer->audioqueue);
300 g_object_set (G_OBJECT (videosink), "sync", TRUE, NULL);
301 g_object_set (G_OBJECT (audiosink), "sync", FALSE, NULL);
303 gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (tvplayer->gst_pipeline)),
306 gst_bin_add_many (GST_BIN (pipeline), source, parser, videoqueue,
307 videodec, videosink, audioqueue, audiodec, audioconv, audiosink, NULL);
310 // GstCaps *rtpcaps = gst_caps_new_simple ("application/x-rtp", NULL);
311 // gst_element_link_filtered(source, parser, rtpcaps);
314 gst_element_link (source, parser);
315 gst_element_link_many (videoqueue, videodec, videosink, NULL);
316 gst_element_link_many (audioqueue, audiodec, audioconv, audiosink, NULL);
318 g_signal_connect (parser, "pad-added", G_CALLBACK (new_pad_cb), tvplayer);
323 /** Configures the backend and the tv player
324 * for playing the recorded content A/V.
326 * FIXME: Change filename to program info or other structure about the recorded
328 * @param tvplayer the object instance.
329 * @param filename the file uri of the recorded content to be played.
330 * @return TRUE if successfull, FALSE if any error happens.
333 gmyth_tvplayer_record_setup (GMythTVPlayer *tvplayer, gchar *filename)
335 GMythSettings *msettings = gmyth_context_get_settings();
337 // FIXME: we should receive the uri instead of filename
338 GString *hostname = gmyth_settings_get_backend_hostname (msettings);
339 int port = gmyth_settings_get_backend_port(msettings);
341 GString *fullpath = g_string_new ("myth://");
342 g_string_append_printf (fullpath, "%s:%d/%s", hostname->str, port, filename);
344 tvplayer->is_livetv = FALSE;
346 g_debug ("[%s] Setting record uri to gstreamer pipeline to %s", __FUNCTION__, fullpath->str);
348 g_object_set (G_OBJECT (tvplayer->gst_source), "location",
349 fullpath->str, NULL);
354 /** Configures the backend and the tv player
355 * for playing the live tv.
357 * @param tvplayer the object instance.
358 * @return TRUE if successfull, FALSE if any error happens.
361 gmyth_tvplayer_livetv_setup (GMythTVPlayer *tvplayer)
363 GMythSettings *msettings = gmyth_context_get_settings ();
366 res = gmyth_context_check_connection();
368 g_warning ("[%s] LiveTV can not connect to backend", __FUNCTION__);
373 tvplayer->backend_hostname = gmyth_settings_get_backend_hostname(msettings);
374 tvplayer->backend_port = gmyth_settings_get_backend_port (msettings);
376 tvplayer->local_hostname = g_string_new("");
377 gmyth_context_get_local_hostname (tvplayer->local_hostname);
379 if ( tvplayer->local_hostname == NULL ) {
384 // Gets the remote encoder num
385 tvplayer->remote_encoder = remote_request_next_free_recorder (-1);
387 if ( tvplayer->remote_encoder == NULL ) {
388 g_warning ("[%s] None remote encoder available", __FUNCTION__);
393 // Creates livetv chain handler
394 tvplayer->tvchain = GMYTH_TVCHAIN ( g_object_new(GMYTH_TVCHAIN_TYPE, NULL) );
395 gmyth_tvchain_initialize ( tvplayer->tvchain, tvplayer->local_hostname );
397 if ( tvplayer->tvchain == NULL || tvplayer->tvchain->tvchain_id == NULL ) {
402 // Init remote encoder. Opens its control socket.
403 res = gmyth_remote_encoder_setup(tvplayer->remote_encoder);
405 g_warning ("[%s] Fail while setting remote encoder\n", __FUNCTION__);
409 // Spawn live tv. Uses the socket to send mythprotocol data to start livetv in the backend (remotelly)
410 res = gmyth_remote_encoder_spawntv ( tvplayer->remote_encoder,
411 gmyth_tvchain_get_id(tvplayer->tvchain) );
413 g_warning ("[%s] Fail while spawn tv\n", __FUNCTION__);
418 // Reload all TV chain from Mysql database.
419 gmyth_tvchain_reload_all (tvplayer->tvchain);
421 if ( tvplayer->tvchain == NULL ) {
426 // Get program info from database using chanid and starttime
427 tvplayer->proginfo = gmyth_tvchain_get_program_at (tvplayer->tvchain, -1);
428 if ( tvplayer->proginfo == NULL ) {
429 g_warning ("[%s] LiveTV not successfully started.\n", __FUNCTION__ );
433 g_debug ("[%s] MythLiveTV: All requests to backend to start TV were OK.\n", __FUNCTION__ );
439 if ( tvplayer->backend_hostname != NULL ) {
440 g_string_free( tvplayer->backend_hostname, TRUE );
444 if ( tvplayer->local_hostname != NULL ) {
445 g_string_free( tvplayer->local_hostname, TRUE );
449 if ( tvplayer->remote_encoder != NULL ) {
450 g_object_unref (tvplayer->remote_encoder);
451 tvplayer->remote_encoder = NULL;
454 if ( tvplayer->tvchain != NULL ) {
455 g_object_unref (tvplayer->tvchain);
456 tvplayer->tvchain = NULL;
459 if ( tvplayer->proginfo != NULL ) {
460 g_object_unref (tvplayer->proginfo);
461 tvplayer->proginfo = NULL;
468 /** Sets the GTK video widget for the tv player.
470 * @param tvplayer the object instance.
471 * @param videow the GTK video window.
472 * @return TRUE if successfull, FALSE if any error happens.
475 gmyth_tvplayer_set_widget (GMythTVPlayer *tvplayer, GtkWidget *videow)
477 tvplayer->videow = videow;
478 g_object_ref (videow);
480 g_debug ("[%s] Setting widget for tv player render", __FUNCTION__);
482 tvplayer->expose_handler = g_signal_connect (tvplayer->videow, "expose-event",
483 G_CALLBACK (expose_cb), tvplayer);
485 //g_signal_connect(miptv_ui->videow, "size_request", G_CALLBACK(cb_preferred_video_size), miptv_ui);
491 bus_call (GstBus * bus, GstMessage * msg, gpointer data)
493 //GMythTVPlayer *tvplayer = GMYTH_TVPLAYER ( data );
494 //GMainLoop *loop = tvplayer->loop;
496 switch (GST_MESSAGE_TYPE (msg)) {
497 case GST_MESSAGE_EOS:
498 printf ("End of stream\n");
499 //g_idle_add ((GSourceFunc) idle_eos, data);
500 gst_element_set_state ( GST_ELEMENT (GST_MESSAGE_SRC (msg)), GST_STATE_NULL );
501 gst_element_set_locked_state ( GST_ELEMENT (GST_MESSAGE_SRC (msg)), FALSE );
503 case GST_MESSAGE_ERROR:
508 gst_message_parse_error (msg, &err, &debug);
511 printf ("Error: %s\n", err->message);
514 //g_main_loop_quit (loop);
518 printf (gst_message_type_get_name (GST_MESSAGE_TYPE (msg)));
529 idle_state (gpointer data)
531 GstPlayerWindowStateChange *st = data;
533 if (st->old_state == GST_STATE_PLAYING) {
534 if (st->miptv_ui->idle_id != 0) {
535 g_source_remove (st->miptv_ui->idle_id);
536 st->miptv_ui->idle_id = 0;
539 else if (st->new_state == GST_STATE_PLAYING) {
540 if (st->miptv_ui->idle_id != 0)
541 g_source_remove (st->miptv_ui->idle_id);
543 st->miptv_ui->idle_id = g_idle_add (cb_iterate, st->miptv_ui);
546 /* new movie loaded? */
547 if (st->old_state == GST_STATE_READY && st->new_state > GST_STATE_READY) {
549 /* gboolean have_video = FALSE; */
551 gtk_widget_show (st->miptv_ui->videow);
553 gtk_window_resize (GTK_WINDOW (st->miptv_ui->main_window), 1, 1);
557 /* discarded movie? */
558 if (st->old_state > GST_STATE_READY && st->new_state == GST_STATE_READY) {
560 if (st->miptv_ui->tagcache) {
561 gst_tag_list_free (st->miptv_ui->tagcache);
562 st->miptv_ui->tagcache = NULL;
566 gst_object_unref (GST_OBJECT (st->play));
567 //g_object_unref (G_OBJECT (st->win));
576 /** Stops playing the current A/V.
578 * FIXME: How to proceed differently between livetv
579 * and recorded content?
581 * @param tvplayer the object instance.
585 gmyth_tvplayer_stop_playing (GMythTVPlayer *tvplayer)
587 g_debug ("[%s] Setting gstreamer pipeline state to NULL", __FUNCTION__);
589 gst_element_set_state (tvplayer->gst_pipeline, GST_STATE_NULL);
591 if (tvplayer->is_livetv) {
592 if (!gmyth_remote_encoder_stop_livetv (tvplayer->remote_encoder)) {
593 g_warning ("[%s] Error while stoping remote encoder", __FUNCTION__);
598 /** Queries if the tvplayer is active playing A/V content.
600 * @param tvplayer the object instance.
601 * @return TRUE if the tvplayer is active, FALSE otherwise.
604 gmyth_tvplayer_is_playing (GMythTVPlayer *tvplayer)
606 return (GST_STATE (tvplayer->gst_pipeline) == GST_STATE_PLAYING);
609 /** Static function that sets the tvplayer state to PLAYING.
611 * @param tvplayer the object instance.
612 * @return TRUE if the tvplayer is active, FALSE otherwise.
615 idle_play (gpointer data)
617 GMythTVPlayer *tvplayer = GMYTH_TVPLAYER (data);
619 g_debug ("GMythTVPlayer: Setting pipeline state to PLAYING\n");
621 gst_element_set_state (tvplayer->gst_pipeline, GST_STATE_PLAYING);
626 /** Start playing A/V with the tvplayer attributes.
628 * @param tvplayer the object instance.
631 gmyth_tvplayer_start_playing (GMythTVPlayer *tvplayer)
634 // FIXME: Move this to livetv_setup??
635 if (tvplayer->is_livetv) {
638 if (!tvplayer || !(tvplayer->proginfo) || !(tvplayer->local_hostname)
639 || !(tvplayer->gst_source)) {
640 g_warning ("GMythtvPlayer not ready to start playing\n");
643 if (!(tvplayer->proginfo->pathname)) {
644 g_warning ("[%s] Playback url is null, could not play the myth content", __FUNCTION__);
648 g_debug ("GMythTVPlayer: Start playing %s", tvplayer->proginfo->pathname->str);
650 g_object_set (G_OBJECT (tvplayer->gst_source), "mythtv-live",
653 if ( tvplayer->tvchain != NULL ) {
654 GString *str_chainid = gmyth_tvchain_get_id(tvplayer->tvchain);
655 g_print( "[%s]\tCHAIN ID: %s\n", __FUNCTION__, str_chainid->str );
657 g_object_set (G_OBJECT (tvplayer->gst_source), "mythtv-live-chainid",
658 g_strdup( str_chainid->str ), NULL);
659 if ( str_chainid!=NULL)
660 g_string_free( str_chainid, FALSE );
663 if ( tvplayer->remote_encoder != NULL )
664 g_object_set (G_OBJECT (tvplayer->gst_source), "mythtv-live-id",
665 tvplayer->remote_encoder->recorder_num, NULL );
666 g_debug ("[%s] Setting location to %s", __FUNCTION__,
667 tvplayer->proginfo->pathname->str);
669 /* Sets the gstreamer properties acording to the service access address */
670 g_object_set (G_OBJECT (tvplayer->gst_source), "location",
671 tvplayer->proginfo->pathname->str, NULL);
675 g_object_set (G_OBJECT (tvplayer->gst_source), "mythtv-version",
676 MYTHTV_VERSION_DEFAULT, NULL);
678 g_object_set (G_OBJECT (tvplayer->gst_source), "mythtv-debug",
681 g_idle_add (idle_play, tvplayer);