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 {
41 MMythTVPlayer *tvplayer;
42 } GstPlayerWindowStateChange;
44 typedef struct _GstPlayerWindowTagFound {
47 MMythTVPlayer *tvplayer;
48 } GstPlayerWindowTagFound;
51 * static gboolean idle_state (gpointer data);
53 static gboolean bus_call(GstBus * bus, GstMessage * msg, gpointer data);
55 static void mmyth_tvplayer_class_init(MMythTVPlayerClass * klass);
56 static void mmyth_tvplayer_init(MMythTVPlayer * object);
58 static void mmyth_tvplayer_dispose(GObject * object);
59 static void mmyth_tvplayer_finalize(GObject * object);
61 G_DEFINE_TYPE(MMythTVPlayer, mmyth_tvplayer, G_TYPE_OBJECT)
63 static gboolean mmyth_tvplayer_create_pipeline(MMythTVPlayer *
65 static void new_pad_cb(GstElement * element, GstPad * pad,
68 static gboolean expose_cb(GtkWidget * widget,
69 GdkEventExpose * event, gpointer user_data);
72 mmyth_tvplayer_class_init(MMythTVPlayerClass * klass)
74 GObjectClass *gobject_class;
76 gobject_class = (GObjectClass *) klass;
78 gobject_class->dispose = mmyth_tvplayer_dispose;
79 gobject_class->finalize = mmyth_tvplayer_finalize;
83 new_pad_cb(GstElement * element, GstPad * pad, gpointer data)
85 MMythTVPlayer *tvplayer = MMYTH_TVPLAYER(data);
89 s = gst_caps_to_string(pad->caps);
94 gst_element_get_pad(tvplayer->audioqueue1,
99 gst_element_get_pad(tvplayer->videoqueue1,
107 expose_cb(GtkWidget * widget, GdkEventExpose * event, gpointer user_data)
109 MMythTVPlayer *tvplayer = MMYTH_TVPLAYER(user_data);
111 if (tvplayer && tvplayer->videow) {
112 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY
113 (tvplayer->gst_videosink),
114 GDK_WINDOW_XWINDOW(widget->window));
118 g_warning("MMythTVPlayer expose called before setting video window\n");
124 mmyth_tvplayer_init(MMythTVPlayer * tvplayer)
126 tvplayer->gst_pipeline = NULL;
127 tvplayer->gst_source = NULL;
128 tvplayer->gst_videodec = NULL;
129 tvplayer->gst_videosink = NULL;
130 tvplayer->gst_videocolortrs = NULL;
131 tvplayer->videoqueue1 = NULL;
132 tvplayer->videoqueue2 = NULL;
133 tvplayer->audioqueue1 = NULL;
134 tvplayer->audioqueue2 = NULL;
137 * GTKWidget for rendering the video
139 tvplayer->videow = NULL;
140 tvplayer->expose_handler = 0;
142 tvplayer->backend_hostname = NULL;
143 tvplayer->backend_port = 0;
144 tvplayer->local_hostname = NULL;
146 tvplayer->recorder = NULL;
147 tvplayer->tvchain = NULL;
148 tvplayer->proginfo = NULL;
152 mmyth_tvplayer_dispose(GObject * object)
155 G_OBJECT_CLASS(mmyth_tvplayer_parent_class)->dispose(object);
159 mmyth_tvplayer_finalize(GObject * object)
161 g_signal_handlers_destroy(object);
163 MMythTVPlayer *tvplayer = MMYTH_TVPLAYER(object);
165 g_debug("[%s] Finalizing tvplayer", __FUNCTION__);
167 if (tvplayer->videow != NULL) {
168 if (g_signal_handler_is_connected(tvplayer->videow,
169 tvplayer->expose_handler)) {
170 g_signal_handler_disconnect(tvplayer->videow,
171 tvplayer->expose_handler);
173 g_object_unref(tvplayer->videow);
176 if (tvplayer->recorder != NULL)
177 g_object_unref(tvplayer->recorder);
178 if (tvplayer->tvchain != NULL)
179 g_object_unref(tvplayer->tvchain);
180 if (tvplayer->proginfo != NULL)
181 g_object_unref(tvplayer->proginfo);
183 // Release Gstreamer elements
184 if (tvplayer->gst_pipeline != NULL)
185 g_object_unref(tvplayer->gst_pipeline);
186 if (tvplayer->gst_source != NULL)
187 g_object_unref(tvplayer->gst_source);
188 if (tvplayer->gst_videodec != NULL)
189 g_object_unref(tvplayer->gst_videodec);
190 if (tvplayer->gst_videocolortrs != NULL)
191 g_object_unref(tvplayer->gst_videocolortrs);
192 if (tvplayer->gst_videosink != NULL)
193 g_object_unref(tvplayer->gst_videosink);
194 if (tvplayer->videoqueue1 != NULL)
195 g_object_unref(tvplayer->videoqueue1);
196 if (tvplayer->videoqueue2 != NULL)
197 g_object_unref(tvplayer->videoqueue2);
198 if (tvplayer->audioqueue1 != NULL)
199 g_object_unref(tvplayer->audioqueue1);
200 if (tvplayer->audioqueue2 != NULL)
201 g_object_unref(tvplayer->audioqueue2);
203 G_OBJECT_CLASS(mmyth_tvplayer_parent_class)->finalize(object);
206 /** Creates a new instance of MMythTVPlayer.
208 * @return a new instance of MMythTVPlayer.
213 MMythTVPlayer *tvplayer =
214 MMYTH_TVPLAYER(g_object_new(MMYTH_TVPLAYER_TYPE, NULL));
219 /** Initializes the tv player.
221 * @param tvplayer the object instance.
222 * @return gboolean TRUE if the pipeline was created
223 * successfully, FALSE otherwise.
226 mmyth_tvplayer_initialize(MMythTVPlayer * tvplayer,
227 GMythBackendInfo * backend_info)
229 tvplayer->backend_info = backend_info;
231 if (!mmyth_tvplayer_create_pipeline(tvplayer)) {
233 ("[%s] Error while creating pipeline. TV Player not initialized",
237 g_debug("[%s] GStreamer pipeline created", __FUNCTION__);
243 /** Creates the GStreamer pipeline used by the player.
245 * @param tvplayer the object instance.
246 * @return gboolean TRUE if the pipeline was created
247 * successfully, FALSE otherwise.
250 mmyth_tvplayer_create_pipeline(MMythTVPlayer * tvplayer)
252 GstElement *pipeline;
255 GstElement *videodec,
257 GstElement *videocolortrs;
258 #ifndef MAEMO_PLATFORM
259 GstElement *audiodec,
263 GstElement *audiosink;
264 GstElement *videoqueue1,
268 g_debug("MMythTVPlayer: Setting the Gstreamer pipeline\n");
270 pipeline = gst_pipeline_new("video-player");
271 source = gst_element_factory_make("mythtvsrc", "myth-source");
272 parser = gst_element_factory_make("nuvdemux", "nuv-demux");
275 * Gstreamer Video elements
277 videoqueue1 = gst_element_factory_make("queue", "video-queue1");
278 videodec = gst_element_factory_make("ffdec_mpeg4", "video-decoder");
279 videoqueue2 = gst_element_factory_make("queue", "video-queue2");
281 gst_element_factory_make("ffmpegcolorspace",
282 "image-color-transforms");
284 #ifdef MAEMO_PLATFORM
285 videosink = gst_element_factory_make("sdlvideosink", "image-output");
287 videosink = gst_element_factory_make("xvimagesink", "image-output");
291 * Gstreamer Audio elements
293 audioqueue1 = gst_element_factory_make("queue", "audio-queue1");
294 #ifdef MAEMO_PLATFORM
295 audiosink = gst_element_factory_make("dspmp3sink", "audio-output");
297 audioqueue2 = gst_element_factory_make("queue", "audio-queue2");
298 audiodec = gst_element_factory_make("mad", "audio-decoder");
300 gst_element_factory_make("audioconvert", "audio-converter");
301 audiosink = gst_element_factory_make("alsasink", "audio-output");
304 if (!(pipeline && source && parser && videodec && videosink) ||
305 !(videoqueue1 && videoqueue2 && audioqueue1 && audiosink)) {
307 * FIXME: hanlde the error correctly
310 * video_alignment is not being created (below) and is causing
314 tvplayer->gst_pipeline = NULL;
315 tvplayer->gst_videodec = NULL;
316 tvplayer->gst_videosink = NULL;
317 tvplayer->gst_videocolortrs = NULL;
319 g_warning("GstElement creation error!\n");
322 #ifndef MAEMO_PLATFORM
323 if (!(audiodec && audioconv)) {
324 g_warning("GstElement for audio stream creation error!");
329 tvplayer->gst_pipeline = pipeline;
330 tvplayer->gst_source = source;
331 tvplayer->gst_videodec = videodec;
332 tvplayer->gst_videosink = videosink;
333 tvplayer->gst_videocolortrs = videocolortrs;
334 g_object_ref(tvplayer->gst_pipeline);
335 g_object_ref(tvplayer->gst_source);
336 g_object_ref(tvplayer->gst_videodec);
337 g_object_ref(tvplayer->gst_videosink);
338 g_object_ref(tvplayer->gst_videocolortrs);
340 tvplayer->videoqueue1 = videoqueue1;
341 tvplayer->videoqueue2 = videoqueue2;
342 tvplayer->audioqueue1 = audioqueue1;
343 g_object_ref(tvplayer->videoqueue1);
344 g_object_ref(tvplayer->videoqueue2);
345 g_object_ref(tvplayer->audioqueue1);
347 #ifndef MAEMO_PLATFORM
348 tvplayer->audioqueue2 = audioqueue2;
349 g_object_ref(tvplayer->audioqueue2);
352 // g_object_set (G_OBJECT (videosink), "sync", TRUE, NULL);
353 g_object_set(G_OBJECT(audiosink), "sync", FALSE, NULL);
355 gst_bus_add_watch(gst_pipeline_get_bus
356 (GST_PIPELINE(tvplayer->gst_pipeline)), bus_call,
359 gst_bin_add_many(GST_BIN(pipeline), source, parser, videoqueue1,
360 videodec, videoqueue2, videocolortrs, videosink,
363 #ifndef MAEMO_PLATFORM
364 gst_bin_add_many(GST_BIN(pipeline), audioqueue1, audiodec, audioconv,
365 audioqueue2, audiosink, NULL);
367 gst_bin_add_many(GST_BIN(pipeline), audioqueue1, audiosink, NULL);
371 // GstCaps *rtpcaps = gst_caps_new_simple ("application/x-rtp",
373 // gst_element_link_filtered(source, parser, rtpcaps);
376 gst_element_link(source, parser);
377 gst_element_link_many(videoqueue1, videodec, videoqueue2,
378 videocolortrs, videosink, NULL);
380 #ifndef MAEMO_PLATFORM
381 gst_element_link_many(videosink, audioqueue1, audiodec, audioconv,
382 audioqueue2, audiosink, NULL);
384 gst_element_link_many(videosink, audioqueue1, audiosink, NULL);
387 g_signal_connect(parser, "pad-added", G_CALLBACK(new_pad_cb),
393 /** Configures the backend and the tv player
394 * for playing the recorded content A/V.
396 * FIXME: Change filename to program info or other structure about the recorded
398 * @param tvplayer the object instance.
399 * @param filename the file uri of the recorded content to be played.
400 * @return TRUE if successfull, FALSE if any error happens.
403 mmyth_tvplayer_record_setup(MMythTVPlayer * tvplayer,
404 const gchar * filename)
406 // FIXME: we should receive the uri instead of filename
407 const gchar *hostname =
408 gmyth_backend_info_get_hostname(tvplayer->backend_info);
410 gmyth_backend_info_get_port(tvplayer->backend_info);
412 GString *fullpath = g_string_new("myth://");
413 g_string_append_printf(fullpath, "%s:%d/%s", hostname, port, filename);
415 tvplayer->is_livetv = FALSE;
417 g_debug("[%s] Setting record uri to gstreamer pipeline to %s",
418 __FUNCTION__, fullpath->str);
420 g_object_set(G_OBJECT(tvplayer->gst_source), "location",
421 fullpath->str, NULL);
426 /** Configures the backend and the tv player
427 * for playing the live tv.
429 * @param tvplayer the object instance.
430 * @return TRUE if successfull, FALSE if any error happens.
433 mmyth_tvplayer_livetv_setup(MMythTVPlayer * tvplayer)
437 tvplayer->livetv = gmyth_livetv_new();
439 if (!gmyth_livetv_setup(tvplayer->livetv, tvplayer->backend_info))
446 if (tvplayer->livetv != NULL) {
447 g_object_unref(tvplayer->livetv);
450 if (tvplayer->local_hostname != NULL) {
451 g_string_free(tvplayer->local_hostname, TRUE);
454 if (tvplayer->recorder != NULL) {
455 g_object_unref(tvplayer->recorder);
456 tvplayer->recorder = NULL;
459 if (tvplayer->tvchain != NULL) {
460 g_object_unref(tvplayer->tvchain);
461 tvplayer->tvchain = NULL;
464 if (tvplayer->proginfo != NULL) {
465 g_object_unref(tvplayer->proginfo);
466 tvplayer->proginfo = NULL;
473 /** Sets the GTK video widget for the tv player.
475 * @param tvplayer the object instance.
476 * @param videow the GTK video window.
477 * @return TRUE if successfull, FALSE if any error happens.
480 mmyth_tvplayer_set_widget(MMythTVPlayer * tvplayer, GtkWidget * videow)
482 tvplayer->videow = videow;
483 g_object_ref(videow);
485 g_debug("[%s] Setting widget for tv player render", __FUNCTION__);
487 tvplayer->expose_handler =
488 g_signal_connect(tvplayer->videow, "expose-event",
489 G_CALLBACK(expose_cb), tvplayer);
491 // g_signal_connect(miptv_ui->videow, "size_request",
492 // G_CALLBACK(cb_preferred_video_size), miptv_ui);
498 bus_call(GstBus * bus, GstMessage * msg, gpointer data)
500 // MMythTVPlayer *tvplayer = MMYTH_TVPLAYER ( data );
501 // GMainLoop *loop = tvplayer->loop;
503 switch (GST_MESSAGE_TYPE(msg)) {
504 case GST_MESSAGE_EOS:
505 printf("End of stream\n");
506 // g_idle_add ((GSourceFunc) idle_eos, data);
507 gst_element_set_state(GST_ELEMENT(GST_MESSAGE_SRC(msg)),
509 gst_element_set_locked_state(GST_ELEMENT(GST_MESSAGE_SRC(msg)),
512 case GST_MESSAGE_ERROR:
517 gst_message_parse_error(msg, &err, &debug);
520 printf("Error: %s\n", err->message);
523 // g_main_loop_quit (loop);
527 printf(gst_message_type_get_name(GST_MESSAGE_TYPE(msg)));
538 idle_state(gpointer data)
540 GstPlayerWindowStateChange *st = data;
542 if (st->old_state == GST_STATE_PLAYING) {
543 if (st->miptv_ui->idle_id != 0) {
544 g_source_remove(st->miptv_ui->idle_id);
545 st->miptv_ui->idle_id = 0;
547 } else if (st->new_state == GST_STATE_PLAYING) {
548 if (st->miptv_ui->idle_id != 0)
549 g_source_remove(st->miptv_ui->idle_id);
551 st->miptv_ui->idle_id = g_idle_add(cb_iterate, st->miptv_ui);
557 if (st->old_state == GST_STATE_READY
558 && st->new_state > GST_STATE_READY) {
561 * gboolean have_video = FALSE;
564 gtk_widget_show(st->miptv_ui->videow);
566 gtk_window_resize(GTK_WINDOW(st->miptv_ui->main_window), 1, 1);
573 if (st->old_state > GST_STATE_READY
574 && st->new_state == GST_STATE_READY) {
576 if (st->miptv_ui->tagcache) {
577 gst_tag_list_free(st->miptv_ui->tagcache);
578 st->miptv_ui->tagcache = NULL;
582 gst_object_unref(GST_OBJECT(st->play));
583 // g_object_unref (G_OBJECT (st->win));
594 /** Stops playing the current A/V.
596 * FIXME: How to proceed differently between livetv
597 * and recorded content?
599 * @param tvplayer the object instance.
603 mmyth_tvplayer_stop_playing(MMythTVPlayer * tvplayer)
605 g_debug("[%s] Setting gstreamer pipeline state to NULL", __FUNCTION__);
607 gst_element_set_state(tvplayer->gst_pipeline, GST_STATE_NULL);
609 if (tvplayer->is_livetv) {
610 if (!gmyth_recorder_stop_livetv(tvplayer->recorder)) {
611 g_warning("[%s] Error while stoping remote encoder",
617 /** Queries if the tvplayer is active playing A/V content.
619 * @param tvplayer the object instance.
620 * @return TRUE if the tvplayer is active, FALSE otherwise.
623 mmyth_tvplayer_is_playing(MMythTVPlayer * tvplayer)
625 return (GST_STATE(tvplayer->gst_pipeline) == GST_STATE_PLAYING);
628 /** Static function that sets the tvplayer state to PLAYING.
630 * @param tvplayer the object instance.
631 * @return TRUE if the tvplayer is active, FALSE otherwise.
634 idle_play(gpointer data)
636 MMythTVPlayer *tvplayer = MMYTH_TVPLAYER(data);
638 g_debug("MMythTVPlayer: Setting pipeline state to PLAYING\n");
640 gst_element_set_state(tvplayer->gst_pipeline, GST_STATE_PLAYING);
645 /** Start playing A/V with the tvplayer attributes.
647 * @param tvplayer the object instance.
650 mmyth_tvplayer_start_playing(MMythTVPlayer * tvplayer)
653 // FIXME: Move this to livetv_setup??
654 if (tvplayer->is_livetv) {
657 if (!tvplayer || !(tvplayer->proginfo)
658 || !(tvplayer->local_hostname)
659 || !(tvplayer->gst_source)) {
660 g_warning("GMythtvPlayer not ready to start playing\n");
663 if (!(tvplayer->proginfo->pathname)) {
665 ("[%s] Playback url is null, could not play the myth content",
670 g_debug("MMythTVPlayer: Start playing %s",
671 tvplayer->proginfo->pathname->str);
673 g_object_set(G_OBJECT(tvplayer->gst_source), "mythtv-live",
676 if (tvplayer->tvchain != NULL) {
677 GString *str_chainid =
678 gmyth_tvchain_get_id(tvplayer->tvchain);
679 g_print("[%s]\tCHAIN ID: %s\n", __FUNCTION__,
682 g_object_set(G_OBJECT(tvplayer->gst_source),
683 "mythtv-live-chainid", g_strdup(str_chainid->str),
685 if (str_chainid != NULL)
686 g_string_free(str_chainid, FALSE);
689 if (tvplayer->recorder != NULL)
690 g_object_set(G_OBJECT(tvplayer->gst_source), "mythtv-live-id",
691 tvplayer->recorder->recorder_num, NULL);
692 g_debug("[%s] Setting location to %s", __FUNCTION__,
693 tvplayer->proginfo->pathname->str);
696 * Sets the gstreamer properties acording to the service access
699 g_object_set(G_OBJECT(tvplayer->gst_source), "location",
700 tvplayer->proginfo->pathname->str, NULL);
704 g_object_set(G_OBJECT(tvplayer->gst_source), "mythtv-version",
705 MYTHTV_VERSION_DEFAULT, NULL);
707 g_object_set(G_OBJECT(tvplayer->gst_source), "mythtv-debug",
710 g_idle_add(idle_play, tvplayer);