From 12a7bf9f7c3229b0f911c9c5510c3d833bf194a0 Mon Sep 17 00:00:00 2001 From: Philippe Normand Date: Mon, 7 Dec 2020 10:47:30 +0000 Subject: [PATCH] player: Rewrite as GstPlay wrapper For the time being the GstPlayer library remains as a wrapper for GstPlay, in order to keep existing applications working and give them time to port to GstPlay. GstPlayer will remain in -bad for a couple cycles and the plan for GstPlay is to move it to -base before 1.20. Part-of: --- gst-libs/gst/play/gstplay-signal-adapter.c | 4 +- gst-libs/gst/player/gstplayer-media-info-private.h | 57 +- gst-libs/gst/player/gstplayer-media-info.c | 246 +- .../gstplayer-wrapped-video-renderer-private.h | 49 + .../gst/player/gstplayer-wrapped-video-renderer.c | 114 + gst-libs/gst/player/gstplayer.c | 3455 +------------------- gst-libs/gst/player/meson.build | 7 +- 7 files changed, 500 insertions(+), 3432 deletions(-) create mode 100644 gst-libs/gst/player/gstplayer-wrapped-video-renderer-private.h create mode 100644 gst-libs/gst/player/gstplayer-wrapped-video-renderer.c diff --git a/gst-libs/gst/play/gstplay-signal-adapter.c b/gst-libs/gst/play/gstplay-signal-adapter.c index 24b7e6b..cc5f0e0 100644 --- a/gst-libs/gst/play/gstplay-signal-adapter.c +++ b/gst-libs/gst/play/gstplay-signal-adapter.c @@ -439,12 +439,12 @@ gst_play_signal_adapter_class_init (GstPlaySignalAdapterClass * klass) signals[SIGNAL_VOLUME_CHANGED] = g_signal_new ("volume-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL, - NULL, NULL, G_TYPE_NONE, 0, G_TYPE_INVALID); + NULL, NULL, G_TYPE_NONE, 1, G_TYPE_DOUBLE); signals[SIGNAL_MUTE_CHANGED] = g_signal_new ("mute-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL, - NULL, NULL, G_TYPE_NONE, 0, G_TYPE_INVALID); + NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); signals[SIGNAL_WARNING] = g_signal_new ("warning", G_TYPE_FROM_CLASS (klass), diff --git a/gst-libs/gst/player/gstplayer-media-info-private.h b/gst-libs/gst/player/gstplayer-media-info-private.h index 70aaea9..1075ef9 100644 --- a/gst-libs/gst/player/gstplayer-media-info-private.h +++ b/gst-libs/gst/player/gstplayer-media-info-private.h @@ -23,16 +23,14 @@ #ifndef __GST_PLAYER_MEDIA_INFO_PRIVATE_H__ #define __GST_PLAYER_MEDIA_INFO_PRIVATE_H__ +#include + struct _GstPlayerStreamInfo { GObject parent; - gchar *codec; - - GstCaps *caps; gint stream_index; - GstTagList *tags; - gchar *stream_id; + GstPlayStreamInfo *info; }; struct _GstPlayerStreamInfoClass @@ -42,9 +40,9 @@ struct _GstPlayerStreamInfoClass struct _GstPlayerSubtitleInfo { - GstPlayerStreamInfo parent; + GstPlayerStreamInfo parent; - gchar *language; + GstPlaySubtitleInfo *info; }; struct _GstPlayerSubtitleInfoClass @@ -54,15 +52,9 @@ struct _GstPlayerSubtitleInfoClass struct _GstPlayerAudioInfo { - GstPlayerStreamInfo parent; - - gint channels; - gint sample_rate; - - guint bitrate; - guint max_bitrate; + GstPlayerStreamInfo parent; - gchar *language; + GstPlayAudioInfo *info; }; struct _GstPlayerAudioInfoClass @@ -72,17 +64,9 @@ struct _GstPlayerAudioInfoClass struct _GstPlayerVideoInfo { - GstPlayerStreamInfo parent; + GstPlayerStreamInfo parent; - gint width; - gint height; - gint framerate_num; - gint framerate_denom; - gint par_num; - gint par_denom; - - guint bitrate; - guint max_bitrate; + GstPlayVideoInfo *info; }; struct _GstPlayerVideoInfoClass @@ -94,19 +78,11 @@ struct _GstPlayerMediaInfo { GObject parent; - gchar *uri; - gchar *title; - gchar *container; - gboolean seekable, is_live; - GstTagList *tags; - GstSample *image_sample; - GList *stream_list; GList *audio_stream_list; GList *video_stream_list; GList *subtitle_stream_list; - - GstClockTime duration; + GstPlayMediaInfo *info; }; struct _GstPlayerMediaInfoClass @@ -115,12 +91,23 @@ struct _GstPlayerMediaInfoClass }; G_GNUC_INTERNAL GstPlayerMediaInfo* gst_player_media_info_new - (const gchar *uri); + (void); G_GNUC_INTERNAL GstPlayerMediaInfo* gst_player_media_info_copy (GstPlayerMediaInfo *ref); G_GNUC_INTERNAL GstPlayerStreamInfo* gst_player_stream_info_new (gint stream_index, GType type); +G_GNUC_INTERNAL GstPlayerStreamInfo* gst_player_stream_info_wrapped + (GstPlayStreamInfo * info); G_GNUC_INTERNAL GstPlayerStreamInfo* gst_player_stream_info_copy (GstPlayerStreamInfo *ref); +G_GNUC_INTERNAL GstPlayerMediaInfo* gst_player_media_info_wrapped + (GstPlayMediaInfo *info); +G_GNUC_INTERNAL GstPlayerAudioInfo* gst_player_audio_info_wrapped + (GstPlayAudioInfo *info); +G_GNUC_INTERNAL GstPlayerVideoInfo* gst_player_video_info_wrapped + (GstPlayVideoInfo *info); +G_GNUC_INTERNAL GstPlayerSubtitleInfo* gst_player_subtitle_info_wrapped + (GstPlaySubtitleInfo *info); + #endif /* __GST_PLAYER_MEDIA_INFO_PRIVATE_H__ */ diff --git a/gst-libs/gst/player/gstplayer-media-info.c b/gst-libs/gst/player/gstplayer-media-info.c index 6c9a4a8..c805ebe 100644 --- a/gst-libs/gst/player/gstplayer-media-info.c +++ b/gst-libs/gst/player/gstplayer-media-info.c @@ -47,14 +47,7 @@ gst_player_stream_info_finalize (GObject * object) { GstPlayerStreamInfo *sinfo = GST_PLAYER_STREAM_INFO (object); - g_free (sinfo->codec); - g_free (sinfo->stream_id); - - if (sinfo->caps) - gst_caps_unref (sinfo->caps); - - if (sinfo->tags) - gst_tag_list_unref (sinfo->tags); + g_clear_object (&sinfo->info); G_OBJECT_CLASS (gst_player_stream_info_parent_class)->finalize (object); } @@ -97,12 +90,7 @@ gst_player_stream_info_get_stream_type (const GstPlayerStreamInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_STREAM_INFO (info), NULL); - if (GST_IS_PLAYER_VIDEO_INFO (info)) - return "video"; - else if (GST_IS_PLAYER_AUDIO_INFO (info)) - return "audio"; - else - return "subtitle"; + return gst_play_stream_info_get_stream_type (info->info); } /** @@ -116,7 +104,7 @@ gst_player_stream_info_get_tags (const GstPlayerStreamInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_STREAM_INFO (info), NULL); - return info->tags; + return gst_play_stream_info_get_tags (info->info); } /** @@ -132,7 +120,7 @@ gst_player_stream_info_get_codec (const GstPlayerStreamInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_STREAM_INFO (info), NULL); - return info->codec; + return gst_play_stream_info_get_codec (info->info); } /** @@ -146,7 +134,7 @@ gst_player_stream_info_get_caps (const GstPlayerStreamInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_STREAM_INFO (info), NULL); - return info->caps; + return gst_play_stream_info_get_caps (info->info); } /* Video information */ @@ -154,14 +142,9 @@ G_DEFINE_TYPE (GstPlayerVideoInfo, gst_player_video_info, GST_TYPE_PLAYER_STREAM_INFO); static void -gst_player_video_info_init (GstPlayerVideoInfo * info) +gst_player_video_info_init (G_GNUC_UNUSED GstPlayerVideoInfo * info) { - info->width = -1; - info->height = -1; - info->framerate_num = 0; - info->framerate_denom = 1; - info->par_num = 1; - info->par_denom = 1; + } static void @@ -181,7 +164,7 @@ gst_player_video_info_get_width (const GstPlayerVideoInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_VIDEO_INFO (info), -1); - return info->width; + return gst_play_video_info_get_width (info->info); } /** @@ -195,7 +178,7 @@ gst_player_video_info_get_height (const GstPlayerVideoInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_VIDEO_INFO (info), -1); - return info->height; + return gst_play_video_info_get_height (info->info); } /** @@ -211,8 +194,7 @@ gst_player_video_info_get_framerate (const GstPlayerVideoInfo * info, { g_return_if_fail (GST_IS_PLAYER_VIDEO_INFO (info)); - *fps_n = info->framerate_num; - *fps_d = info->framerate_denom; + gst_play_video_info_get_framerate (info->info, fps_n, fps_d); } /** @@ -230,8 +212,7 @@ gst_player_video_info_get_pixel_aspect_ratio (const GstPlayerVideoInfo * info, { g_return_if_fail (GST_IS_PLAYER_VIDEO_INFO (info)); - *par_n = info->par_num; - *par_d = info->par_denom; + gst_play_video_info_get_pixel_aspect_ratio (info->info, par_n, par_d); } /** @@ -245,7 +226,7 @@ gst_player_video_info_get_bitrate (const GstPlayerVideoInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_VIDEO_INFO (info), -1); - return info->bitrate; + return gst_play_video_info_get_bitrate (info->info); } /** @@ -259,7 +240,7 @@ gst_player_video_info_get_max_bitrate (const GstPlayerVideoInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_VIDEO_INFO (info), -1); - return info->max_bitrate; + return gst_play_video_info_get_max_bitrate (info->info); } /* Audio information */ @@ -267,12 +248,9 @@ G_DEFINE_TYPE (GstPlayerAudioInfo, gst_player_audio_info, GST_TYPE_PLAYER_STREAM_INFO); static void -gst_player_audio_info_init (GstPlayerAudioInfo * info) +gst_player_audio_info_init (G_GNUC_UNUSED GstPlayerAudioInfo * info) { - info->channels = 0; - info->sample_rate = 0; - info->bitrate = -1; - info->max_bitrate = -1; + } static void @@ -280,7 +258,7 @@ gst_player_audio_info_finalize (GObject * object) { GstPlayerAudioInfo *info = GST_PLAYER_AUDIO_INFO (object); - g_free (info->language); + g_clear_object (&info->info); G_OBJECT_CLASS (gst_player_audio_info_parent_class)->finalize (object); } @@ -304,7 +282,7 @@ gst_player_audio_info_get_language (const GstPlayerAudioInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_AUDIO_INFO (info), NULL); - return info->language; + return gst_play_audio_info_get_language (info->info); } /** @@ -318,7 +296,7 @@ gst_player_audio_info_get_channels (const GstPlayerAudioInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_AUDIO_INFO (info), 0); - return info->channels; + return gst_play_audio_info_get_channels (info->info); } /** @@ -332,7 +310,7 @@ gst_player_audio_info_get_sample_rate (const GstPlayerAudioInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_AUDIO_INFO (info), 0); - return info->sample_rate; + return gst_play_audio_info_get_sample_rate (info->info); } /** @@ -346,7 +324,7 @@ gst_player_audio_info_get_bitrate (const GstPlayerAudioInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_AUDIO_INFO (info), -1); - return info->bitrate; + return gst_play_audio_info_get_bitrate (info->info); } /** @@ -360,7 +338,7 @@ gst_player_audio_info_get_max_bitrate (const GstPlayerAudioInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_AUDIO_INFO (info), -1); - return info->max_bitrate; + return gst_play_audio_info_get_max_bitrate (info->info); } /* Subtitle information */ @@ -378,7 +356,7 @@ gst_player_subtitle_info_finalize (GObject * object) { GstPlayerSubtitleInfo *info = GST_PLAYER_SUBTITLE_INFO (object); - g_free (info->language); + g_clear_object (&info->info); G_OBJECT_CLASS (gst_player_subtitle_info_parent_class)->finalize (object); } @@ -402,18 +380,16 @@ gst_player_subtitle_info_get_language (const GstPlayerSubtitleInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_SUBTITLE_INFO (info), NULL); - return info->language; + return gst_play_subtitle_info_get_language (info->info); } /* Global media information */ G_DEFINE_TYPE (GstPlayerMediaInfo, gst_player_media_info, G_TYPE_OBJECT); static void -gst_player_media_info_init (GstPlayerMediaInfo * info) +gst_player_media_info_init (G_GNUC_UNUSED GstPlayerMediaInfo * info) { - info->duration = -1; - info->is_live = FALSE; - info->seekable = FALSE; + } static void @@ -421,18 +397,6 @@ gst_player_media_info_finalize (GObject * object) { GstPlayerMediaInfo *info = GST_PLAYER_MEDIA_INFO (object); - g_free (info->uri); - - if (info->tags) - gst_tag_list_unref (info->tags); - - g_free (info->title); - - g_free (info->container); - - if (info->image_sample) - gst_sample_unref (info->image_sample); - if (info->audio_stream_list) g_list_free (info->audio_stream_list); @@ -444,6 +408,7 @@ gst_player_media_info_finalize (GObject * object) if (info->stream_list) g_list_free_full (info->stream_list, g_object_unref); + g_clear_object (&info->info); G_OBJECT_CLASS (gst_player_media_info_parent_class)->finalize (object); } @@ -480,15 +445,7 @@ gst_player_video_info_copy (GstPlayerVideoInfo * ref) GstPlayerVideoInfo *ret; ret = gst_player_video_info_new (); - - ret->width = ref->width; - ret->height = ref->height; - ret->framerate_num = ref->framerate_num; - ret->framerate_denom = ref->framerate_denom; - ret->par_num = ref->par_num; - ret->par_denom = ref->par_denom; - ret->bitrate = ref->bitrate; - ret->max_bitrate = ref->max_bitrate; + ret->info = g_object_ref (ref->info); return (GstPlayerStreamInfo *) ret; } @@ -499,14 +456,7 @@ gst_player_audio_info_copy (GstPlayerAudioInfo * ref) GstPlayerAudioInfo *ret; ret = gst_player_audio_info_new (); - - ret->sample_rate = ref->sample_rate; - ret->channels = ref->channels; - ret->bitrate = ref->bitrate; - ret->max_bitrate = ref->max_bitrate; - - if (ref->language) - ret->language = g_strdup (ref->language); + ret->info = g_object_ref (ref->info); return (GstPlayerStreamInfo *) ret; } @@ -517,8 +467,7 @@ gst_player_subtitle_info_copy (GstPlayerSubtitleInfo * ref) GstPlayerSubtitleInfo *ret; ret = gst_player_subtitle_info_new (); - if (ref->language) - ret->language = g_strdup (ref->language); + ret->info = g_object_ref (ref->info); return (GstPlayerStreamInfo *) ret; } @@ -539,14 +488,6 @@ gst_player_stream_info_copy (GstPlayerStreamInfo * ref) info = gst_player_subtitle_info_copy ((GstPlayerSubtitleInfo *) ref); info->stream_index = ref->stream_index; - if (ref->tags) - info->tags = gst_tag_list_ref (ref->tags); - if (ref->caps) - info->caps = gst_caps_copy (ref->caps); - if (ref->codec) - info->codec = g_strdup (ref->codec); - if (ref->stream_id) - info->stream_id = g_strdup (ref->stream_id); return info; } @@ -560,20 +501,9 @@ gst_player_media_info_copy (GstPlayerMediaInfo * ref) if (!ref) return NULL; - info = gst_player_media_info_new (ref->uri); - info->duration = ref->duration; - info->seekable = ref->seekable; - info->is_live = ref->is_live; - if (ref->tags) - info->tags = gst_tag_list_ref (ref->tags); - if (ref->title) - info->title = g_strdup (ref->title); - if (ref->container) - info->container = g_strdup (ref->container); - if (ref->image_sample) - info->image_sample = gst_sample_ref (ref->image_sample); - - for (l = ref->stream_list; l != NULL; l = l->next) { + info = gst_player_media_info_new (); + + for (l = gst_player_media_info_get_stream_list (ref); l != NULL; l = l->next) { GstPlayerStreamInfo *s; s = gst_player_stream_info_copy ((GstPlayerStreamInfo *) l->data); @@ -588,6 +518,8 @@ gst_player_media_info_copy (GstPlayerMediaInfo * ref) g_list_append (info->subtitle_stream_list, s); } + info->info = g_object_ref (ref->info); + return info; } @@ -608,17 +540,99 @@ gst_player_stream_info_new (gint stream_index, GType type) return info; } +GstPlayerStreamInfo * +gst_player_stream_info_wrapped (GstPlayStreamInfo * info) +{ + GstPlayerStreamInfo *ret; + GType type; + + if (GST_IS_PLAY_AUDIO_INFO (info)) { + type = GST_TYPE_PLAYER_AUDIO_INFO; + } else if (GST_IS_PLAY_VIDEO_INFO (info)) { + type = GST_TYPE_PLAYER_VIDEO_INFO; + } else { + type = GST_TYPE_PLAYER_SUBTITLE_INFO; + } + + ret = + gst_player_stream_info_new (gst_play_stream_info_get_index (info), type); + ret->info = g_object_ref (info); + return ret; +} + GstPlayerMediaInfo * -gst_player_media_info_new (const gchar * uri) +gst_player_media_info_new (void) { - GstPlayerMediaInfo *info; + return g_object_new (GST_TYPE_PLAYER_MEDIA_INFO, NULL); +} - g_return_val_if_fail (uri != NULL, NULL); +GstPlayerMediaInfo * +gst_player_media_info_wrapped (GstPlayMediaInfo * info) +{ + GstPlayerMediaInfo *ret; + GList *l; - info = g_object_new (GST_TYPE_PLAYER_MEDIA_INFO, NULL); - info->uri = g_strdup (uri); + ret = gst_player_media_info_new (); + ret->info = g_object_ref (info); - return info; + for (l = gst_play_media_info_get_stream_list (info); l != NULL; l = l->next) { + GstPlayerStreamInfo *s; + + s = gst_player_stream_info_wrapped ((GstPlayStreamInfo *) l->data); + ret->stream_list = g_list_append (ret->stream_list, s); + + if (GST_IS_PLAYER_AUDIO_INFO (s)) { + GstPlayerAudioInfo *i = GST_PLAYER_AUDIO_INFO (s); + i->info = g_object_ref (GST_PLAY_AUDIO_INFO (l->data)); + ret->audio_stream_list = g_list_append (ret->audio_stream_list, i); + } else if (GST_IS_PLAYER_VIDEO_INFO (s)) { + GstPlayerVideoInfo *i = GST_PLAYER_VIDEO_INFO (s); + i->info = g_object_ref (GST_PLAY_VIDEO_INFO (l->data)); + ret->video_stream_list = g_list_append (ret->video_stream_list, i); + } else { + GstPlayerSubtitleInfo *i = GST_PLAYER_SUBTITLE_INFO (s); + i->info = g_object_ref (GST_PLAY_SUBTITLE_INFO (l->data)); + ret->subtitle_stream_list = g_list_append (ret->subtitle_stream_list, i); + } + } + + return ret; +} + +GstPlayerAudioInfo * +gst_player_audio_info_wrapped (GstPlayAudioInfo * info) +{ + GstPlayerStreamInfo *s; + GstPlayerAudioInfo *i; + + s = gst_player_stream_info_wrapped ((GstPlayStreamInfo *) info); + i = GST_PLAYER_AUDIO_INFO (s); + i->info = g_object_ref (info); + return i; +} + +GstPlayerVideoInfo * +gst_player_video_info_wrapped (GstPlayVideoInfo * info) +{ + GstPlayerStreamInfo *s; + GstPlayerVideoInfo *i; + + s = gst_player_stream_info_wrapped ((GstPlayStreamInfo *) info); + i = GST_PLAYER_VIDEO_INFO (s); + i->info = g_object_ref (info); + return i; +} + +GstPlayerSubtitleInfo * +gst_player_subtitle_info_wrapped (GstPlaySubtitleInfo * info) +{ + GstPlayerStreamInfo *s; + GstPlayerSubtitleInfo *i; + + s = gst_player_stream_info_wrapped ((GstPlayStreamInfo *) info); + i = GST_PLAYER_SUBTITLE_INFO (s); + i->info = g_object_ref (info); + return i; } /** @@ -632,7 +646,7 @@ gst_player_media_info_get_uri (const GstPlayerMediaInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_MEDIA_INFO (info), NULL); - return info->uri; + return gst_play_media_info_get_uri (info->info); } /** @@ -646,7 +660,7 @@ gst_player_media_info_is_seekable (const GstPlayerMediaInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_MEDIA_INFO (info), FALSE); - return info->seekable; + return gst_play_media_info_is_seekable (info->info); } /** @@ -660,7 +674,7 @@ gst_player_media_info_is_live (const GstPlayerMediaInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_MEDIA_INFO (info), FALSE); - return info->is_live; + return gst_play_media_info_is_live (info->info); } /** @@ -734,7 +748,7 @@ gst_player_media_info_get_duration (const GstPlayerMediaInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_MEDIA_INFO (info), -1); - return info->duration; + return gst_play_media_info_get_duration (info->info); } /** @@ -748,7 +762,7 @@ gst_player_media_info_get_tags (const GstPlayerMediaInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_MEDIA_INFO (info), NULL); - return info->tags; + return gst_play_media_info_get_tags (info->info); } /** @@ -762,7 +776,7 @@ gst_player_media_info_get_title (const GstPlayerMediaInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_MEDIA_INFO (info), NULL); - return info->title; + return gst_play_media_info_get_title (info->info); } /** @@ -776,7 +790,7 @@ gst_player_media_info_get_container_format (const GstPlayerMediaInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_MEDIA_INFO (info), NULL); - return info->container; + return gst_play_media_info_get_container_format (info->info); } /** @@ -793,7 +807,7 @@ gst_player_media_info_get_image_sample (const GstPlayerMediaInfo * info) { g_return_val_if_fail (GST_IS_PLAYER_MEDIA_INFO (info), NULL); - return info->image_sample; + return gst_play_media_info_get_image_sample (info->info); } /** diff --git a/gst-libs/gst/player/gstplayer-wrapped-video-renderer-private.h b/gst-libs/gst/player/gstplayer-wrapped-video-renderer-private.h new file mode 100644 index 0000000..99fd155 --- /dev/null +++ b/gst-libs/gst/player/gstplayer-wrapped-video-renderer-private.h @@ -0,0 +1,49 @@ +/* GStreamer + * + * Copyright (C) 2020 Philippe Normand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_PLAYER_WRAPPED_VIDEO_RENDERER_H__ +#define __GST_PLAYER_WRAPPED_VIDEO_RENDERER_H__ + +#include +#include + +G_BEGIN_DECLS + +typedef struct _GstPlayerWrappedVideoRenderer + GstPlayerWrappedVideoRenderer; +typedef struct _GstPlayerWrappedVideoRendererClass + GstPlayerWrappedVideoRendererClass; + +#define GST_TYPE_PLAYER_WRAPPED_VIDEO_RENDERER (gst_player_wrapped_video_renderer_get_type ()) +#define GST_IS_PLAYER_WRAPPED_VIDEO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PLAYER_WRAPPED_VIDEO_RENDERER)) +#define GST_IS_PLAYER_WRAPPED_VIDEO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLAYER_WRAPPED_VIDEO_RENDERER)) +#define GST_PLAYER_WRAPPED_VIDEO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PLAYER_WRAPPED_VIDEO_RENDERER, GstPlayerWrappedVideoRendererClass)) +#define GST_PLAYER_WRAPPED_VIDEO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PLAYER_WRAPPED_VIDEO_RENDERER, GstPlayerWrappedVideoRenderer)) +#define GST_PLAYER_WRAPPED_VIDEO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PLAYER_WRAPPED_VIDEO_RENDERER, GstPlayerWrappedVideoRendererClass)) +#define GST_PLAYER_WRAPPED_VIDEO_RENDERER_CAST(obj) ((GstPlayerWrappedVideoRenderer*)(obj)) + +GType gst_player_wrapped_video_renderer_get_type (void); + +GstPlayerVideoRenderer * gst_player_wrapped_video_renderer_new (GstPlayerVideoRenderer * renderer, GstPlayer * player); + + +G_END_DECLS + +#endif /* __GST_PLAYER_WRAPPED_VIDEO_RENDERER_H__ */ diff --git a/gst-libs/gst/player/gstplayer-wrapped-video-renderer.c b/gst-libs/gst/player/gstplayer-wrapped-video-renderer.c new file mode 100644 index 0000000..a54f1df --- /dev/null +++ b/gst-libs/gst/player/gstplayer-wrapped-video-renderer.c @@ -0,0 +1,114 @@ +/* GStreamer + * + * Copyright (C) 2020 Philippe Normand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstplayer-wrapped-video-renderer-private.h" +#include "gstplayer.h" +#include "gstplayer-video-renderer-private.h" + +/* + * This object is an internal wrapper created by the GstPlayer, implementing the + * new GstPlayVideoRenderer interface and acting as a bridge from the legacy + * GstPlayerVideoRenderer interface. + */ + +struct _GstPlayerWrappedVideoRenderer +{ + GObject parent; + + GstPlayerVideoRenderer *renderer; + GstPlayer *player; +}; + +struct _GstPlayerWrappedVideoRendererClass +{ + GObjectClass parent_class; +}; + +static void + gst_player_wrapped_video_renderer_interface_init + (GstPlayVideoRendererInterface * iface); + +G_DEFINE_TYPE_WITH_CODE (GstPlayerWrappedVideoRenderer, + gst_player_wrapped_video_renderer, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GST_TYPE_PLAY_VIDEO_RENDERER, + gst_player_wrapped_video_renderer_interface_init)); + +static void +gst_player_wrapped_video_renderer_finalize (GObject * object) +{ + GstPlayerWrappedVideoRenderer *self = + GST_PLAYER_WRAPPED_VIDEO_RENDERER (object); + + g_clear_object (&self->renderer); + + G_OBJECT_CLASS + (gst_player_wrapped_video_renderer_parent_class)->finalize (object); +} + +static void +gst_player_wrapped_video_renderer_class_init (GstPlayerWrappedVideoRendererClass + * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = gst_player_wrapped_video_renderer_finalize; +} + +static void +gst_player_wrapped_video_renderer_init (GstPlayerWrappedVideoRenderer * self) +{ +} + +static GstElement * +gst_player_wrapped_video_renderer_create_video_sink (GstPlayVideoRenderer * + iface, GstPlay * player) +{ + GstPlayerWrappedVideoRenderer *self = + GST_PLAYER_WRAPPED_VIDEO_RENDERER (iface); + + return gst_player_video_renderer_create_video_sink (self->renderer, + self->player); +} + +static void +gst_player_wrapped_video_renderer_interface_init (GstPlayVideoRendererInterface + * iface) +{ + iface->create_video_sink = + gst_player_wrapped_video_renderer_create_video_sink; +} + +GstPlayerVideoRenderer * +gst_player_wrapped_video_renderer_new (GstPlayerVideoRenderer * renderer, + GstPlayer * player) +{ + GstPlayerWrappedVideoRenderer *self = + g_object_new (GST_TYPE_PLAYER_WRAPPED_VIDEO_RENDERER, + NULL); + self->renderer = g_object_ref (renderer); + self->player = player; + return (GstPlayerVideoRenderer *) self; +} diff --git a/gst-libs/gst/player/gstplayer.c b/gst-libs/gst/player/gstplayer.c index 07b7ae2..a1eea9f 100644 --- a/gst-libs/gst/player/gstplayer.c +++ b/gst-libs/gst/player/gstplayer.c @@ -26,6 +26,8 @@ * @symbols: * - GstPlayer * + * Starting from GStreamer 1.20, application developers are strongly advised to migrate to #GstPlay. + * #GstPlayer will be deprecated in 1.20 and most likely removed by 1.24. */ /* TODO: @@ -49,8 +51,10 @@ #include "gstplayer-signal-dispatcher-private.h" #include "gstplayer-video-renderer-private.h" #include "gstplayer-media-info-private.h" +#include "gstplayer-wrapped-video-renderer-private.h" #include +#include #include #include #include @@ -144,67 +148,15 @@ enum SIGNAL_LAST }; -enum -{ - GST_PLAY_FLAG_VIDEO = (1 << 0), - GST_PLAY_FLAG_AUDIO = (1 << 1), - GST_PLAY_FLAG_SUBTITLE = (1 << 2), - GST_PLAY_FLAG_VIS = (1 << 3) -}; - struct _GstPlayer { GstObject parent; - GstPlayerVideoRenderer *video_renderer; - GstPlayerSignalDispatcher *signal_dispatcher; + GstPlay *play; + GstPlaySignalAdapter *signal_adapter; - gchar *uri; - gchar *redirect_uri; - gchar *suburi; - - GThread *thread; - GMutex lock; - GCond cond; - GMainContext *context; - GMainLoop *loop; - - GstElement *playbin; - GstBus *bus; - GstState target_state, current_state; - gboolean is_live, is_eos; - GSource *tick_source, *ready_timeout_source; - GstClockTime cached_duration; - - gdouble rate; - - GstPlayerState app_state; - gint buffering; - - GstTagList *global_tags; - GstPlayerMediaInfo *media_info; - - GstElement *current_vis_element; - - GstStructure *config; - - /* Protected by lock */ - gboolean seek_pending; /* Only set from main context */ - GstClockTime last_seek_time; /* Only set from main context */ - GSource *seek_source; - GstClockTime seek_position; - /* If TRUE, all signals are inhibited except the - * state-changed:GST_PLAYER_STATE_STOPPED/PAUSED. This ensures that no signal - * is emitted after gst_player_stop/pause() has been called by the user. */ - gboolean inhibit_sigs; - - /* For playbin3 */ - gboolean use_playbin3; - GstStreamCollection *collection; - gchar *video_sid; - gchar *audio_sid; - gchar *subtitle_sid; - gulong stream_notify_id; + /* legacy */ + GstPlayerSignalDispatcher *signal_dispatcher; }; struct _GstPlayerClass @@ -218,92 +170,16 @@ G_DEFINE_TYPE (GstPlayer, gst_player, GST_TYPE_OBJECT); static guint signals[SIGNAL_LAST] = { 0, }; static GParamSpec *param_specs[PROP_LAST] = { NULL, }; -static void gst_player_dispose (GObject * object); static void gst_player_finalize (GObject * object); static void gst_player_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_player_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_player_constructed (GObject * object); - -static gpointer gst_player_main (gpointer data); - -static void gst_player_seek_internal_locked (GstPlayer * self); -static void gst_player_stop_internal (GstPlayer * self, gboolean transient); -static gboolean gst_player_pause_internal (gpointer user_data); -static gboolean gst_player_play_internal (gpointer user_data); -static gboolean gst_player_seek_internal (gpointer user_data); -static void gst_player_set_rate_internal (GstPlayer * self); -static void change_state (GstPlayer * self, GstPlayerState state); - -static GstPlayerMediaInfo *gst_player_media_info_create (GstPlayer * self); - -static void gst_player_streams_info_create (GstPlayer * self, - GstPlayerMediaInfo * media_info, const gchar * prop, GType type); -static void gst_player_stream_info_update (GstPlayer * self, - GstPlayerStreamInfo * s); -static void gst_player_stream_info_update_tags_and_caps (GstPlayer * self, - GstPlayerStreamInfo * s); -static GstPlayerStreamInfo *gst_player_stream_info_find (GstPlayerMediaInfo * - media_info, GType type, gint stream_index); -static GstPlayerStreamInfo *gst_player_stream_info_get_current (GstPlayer * - self, const gchar * prop, GType type); - -static void gst_player_video_info_update (GstPlayer * self, - GstPlayerStreamInfo * stream_info); -static void gst_player_audio_info_update (GstPlayer * self, - GstPlayerStreamInfo * stream_info); -static void gst_player_subtitle_info_update (GstPlayer * self, - GstPlayerStreamInfo * stream_info); - -/* For playbin3 */ -static void gst_player_streams_info_create_from_collection (GstPlayer * self, - GstPlayerMediaInfo * media_info, GstStreamCollection * collection); -static void gst_player_stream_info_update_from_stream (GstPlayer * self, - GstPlayerStreamInfo * s, GstStream * stream); -static GstPlayerStreamInfo *gst_player_stream_info_find_from_stream_id - (GstPlayerMediaInfo * media_info, const gchar * stream_id); -static GstPlayerStreamInfo *gst_player_stream_info_get_current_from_stream_id - (GstPlayer * self, const gchar * stream_id, GType type); -static void stream_notify_cb (GstStreamCollection * collection, - GstStream * stream, GParamSpec * pspec, GstPlayer * self); - -static void emit_media_info_updated_signal (GstPlayer * self); - -static void *get_title (GstTagList * tags); -static void *get_container_format (GstTagList * tags); -static void *get_from_tags (GstPlayer * self, GstPlayerMediaInfo * media_info, - void *(*func) (GstTagList *)); -static void *get_cover_sample (GstTagList * tags); - -static void remove_seek_source (GstPlayer * self); static void -gst_player_init (GstPlayer * self) +gst_player_init (G_GNUC_UNUSED GstPlayer * self) { - GST_TRACE_OBJECT (self, "Initializing"); - - self = gst_player_get_instance_private (self); - - g_mutex_init (&self->lock); - g_cond_init (&self->cond); - self->context = g_main_context_new (); - self->loop = g_main_loop_new (self->context, FALSE); - - /* *INDENT-OFF* */ - self->config = gst_structure_new_id (QUARK_CONFIG, - CONFIG_QUARK (POSITION_INTERVAL_UPDATE), G_TYPE_UINT, DEFAULT_POSITION_UPDATE_INTERVAL_MS, - CONFIG_QUARK (ACCURATE_SEEK), G_TYPE_BOOLEAN, FALSE, - NULL); - /* *INDENT-ON* */ - - self->seek_pending = FALSE; - self->seek_position = GST_CLOCK_TIME_NONE; - self->last_seek_time = GST_CLOCK_TIME_NONE; - self->inhibit_sigs = FALSE; - - GST_TRACE_OBJECT (self, "Initialized"); } static void @@ -330,15 +206,13 @@ gst_player_class_init (GstPlayerClass * klass) gobject_class->set_property = gst_player_set_property; gobject_class->get_property = gst_player_get_property; - gobject_class->dispose = gst_player_dispose; gobject_class->finalize = gst_player_finalize; - gobject_class->constructed = gst_player_constructed; param_specs[PROP_VIDEO_RENDERER] = g_param_spec_object ("video-renderer", "Video Renderer", "Video renderer to use for rendering videos", GST_TYPE_PLAYER_VIDEO_RENDERER, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); param_specs[PROP_SIGNAL_DISPATCHER] = g_param_spec_object ("signal-dispatcher", @@ -495,260 +369,33 @@ gst_player_class_init (GstPlayerClass * klass) } static void -gst_player_dispose (GObject * object) -{ - GstPlayer *self = GST_PLAYER (object); - - GST_TRACE_OBJECT (self, "Stopping main thread"); - - if (self->loop) { - g_main_loop_quit (self->loop); - - if (self->thread != g_thread_self ()) - g_thread_join (self->thread); - else - g_thread_unref (self->thread); - self->thread = NULL; - - g_main_loop_unref (self->loop); - self->loop = NULL; - - g_main_context_unref (self->context); - self->context = NULL; - } - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void gst_player_finalize (GObject * object) { GstPlayer *self = GST_PLAYER (object); GST_TRACE_OBJECT (self, "Finalizing"); - g_free (self->uri); - g_free (self->redirect_uri); - g_free (self->suburi); - g_free (self->video_sid); - g_free (self->audio_sid); - g_free (self->subtitle_sid); - if (self->global_tags) - gst_tag_list_unref (self->global_tags); - if (self->video_renderer) - g_object_unref (self->video_renderer); if (self->signal_dispatcher) g_object_unref (self->signal_dispatcher); - if (self->current_vis_element) - gst_object_unref (self->current_vis_element); - if (self->config) - gst_structure_free (self->config); - if (self->collection) - gst_object_unref (self->collection); - g_mutex_clear (&self->lock); - g_cond_clear (&self->cond); + if (self->play) + gst_object_unref (self->play); G_OBJECT_CLASS (parent_class)->finalize (object); } static void -gst_player_constructed (GObject * object) -{ - GstPlayer *self = GST_PLAYER (object); - - GST_TRACE_OBJECT (self, "Constructed"); - - g_mutex_lock (&self->lock); - self->thread = g_thread_new ("GstPlayer", gst_player_main, self); - while (!self->loop || !g_main_loop_is_running (self->loop)) - g_cond_wait (&self->cond, &self->lock); - g_mutex_unlock (&self->lock); - - G_OBJECT_CLASS (parent_class)->constructed (object); -} - -typedef struct -{ - GstPlayer *player; - gchar *uri; -} UriLoadedSignalData; - -static void -uri_loaded_dispatch (gpointer user_data) -{ - UriLoadedSignalData *data = user_data; - - g_signal_emit (data->player, signals[SIGNAL_URI_LOADED], 0, data->uri); -} - -static void -uri_loaded_signal_data_free (UriLoadedSignalData * data) -{ - g_object_unref (data->player); - g_free (data->uri); - g_free (data); -} - -static gboolean -gst_player_set_uri_internal (gpointer user_data) -{ - GstPlayer *self = user_data; - - gst_player_stop_internal (self, FALSE); - - g_mutex_lock (&self->lock); - - GST_DEBUG_OBJECT (self, "Changing URI to '%s'", GST_STR_NULL (self->uri)); - - g_object_set (self->playbin, "uri", self->uri, NULL); - - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_URI_LOADED], 0, NULL, NULL, NULL) != 0) { - UriLoadedSignalData *data = g_new (UriLoadedSignalData, 1); - - data->player = g_object_ref (self); - data->uri = g_strdup (self->uri); - gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self, - uri_loaded_dispatch, data, - (GDestroyNotify) uri_loaded_signal_data_free); - } - - g_object_set (self->playbin, "suburi", NULL, NULL); - - g_mutex_unlock (&self->lock); - - return G_SOURCE_REMOVE; -} - -static gboolean -gst_player_set_suburi_internal (gpointer user_data) -{ - GstPlayer *self = user_data; - GstClockTime position; - GstState target_state; - - /* save the state and position */ - target_state = self->target_state; - position = gst_player_get_position (self); - - gst_player_stop_internal (self, TRUE); - g_mutex_lock (&self->lock); - - GST_DEBUG_OBJECT (self, "Changing SUBURI to '%s'", - GST_STR_NULL (self->suburi)); - - g_object_set (self->playbin, "suburi", self->suburi, NULL); - - g_mutex_unlock (&self->lock); - - /* restore state and position */ - if (position != GST_CLOCK_TIME_NONE) - gst_player_seek (self, position); - if (target_state == GST_STATE_PAUSED) - gst_player_pause_internal (self); - else if (target_state == GST_STATE_PLAYING) - gst_player_play_internal (self); - - return G_SOURCE_REMOVE; -} - -static void -gst_player_set_rate_internal (GstPlayer * self) -{ - self->seek_position = gst_player_get_position (self); - - /* If there is no seek being dispatch to the main context currently do that, - * otherwise we just updated the rate so that it will be taken by - * the seek handler from the main context instead of the old one. - */ - if (!self->seek_source) { - /* If no seek is pending then create new seek source */ - if (!self->seek_pending) { - self->seek_source = g_idle_source_new (); - g_source_set_callback (self->seek_source, - (GSourceFunc) gst_player_seek_internal, self, NULL); - g_source_attach (self->seek_source, self->context); - } - } -} - -static void gst_player_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstPlayer *self = GST_PLAYER (object); switch (prop_id) { - case PROP_VIDEO_RENDERER: - self->video_renderer = g_value_dup_object (value); - break; case PROP_SIGNAL_DISPATCHER: self->signal_dispatcher = g_value_dup_object (value); break; - case PROP_URI:{ - g_mutex_lock (&self->lock); - g_free (self->uri); - g_free (self->redirect_uri); - self->redirect_uri = NULL; - - g_free (self->suburi); - self->suburi = NULL; - - self->uri = g_value_dup_string (value); - GST_DEBUG_OBJECT (self, "Set uri=%s", self->uri); - g_mutex_unlock (&self->lock); - - g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT, - gst_player_set_uri_internal, self, NULL); - break; - } - case PROP_SUBURI:{ - g_mutex_lock (&self->lock); - g_free (self->suburi); - - self->suburi = g_value_dup_string (value); - GST_DEBUG_OBJECT (self, "Set suburi=%s", self->suburi); - g_mutex_unlock (&self->lock); - - g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT, - gst_player_set_suburi_internal, self, NULL); - break; - } - case PROP_VOLUME: - GST_DEBUG_OBJECT (self, "Set volume=%lf", g_value_get_double (value)); - g_object_set_property (G_OBJECT (self->playbin), "volume", value); - break; - case PROP_RATE: - g_mutex_lock (&self->lock); - self->rate = g_value_get_double (value); - GST_DEBUG_OBJECT (self, "Set rate=%lf", g_value_get_double (value)); - gst_player_set_rate_internal (self); - g_mutex_unlock (&self->lock); - break; - case PROP_MUTE: - GST_DEBUG_OBJECT (self, "Set mute=%d", g_value_get_boolean (value)); - g_object_set_property (G_OBJECT (self->playbin), "mute", value); - break; - case PROP_VIDEO_MULTIVIEW_MODE: - GST_DEBUG_OBJECT (self, "Set multiview mode=%u", - g_value_get_enum (value)); - g_object_set_property (G_OBJECT (self->playbin), "video-multiview-mode", - value); - break; - case PROP_VIDEO_MULTIVIEW_FLAGS: - GST_DEBUG_OBJECT (self, "Set multiview flags=%x", - g_value_get_flags (value)); - g_object_set_property (G_OBJECT (self->playbin), "video-multiview-flags", - value); - break; - case PROP_AUDIO_VIDEO_OFFSET: - g_object_set_property (G_OBJECT (self->playbin), "av-offset", value); - break; - case PROP_SUBTITLE_VIDEO_OFFSET: - g_object_set_property (G_OBJECT (self->playbin), "text-offset", value); - break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + g_object_set_property (G_OBJECT (self->play), + g_param_spec_get_name (pspec), value); break; } } @@ -760,2274 +407,141 @@ gst_player_get_property (GObject * object, guint prop_id, GstPlayer *self = GST_PLAYER (object); switch (prop_id) { - case PROP_URI: - g_mutex_lock (&self->lock); - g_value_set_string (value, self->uri); - g_mutex_unlock (&self->lock); - break; - case PROP_SUBURI: - g_mutex_lock (&self->lock); - g_value_set_string (value, self->suburi); - g_mutex_unlock (&self->lock); - GST_DEBUG_OBJECT (self, "Returning suburi=%s", - g_value_get_string (value)); - break; - case PROP_POSITION:{ - gint64 position = GST_CLOCK_TIME_NONE; - - gst_element_query_position (self->playbin, GST_FORMAT_TIME, &position); - g_value_set_uint64 (value, position); - GST_TRACE_OBJECT (self, "Returning position=%" GST_TIME_FORMAT, - GST_TIME_ARGS (g_value_get_uint64 (value))); - break; - } - case PROP_DURATION:{ - g_value_set_uint64 (value, self->cached_duration); - GST_TRACE_OBJECT (self, "Returning duration=%" GST_TIME_FORMAT, - GST_TIME_ARGS (g_value_get_uint64 (value))); - break; - } - case PROP_MEDIA_INFO:{ - GstPlayerMediaInfo *media_info = gst_player_get_media_info (self); - g_value_take_object (value, media_info); - break; - } - case PROP_CURRENT_AUDIO_TRACK:{ - GstPlayerAudioInfo *audio_info = - gst_player_get_current_audio_track (self); - g_value_take_object (value, audio_info); - break; - } - case PROP_CURRENT_VIDEO_TRACK:{ - GstPlayerVideoInfo *video_info = - gst_player_get_current_video_track (self); - g_value_take_object (value, video_info); - break; - } - case PROP_CURRENT_SUBTITLE_TRACK:{ - GstPlayerSubtitleInfo *subtitle_info = - gst_player_get_current_subtitle_track (self); - g_value_take_object (value, subtitle_info); + case PROP_MEDIA_INFO: + g_value_take_object (value, gst_player_get_media_info (self)); break; - } - case PROP_VOLUME: - g_object_get_property (G_OBJECT (self->playbin), "volume", value); - GST_TRACE_OBJECT (self, "Returning volume=%lf", - g_value_get_double (value)); + case PROP_CURRENT_AUDIO_TRACK: + g_value_take_object (value, gst_player_get_current_audio_track (self)); break; - case PROP_RATE: - g_mutex_lock (&self->lock); - g_value_set_double (value, self->rate); - g_mutex_unlock (&self->lock); + case PROP_CURRENT_VIDEO_TRACK: + g_value_take_object (value, gst_player_get_current_video_track (self)); break; - case PROP_MUTE: - g_object_get_property (G_OBJECT (self->playbin), "mute", value); - GST_TRACE_OBJECT (self, "Returning mute=%d", g_value_get_boolean (value)); - break; - case PROP_PIPELINE: - g_value_set_object (value, self->playbin); - break; - case PROP_VIDEO_MULTIVIEW_MODE:{ - g_object_get_property (G_OBJECT (self->playbin), "video-multiview-mode", - value); - GST_TRACE_OBJECT (self, "Return multiview mode=%d", - g_value_get_enum (value)); - break; - } - case PROP_VIDEO_MULTIVIEW_FLAGS:{ - g_object_get_property (G_OBJECT (self->playbin), "video-multiview-flags", - value); - GST_TRACE_OBJECT (self, "Return multiview flags=%x", - g_value_get_flags (value)); - break; - } - case PROP_AUDIO_VIDEO_OFFSET: - g_object_get_property (G_OBJECT (self->playbin), "av-offset", value); - break; - case PROP_SUBTITLE_VIDEO_OFFSET: - g_object_get_property (G_OBJECT (self->playbin), "text-offset", value); + case PROP_CURRENT_SUBTITLE_TRACK: + g_value_take_object (value, gst_player_get_current_subtitle_track (self)); break; default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + g_object_get_property (G_OBJECT (self->play), + g_param_spec_get_name (pspec), value); break; } } -static gboolean -main_loop_running_cb (gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - - GST_TRACE_OBJECT (self, "Main loop running now"); - - g_mutex_lock (&self->lock); - g_cond_signal (&self->cond); - g_mutex_unlock (&self->lock); - - return G_SOURCE_REMOVE; -} - -typedef struct -{ - GstPlayer *player; - GstPlayerState state; -} StateChangedSignalData; - -static void -state_changed_dispatch (gpointer user_data) -{ - StateChangedSignalData *data = user_data; - - if (data->player->inhibit_sigs && data->state != GST_PLAYER_STATE_STOPPED - && data->state != GST_PLAYER_STATE_PAUSED) - return; - - g_signal_emit (data->player, signals[SIGNAL_STATE_CHANGED], 0, data->state); -} - -static void -state_changed_signal_data_free (StateChangedSignalData * data) -{ - g_object_unref (data->player); - g_free (data); -} - -static void -change_state (GstPlayer * self, GstPlayerState state) -{ - if (state == self->app_state) - return; - - GST_DEBUG_OBJECT (self, "Changing app state from %s to %s", - gst_player_state_get_name (self->app_state), - gst_player_state_get_name (state)); - self->app_state = state; - - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_STATE_CHANGED], 0, NULL, NULL, NULL) != 0) { - StateChangedSignalData *data = g_new (StateChangedSignalData, 1); - - data->player = g_object_ref (self); - data->state = state; - gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self, - state_changed_dispatch, data, - (GDestroyNotify) state_changed_signal_data_free); - } -} - -typedef struct -{ - GstPlayer *player; - GstClockTime position; -} PositionUpdatedSignalData; - -static void -position_updated_dispatch (gpointer user_data) -{ - PositionUpdatedSignalData *data = user_data; - - if (data->player->inhibit_sigs) - return; - - if (data->player->target_state >= GST_STATE_PAUSED) { - g_signal_emit (data->player, signals[SIGNAL_POSITION_UPDATED], 0, - data->position); - g_object_notify_by_pspec (G_OBJECT (data->player), - param_specs[PROP_POSITION]); - } -} - -static void -position_updated_signal_data_free (PositionUpdatedSignalData * data) -{ - g_object_unref (data->player); - g_free (data); -} - -static gboolean -tick_cb (gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - gint64 position; - - if (self->target_state >= GST_STATE_PAUSED - && gst_element_query_position (self->playbin, GST_FORMAT_TIME, - &position)) { - GST_LOG_OBJECT (self, "Position %" GST_TIME_FORMAT, - GST_TIME_ARGS (position)); - - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_POSITION_UPDATED], 0, NULL, NULL, NULL) != 0) { - PositionUpdatedSignalData *data = g_new (PositionUpdatedSignalData, 1); - - data->player = g_object_ref (self); - data->position = position; - gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self, - position_updated_dispatch, data, - (GDestroyNotify) position_updated_signal_data_free); - } - } - - return G_SOURCE_CONTINUE; -} - -static void -add_tick_source (GstPlayer * self) -{ - guint position_update_interval_ms; - - if (self->tick_source) - return; - - position_update_interval_ms = - gst_player_config_get_position_update_interval (self->config); - if (!position_update_interval_ms) - return; - - self->tick_source = g_timeout_source_new (position_update_interval_ms); - g_source_set_callback (self->tick_source, (GSourceFunc) tick_cb, self, NULL); - g_source_attach (self->tick_source, self->context); -} - -static void -remove_tick_source (GstPlayer * self) -{ - if (!self->tick_source) - return; - - g_source_destroy (self->tick_source); - g_source_unref (self->tick_source); - self->tick_source = NULL; -} - -static gboolean -ready_timeout_cb (gpointer user_data) -{ - GstPlayer *self = user_data; - - if (self->target_state <= GST_STATE_READY) { - GST_DEBUG_OBJECT (self, "Setting pipeline to NULL state"); - self->target_state = GST_STATE_NULL; - self->current_state = GST_STATE_NULL; - gst_element_set_state (self->playbin, GST_STATE_NULL); - } - - return G_SOURCE_REMOVE; -} - -static void -add_ready_timeout_source (GstPlayer * self) -{ - if (self->ready_timeout_source) - return; - - self->ready_timeout_source = g_timeout_source_new_seconds (60); - g_source_set_callback (self->ready_timeout_source, - (GSourceFunc) ready_timeout_cb, self, NULL); - g_source_attach (self->ready_timeout_source, self->context); -} - -static void -remove_ready_timeout_source (GstPlayer * self) -{ - if (!self->ready_timeout_source) - return; - - g_source_destroy (self->ready_timeout_source); - g_source_unref (self->ready_timeout_source); - self->ready_timeout_source = NULL; -} - -typedef struct -{ - GstPlayer *player; - GError *err; -} ErrorSignalData; - -static void -error_dispatch (gpointer user_data) -{ - ErrorSignalData *data = user_data; - - if (data->player->inhibit_sigs) - return; - - g_signal_emit (data->player, signals[SIGNAL_ERROR], 0, data->err); -} - -static void -free_error_signal_data (ErrorSignalData * data) -{ - g_object_unref (data->player); - g_clear_error (&data->err); - g_free (data); -} - -static void -emit_error (GstPlayer * self, GError * err) -{ - GST_ERROR_OBJECT (self, "Error: %s (%s, %d)", err->message, - g_quark_to_string (err->domain), err->code); - - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_ERROR], 0, NULL, NULL, NULL) != 0) { - ErrorSignalData *data = g_new (ErrorSignalData, 1); - - data->player = g_object_ref (self); - data->err = g_error_copy (err); - gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self, - error_dispatch, data, (GDestroyNotify) free_error_signal_data); - } - - g_error_free (err); - - remove_tick_source (self); - remove_ready_timeout_source (self); - - self->target_state = GST_STATE_NULL; - self->current_state = GST_STATE_NULL; - self->is_live = FALSE; - self->is_eos = FALSE; - gst_element_set_state (self->playbin, GST_STATE_NULL); - change_state (self, GST_PLAYER_STATE_STOPPED); - self->buffering = 100; - - g_mutex_lock (&self->lock); - if (self->media_info) { - g_object_unref (self->media_info); - self->media_info = NULL; - } - - if (self->global_tags) { - gst_tag_list_unref (self->global_tags); - self->global_tags = NULL; - } - - self->seek_pending = FALSE; - remove_seek_source (self); - self->seek_position = GST_CLOCK_TIME_NONE; - self->last_seek_time = GST_CLOCK_TIME_NONE; - g_mutex_unlock (&self->lock); -} - -static void -dump_dot_file (GstPlayer * self, const gchar * name) -{ - gchar *full_name; - - full_name = g_strdup_printf ("gst-player.%p.%s", self, name); - - GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (self->playbin), - GST_DEBUG_GRAPH_SHOW_ALL, full_name); - - g_free (full_name); -} - -typedef struct -{ - GstPlayer *player; - GError *err; -} WarningSignalData; - -static void -warning_dispatch (gpointer user_data) -{ - WarningSignalData *data = user_data; - - if (data->player->inhibit_sigs) - return; - - g_signal_emit (data->player, signals[SIGNAL_WARNING], 0, data->err); -} - -static void -free_warning_signal_data (WarningSignalData * data) -{ - g_object_unref (data->player); - g_clear_error (&data->err); - g_free (data); -} - -static void -emit_warning (GstPlayer * self, GError * err) +static gpointer +gst_player_init_once (G_GNUC_UNUSED gpointer user_data) { - GST_ERROR_OBJECT (self, "Warning: %s (%s, %d)", err->message, - g_quark_to_string (err->domain), err->code); - - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_WARNING], 0, NULL, NULL, NULL) != 0) { - WarningSignalData *data = g_new (WarningSignalData, 1); - - data->player = g_object_ref (self); - data->err = g_error_copy (err); - gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self, - warning_dispatch, data, (GDestroyNotify) free_warning_signal_data); - } + gst_init (NULL, NULL); - g_error_free (err); -} + GST_DEBUG_CATEGORY_INIT (gst_player_debug, "gst-player", 0, "GstPlayer"); + gst_player_error_quark (); -static void -error_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - GError *err, *player_err; - gchar *name, *debug, *message, *full_message; - - dump_dot_file (self, "error"); - - gst_message_parse_error (msg, &err, &debug); - - name = gst_object_get_path_string (msg->src); - message = gst_error_get_message (err->domain, err->code); - - if (debug) - full_message = - g_strdup_printf ("Error from element %s: %s\n%s\n%s", name, message, - err->message, debug); - else - full_message = - g_strdup_printf ("Error from element %s: %s\n%s", name, message, - err->message); - - GST_ERROR_OBJECT (self, "ERROR: from element %s: %s", name, err->message); - if (debug != NULL) - GST_ERROR_OBJECT (self, "Additional debug info: %s", debug); - - player_err = - g_error_new_literal (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED, - full_message); - emit_error (self, player_err); - - g_clear_error (&err); - g_free (debug); - g_free (name); - g_free (full_message); - g_free (message); + return NULL; } static void -warning_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) +uri_loaded_cb (GstPlaySignalAdapter * adapter, const gchar * uri, + GstPlayer * self) { - GstPlayer *self = GST_PLAYER (user_data); - GError *err, *player_err; - gchar *name, *debug, *message, *full_message; - - dump_dot_file (self, "warning"); - - gst_message_parse_warning (msg, &err, &debug); - - name = gst_object_get_path_string (msg->src); - message = gst_error_get_message (err->domain, err->code); - - if (debug) - full_message = - g_strdup_printf ("Warning from element %s: %s\n%s\n%s", name, message, - err->message, debug); - else - full_message = - g_strdup_printf ("Warning from element %s: %s\n%s", name, message, - err->message); - - GST_WARNING_OBJECT (self, "WARNING: from element %s: %s", name, err->message); - if (debug != NULL) - GST_WARNING_OBJECT (self, "Additional debug info: %s", debug); - - player_err = - g_error_new_literal (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED, - full_message); - emit_warning (self, player_err); - - g_clear_error (&err); - g_free (debug); - g_free (name); - g_free (full_message); - g_free (message); + g_signal_emit (self, signals[SIGNAL_URI_LOADED], 0, uri); } static void -eos_dispatch (gpointer user_data) +position_updated_cb (GstPlaySignalAdapter * adapter, GstClockTime position, + GstPlayer * self) { - GstPlayer *player = user_data; - - if (player->inhibit_sigs) - return; - - g_signal_emit (player, signals[SIGNAL_END_OF_STREAM], 0); + g_signal_emit (self, signals[SIGNAL_POSITION_UPDATED], 0, position); } static void -eos_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg, - gpointer user_data) +duration_changed_cb (GstPlaySignalAdapter * adapter, GstClockTime duraton, + GstPlayer * self) { - GstPlayer *self = GST_PLAYER (user_data); - - GST_DEBUG_OBJECT (self, "End of stream"); - - tick_cb (self); - remove_tick_source (self); - - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_END_OF_STREAM], 0, NULL, NULL, NULL) != 0) { - gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self, - eos_dispatch, g_object_ref (self), (GDestroyNotify) g_object_unref); - } - change_state (self, GST_PLAYER_STATE_STOPPED); - self->buffering = 100; - self->is_eos = TRUE; + g_signal_emit (self, signals[SIGNAL_DURATION_CHANGED], 0, duraton); } -typedef struct -{ - GstPlayer *player; - gint percent; -} BufferingSignalData; - static void -buffering_dispatch (gpointer user_data) +state_changed_cb (GstPlaySignalAdapter * adapter, GstPlayState state, + GstPlayer * self) { - BufferingSignalData *data = user_data; - - if (data->player->inhibit_sigs) - return; - - if (data->player->target_state >= GST_STATE_PAUSED) { - g_signal_emit (data->player, signals[SIGNAL_BUFFERING], 0, data->percent); + GstPlayerState s = GST_PLAYER_STATE_BUFFERING; + switch (state) { + case GST_PLAY_STATE_BUFFERING: + s = GST_PLAYER_STATE_BUFFERING; + break; + case GST_PLAY_STATE_PAUSED: + s = GST_PLAYER_STATE_PAUSED; + break; + case GST_PLAY_STATE_PLAYING: + s = GST_PLAYER_STATE_PLAYING; + break; + case GST_PLAY_STATE_STOPPED: + s = GST_PLAYER_STATE_STOPPED; + break; } + g_signal_emit (self, signals[SIGNAL_STATE_CHANGED], 0, s); } static void -buffering_signal_data_free (BufferingSignalData * data) -{ - g_object_unref (data->player); - g_free (data); -} - -static void -buffering_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - gint percent; - - if (self->target_state < GST_STATE_PAUSED) - return; - if (self->is_live) - return; - - gst_message_parse_buffering (msg, &percent); - GST_LOG_OBJECT (self, "Buffering %d%%", percent); - - if (percent < 100 && self->target_state >= GST_STATE_PAUSED) { - GstStateChangeReturn state_ret; - - GST_DEBUG_OBJECT (self, "Waiting for buffering to finish"); - state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED); - - if (state_ret == GST_STATE_CHANGE_FAILURE) { - emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED, - "Failed to handle buffering")); - return; - } - - change_state (self, GST_PLAYER_STATE_BUFFERING); - } - - if (self->buffering != percent) { - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_BUFFERING], 0, NULL, NULL, NULL) != 0) { - BufferingSignalData *data = g_new (BufferingSignalData, 1); - - data->player = g_object_ref (self); - data->percent = percent; - gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self, - buffering_dispatch, data, - (GDestroyNotify) buffering_signal_data_free); - } - - self->buffering = percent; - } - - - g_mutex_lock (&self->lock); - if (percent == 100 && (self->seek_position != GST_CLOCK_TIME_NONE || - self->seek_pending)) { - g_mutex_unlock (&self->lock); - - GST_DEBUG_OBJECT (self, "Buffering finished - seek pending"); - } else if (percent == 100 && self->target_state >= GST_STATE_PLAYING - && self->current_state >= GST_STATE_PAUSED) { - GstStateChangeReturn state_ret; - - g_mutex_unlock (&self->lock); - - GST_DEBUG_OBJECT (self, "Buffering finished - going to PLAYING"); - state_ret = gst_element_set_state (self->playbin, GST_STATE_PLAYING); - /* Application state change is happening when the state change happened */ - if (state_ret == GST_STATE_CHANGE_FAILURE) - emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED, - "Failed to handle buffering")); - } else if (percent == 100 && self->target_state >= GST_STATE_PAUSED) { - g_mutex_unlock (&self->lock); - - GST_DEBUG_OBJECT (self, "Buffering finished - staying PAUSED"); - change_state (self, GST_PLAYER_STATE_PAUSED); - } else { - g_mutex_unlock (&self->lock); - } -} - -static void -clock_lost_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg, - gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - GstStateChangeReturn state_ret; - - GST_DEBUG_OBJECT (self, "Clock lost"); - if (self->target_state >= GST_STATE_PLAYING) { - state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED); - if (state_ret != GST_STATE_CHANGE_FAILURE) - state_ret = gst_element_set_state (self->playbin, GST_STATE_PLAYING); - - if (state_ret == GST_STATE_CHANGE_FAILURE) - emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED, - "Failed to handle clock loss")); - } -} - -typedef struct -{ - GstPlayer *player; - gint width, height; -} VideoDimensionsChangedSignalData; - -static void -video_dimensions_changed_dispatch (gpointer user_data) -{ - VideoDimensionsChangedSignalData *data = user_data; - - if (data->player->inhibit_sigs) - return; - - if (data->player->target_state >= GST_STATE_PAUSED) { - g_signal_emit (data->player, signals[SIGNAL_VIDEO_DIMENSIONS_CHANGED], 0, - data->width, data->height); - } -} - -static void -video_dimensions_changed_signal_data_free (VideoDimensionsChangedSignalData * - data) -{ - g_object_unref (data->player); - g_free (data); -} - -static void -check_video_dimensions_changed (GstPlayer * self) -{ - GstElement *video_sink; - GstPad *video_sink_pad; - GstCaps *caps; - GstVideoInfo info; - gint width = 0, height = 0; - - g_object_get (self->playbin, "video-sink", &video_sink, NULL); - if (!video_sink) - goto out; - - video_sink_pad = gst_element_get_static_pad (video_sink, "sink"); - if (!video_sink_pad) { - gst_object_unref (video_sink); - goto out; - } - - caps = gst_pad_get_current_caps (video_sink_pad); - - if (caps) { - if (gst_video_info_from_caps (&info, caps)) { - info.width = info.width * info.par_n / info.par_d; - - GST_DEBUG_OBJECT (self, "Video dimensions changed: %dx%d", info.width, - info.height); - width = info.width; - height = info.height; - } - - gst_caps_unref (caps); - } - gst_object_unref (video_sink_pad); - gst_object_unref (video_sink); - -out: - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_VIDEO_DIMENSIONS_CHANGED], 0, NULL, NULL, NULL) != 0) { - VideoDimensionsChangedSignalData *data = - g_new (VideoDimensionsChangedSignalData, 1); - - data->player = g_object_ref (self); - data->width = width; - data->height = height; - gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self, - video_dimensions_changed_dispatch, data, - (GDestroyNotify) video_dimensions_changed_signal_data_free); - } -} - -static void -notify_caps_cb (G_GNUC_UNUSED GObject * object, - G_GNUC_UNUSED GParamSpec * pspec, gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - - check_video_dimensions_changed (self); -} - -typedef struct -{ - GstPlayer *player; - GstClockTime duration; -} DurationChangedSignalData; - -static void -duration_changed_dispatch (gpointer user_data) -{ - DurationChangedSignalData *data = user_data; - - if (data->player->inhibit_sigs) - return; - - if (data->player->target_state >= GST_STATE_PAUSED) { - g_signal_emit (data->player, signals[SIGNAL_DURATION_CHANGED], 0, - data->duration); - g_object_notify_by_pspec (G_OBJECT (data->player), - param_specs[PROP_DURATION]); - } -} - -static void -duration_changed_signal_data_free (DurationChangedSignalData * data) -{ - g_object_unref (data->player); - g_free (data); -} - -static void -emit_duration_changed (GstPlayer * self, GstClockTime duration) -{ - gboolean updated = FALSE; - - if (self->cached_duration == duration) - return; - - GST_DEBUG_OBJECT (self, "Duration changed %" GST_TIME_FORMAT, - GST_TIME_ARGS (duration)); - - self->cached_duration = duration; - g_mutex_lock (&self->lock); - if (self->media_info) { - self->media_info->duration = duration; - updated = TRUE; - } - g_mutex_unlock (&self->lock); - if (updated) { - emit_media_info_updated_signal (self); - } - - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_DURATION_CHANGED], 0, NULL, NULL, NULL) != 0) { - DurationChangedSignalData *data = g_new (DurationChangedSignalData, 1); - - data->player = g_object_ref (self); - data->duration = duration; - gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self, - duration_changed_dispatch, data, - (GDestroyNotify) duration_changed_signal_data_free); - } -} - -typedef struct -{ - GstPlayer *player; - GstClockTime position; -} SeekDoneSignalData; - -static void -seek_done_dispatch (gpointer user_data) -{ - SeekDoneSignalData *data = user_data; - - if (data->player->inhibit_sigs) - return; - - g_signal_emit (data->player, signals[SIGNAL_SEEK_DONE], 0, data->position); -} - -static void -seek_done_signal_data_free (SeekDoneSignalData * data) -{ - g_object_unref (data->player); - g_free (data); -} - -static void -emit_seek_done (GstPlayer * self) -{ - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_SEEK_DONE], 0, NULL, NULL, NULL) != 0) { - SeekDoneSignalData *data = g_new (SeekDoneSignalData, 1); - - data->player = g_object_ref (self); - data->position = gst_player_get_position (self); - gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self, - seek_done_dispatch, data, (GDestroyNotify) seek_done_signal_data_free); - } -} - -static void -state_changed_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, - gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - GstState old_state, new_state, pending_state; - - gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state); - - if (GST_MESSAGE_SRC (msg) == GST_OBJECT (self->playbin)) { - gchar *transition_name; - - GST_DEBUG_OBJECT (self, "Changed state old: %s new: %s pending: %s", - gst_element_state_get_name (old_state), - gst_element_state_get_name (new_state), - gst_element_state_get_name (pending_state)); - - transition_name = g_strdup_printf ("%s_%s", - gst_element_state_get_name (old_state), - gst_element_state_get_name (new_state)); - dump_dot_file (self, transition_name); - g_free (transition_name); - - self->current_state = new_state; - - if (old_state == GST_STATE_READY && new_state == GST_STATE_PAUSED - && pending_state == GST_STATE_VOID_PENDING) { - GstElement *video_sink; - GstPad *video_sink_pad; - gint64 duration = -1; - - GST_DEBUG_OBJECT (self, "Initial PAUSED - pre-rolled"); - - g_mutex_lock (&self->lock); - if (self->media_info) - g_object_unref (self->media_info); - self->media_info = gst_player_media_info_create (self); - g_mutex_unlock (&self->lock); - emit_media_info_updated_signal (self); - - g_object_get (self->playbin, "video-sink", &video_sink, NULL); - - if (video_sink) { - video_sink_pad = gst_element_get_static_pad (video_sink, "sink"); - - if (video_sink_pad) { - g_signal_connect (video_sink_pad, "notify::caps", - (GCallback) notify_caps_cb, self); - gst_object_unref (video_sink_pad); - } - gst_object_unref (video_sink); - } - - check_video_dimensions_changed (self); - if (gst_element_query_duration (self->playbin, GST_FORMAT_TIME, - &duration)) { - emit_duration_changed (self, duration); - } else { - self->cached_duration = GST_CLOCK_TIME_NONE; - } - } - - if (new_state == GST_STATE_PAUSED - && pending_state == GST_STATE_VOID_PENDING) { - remove_tick_source (self); - - g_mutex_lock (&self->lock); - if (self->seek_pending) { - self->seek_pending = FALSE; - - if (!self->media_info->seekable) { - GST_DEBUG_OBJECT (self, "Media is not seekable"); - remove_seek_source (self); - self->seek_position = GST_CLOCK_TIME_NONE; - self->last_seek_time = GST_CLOCK_TIME_NONE; - } else if (self->seek_source) { - GST_DEBUG_OBJECT (self, "Seek finished but new seek is pending"); - gst_player_seek_internal_locked (self); - } else { - GST_DEBUG_OBJECT (self, "Seek finished"); - emit_seek_done (self); - } - } - - if (self->seek_position != GST_CLOCK_TIME_NONE) { - GST_DEBUG_OBJECT (self, "Seeking now that we reached PAUSED state"); - gst_player_seek_internal_locked (self); - g_mutex_unlock (&self->lock); - } else if (!self->seek_pending) { - g_mutex_unlock (&self->lock); - - tick_cb (self); - - if (self->target_state >= GST_STATE_PLAYING && self->buffering == 100) { - GstStateChangeReturn state_ret; - - state_ret = gst_element_set_state (self->playbin, GST_STATE_PLAYING); - if (state_ret == GST_STATE_CHANGE_FAILURE) - emit_error (self, g_error_new (GST_PLAYER_ERROR, - GST_PLAYER_ERROR_FAILED, "Failed to play")); - } else if (self->buffering == 100) { - change_state (self, GST_PLAYER_STATE_PAUSED); - } - } else { - g_mutex_unlock (&self->lock); - } - } else if (new_state == GST_STATE_PLAYING - && pending_state == GST_STATE_VOID_PENDING) { - - /* If no seek is currently pending, add the tick source. This can happen - * if we seeked already but the state-change message was still queued up */ - if (!self->seek_pending) { - add_tick_source (self); - change_state (self, GST_PLAYER_STATE_PLAYING); - } - } else if (new_state == GST_STATE_READY && old_state > GST_STATE_READY) { - change_state (self, GST_PLAYER_STATE_STOPPED); - } else { - /* Otherwise we neither reached PLAYING nor PAUSED, so must - * wait for something to happen... i.e. are BUFFERING now */ - change_state (self, GST_PLAYER_STATE_BUFFERING); - } - } -} - -static void -duration_changed_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg, - gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - gint64 duration = GST_CLOCK_TIME_NONE; - - if (gst_element_query_duration (self->playbin, GST_FORMAT_TIME, &duration)) { - emit_duration_changed (self, duration); - } -} - -static void -latency_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg, - gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - - GST_DEBUG_OBJECT (self, "Latency changed"); - - gst_bin_recalculate_latency (GST_BIN (self->playbin)); -} - -static void -request_state_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, - gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - GstState state; - GstStateChangeReturn state_ret; - - gst_message_parse_request_state (msg, &state); - - GST_DEBUG_OBJECT (self, "State %s requested", - gst_element_state_get_name (state)); - - self->target_state = state; - state_ret = gst_element_set_state (self->playbin, state); - if (state_ret == GST_STATE_CHANGE_FAILURE) - emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED, - "Failed to change to requested state %s", - gst_element_state_get_name (state))); -} - -static void -media_info_update (GstPlayer * self, GstPlayerMediaInfo * info) -{ - g_free (info->title); - info->title = get_from_tags (self, info, get_title); - - g_free (info->container); - info->container = get_from_tags (self, info, get_container_format); - - if (info->image_sample) - gst_sample_unref (info->image_sample); - info->image_sample = get_from_tags (self, info, get_cover_sample); - - GST_DEBUG_OBJECT (self, "title: %s, container: %s " - "image_sample: %p", info->title, info->container, info->image_sample); -} - -static void -tags_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - GstTagList *tags = NULL; - - gst_message_parse_tag (msg, &tags); - - GST_DEBUG_OBJECT (self, "received %s tags", - gst_tag_list_get_scope (tags) == - GST_TAG_SCOPE_GLOBAL ? "global" : "stream"); - - if (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_GLOBAL) { - g_mutex_lock (&self->lock); - if (self->media_info) { - if (self->media_info->tags) - gst_tag_list_unref (self->media_info->tags); - self->media_info->tags = gst_tag_list_ref (tags); - media_info_update (self, self->media_info); - g_mutex_unlock (&self->lock); - emit_media_info_updated_signal (self); - } else { - if (self->global_tags) - gst_tag_list_unref (self->global_tags); - self->global_tags = gst_tag_list_ref (tags); - g_mutex_unlock (&self->lock); - } - } - - gst_tag_list_unref (tags); -} - -static void -element_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - const GstStructure *s; - - s = gst_message_get_structure (msg); - if (gst_structure_has_name (s, "redirect")) { - const gchar *new_location; - - new_location = gst_structure_get_string (s, "new-location"); - if (!new_location) { - const GValue *locations_list, *location_val; - guint i, size; - - locations_list = gst_structure_get_value (s, "locations"); - size = gst_value_list_get_size (locations_list); - for (i = 0; i < size; ++i) { - const GstStructure *location_s; - - location_val = gst_value_list_get_value (locations_list, i); - if (!GST_VALUE_HOLDS_STRUCTURE (location_val)) - continue; - - location_s = (const GstStructure *) g_value_get_boxed (location_val); - if (!gst_structure_has_name (location_s, "redirect")) - continue; - - new_location = gst_structure_get_string (location_s, "new-location"); - if (new_location) - break; - } - } - - if (new_location) { - GstState target_state; - - GST_DEBUG_OBJECT (self, "Redirect to '%s'", new_location); - - /* Remember target state and restore after setting the URI */ - target_state = self->target_state; - - gst_player_stop_internal (self, TRUE); - - g_mutex_lock (&self->lock); - g_free (self->redirect_uri); - self->redirect_uri = g_strdup (new_location); - g_object_set (self->playbin, "uri", self->redirect_uri, NULL); - g_mutex_unlock (&self->lock); - - if (target_state == GST_STATE_PAUSED) - gst_player_pause_internal (self); - else if (target_state == GST_STATE_PLAYING) - gst_player_play_internal (self); - } - } -} - -/* Must be called with lock */ -static gboolean -update_stream_collection (GstPlayer * self, GstStreamCollection * collection) -{ - if (self->collection && self->collection == collection) - return FALSE; - - if (self->collection && self->stream_notify_id) - g_signal_handler_disconnect (self->collection, self->stream_notify_id); - - gst_object_replace ((GstObject **) & self->collection, - (GstObject *) collection); - if (self->media_info) { - gst_object_unref (self->media_info); - self->media_info = gst_player_media_info_create (self); - } - - self->stream_notify_id = - g_signal_connect (self->collection, "stream-notify", - G_CALLBACK (stream_notify_cb), self); - - return TRUE; -} - -static void -stream_collection_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, - gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - GstStreamCollection *collection = NULL; - gboolean updated = FALSE; - - gst_message_parse_stream_collection (msg, &collection); - - if (!collection) - return; - - g_mutex_lock (&self->lock); - updated = update_stream_collection (self, collection); - gst_object_unref (collection); - g_mutex_unlock (&self->lock); - - if (self->media_info && updated) - emit_media_info_updated_signal (self); -} - -static void -streams_selected_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, - gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - GstStreamCollection *collection = NULL; - gboolean updated = FALSE; - guint i, len; - - gst_message_parse_streams_selected (msg, &collection); - - if (!collection) - return; - - g_mutex_lock (&self->lock); - updated = update_stream_collection (self, collection); - gst_object_unref (collection); - - g_free (self->video_sid); - g_free (self->audio_sid); - g_free (self->subtitle_sid); - self->video_sid = NULL; - self->audio_sid = NULL; - self->subtitle_sid = NULL; - - len = gst_message_streams_selected_get_size (msg); - for (i = 0; i < len; i++) { - GstStream *stream; - GstStreamType stream_type; - const gchar *stream_id; - gchar **current_sid; - stream = gst_message_streams_selected_get_stream (msg, i); - stream_type = gst_stream_get_stream_type (stream); - stream_id = gst_stream_get_stream_id (stream); - if (stream_type & GST_STREAM_TYPE_AUDIO) - current_sid = &self->audio_sid; - else if (stream_type & GST_STREAM_TYPE_VIDEO) - current_sid = &self->video_sid; - else if (stream_type & GST_STREAM_TYPE_TEXT) - current_sid = &self->subtitle_sid; - else { - GST_WARNING_OBJECT (self, - "Unknown stream-id %s with type 0x%x", stream_id, stream_type); - continue; - } - - if (G_UNLIKELY (*current_sid)) { - GST_FIXME_OBJECT (self, - "Multiple streams are selected for type %s, choose the first one", - gst_stream_type_get_name (stream_type)); - continue; - } - - *current_sid = g_strdup (stream_id); - } - g_mutex_unlock (&self->lock); - - if (self->media_info && updated) - emit_media_info_updated_signal (self); -} - -static void -player_set_flag (GstPlayer * self, gint pos) -{ - gint flags; - - g_object_get (self->playbin, "flags", &flags, NULL); - flags |= pos; - g_object_set (self->playbin, "flags", flags, NULL); - - GST_DEBUG_OBJECT (self, "setting flags=%#x", flags); -} - -static void -player_clear_flag (GstPlayer * self, gint pos) -{ - gint flags; - - g_object_get (self->playbin, "flags", &flags, NULL); - flags &= ~pos; - g_object_set (self->playbin, "flags", flags, NULL); - - GST_DEBUG_OBJECT (self, "setting flags=%#x", flags); -} - -typedef struct -{ - GstPlayer *player; - GstPlayerMediaInfo *info; -} MediaInfoUpdatedSignalData; - -static void -media_info_updated_dispatch (gpointer user_data) -{ - MediaInfoUpdatedSignalData *data = user_data; - - if (data->player->inhibit_sigs) - return; - - if (data->player->target_state >= GST_STATE_PAUSED) { - g_signal_emit (data->player, signals[SIGNAL_MEDIA_INFO_UPDATED], 0, - data->info); - } -} - -static void -free_media_info_updated_signal_data (MediaInfoUpdatedSignalData * data) -{ - g_object_unref (data->player); - g_object_unref (data->info); - g_free (data); -} - -/* - * emit_media_info_updated_signal: - * - * create a new copy of self->media_info object and emits the newly created - * copy to user application. The newly created media_info will be unref'ed - * as part of signal finalize method. - */ -static void -emit_media_info_updated_signal (GstPlayer * self) -{ - MediaInfoUpdatedSignalData *data = g_new (MediaInfoUpdatedSignalData, 1); - data->player = g_object_ref (self); - g_mutex_lock (&self->lock); - data->info = gst_player_media_info_copy (self->media_info); - g_mutex_unlock (&self->lock); - - gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self, - media_info_updated_dispatch, data, - (GDestroyNotify) free_media_info_updated_signal_data); -} - -static GstCaps * -get_caps (GstPlayer * self, gint stream_index, GType type) -{ - GstPad *pad = NULL; - GstCaps *caps = NULL; - - if (type == GST_TYPE_PLAYER_VIDEO_INFO) - g_signal_emit_by_name (G_OBJECT (self->playbin), - "get-video-pad", stream_index, &pad); - else if (type == GST_TYPE_PLAYER_AUDIO_INFO) - g_signal_emit_by_name (G_OBJECT (self->playbin), - "get-audio-pad", stream_index, &pad); - else - g_signal_emit_by_name (G_OBJECT (self->playbin), - "get-text-pad", stream_index, &pad); - - if (pad) { - caps = gst_pad_get_current_caps (pad); - gst_object_unref (pad); - } - - return caps; -} - -static void -gst_player_subtitle_info_update (GstPlayer * self, - GstPlayerStreamInfo * stream_info) -{ - GstPlayerSubtitleInfo *info = (GstPlayerSubtitleInfo *) stream_info; - - if (stream_info->tags) { - - /* free the old language info */ - g_free (info->language); - info->language = NULL; - - /* First try to get the language full name from tag, if name is not - * available then try language code. If we find the language code - * then use gstreamer api to translate code to full name. - */ - gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_NAME, - &info->language); - if (!info->language) { - gchar *lang_code = NULL; - - gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_CODE, - &lang_code); - if (lang_code) { - info->language = g_strdup (gst_tag_get_language_name (lang_code)); - g_free (lang_code); - } - } - - /* If we are still failed to find language name then check if external - * subtitle is loaded and compare the stream index between current sub - * stream index with our stream index and if matches then declare it as - * external subtitle and use the filename. - */ - if (!info->language) { - gint text_index = -1; - gchar *suburi = NULL; - - g_object_get (G_OBJECT (self->playbin), "current-suburi", &suburi, NULL); - if (suburi) { - if (self->use_playbin3) { - if (g_str_equal (self->subtitle_sid, stream_info->stream_id)) - info->language = g_path_get_basename (suburi); - } else { - g_object_get (G_OBJECT (self->playbin), "current-text", &text_index, - NULL); - if (text_index == gst_player_stream_info_get_index (stream_info)) - info->language = g_path_get_basename (suburi); - } - g_free (suburi); - } - } - - } else { - g_free (info->language); - info->language = NULL; - } - - GST_DEBUG_OBJECT (self, "language=%s", info->language); -} - -static void -gst_player_video_info_update (GstPlayer * self, - GstPlayerStreamInfo * stream_info) -{ - GstPlayerVideoInfo *info = (GstPlayerVideoInfo *) stream_info; - - if (stream_info->caps) { - GstStructure *s; - - s = gst_caps_get_structure (stream_info->caps, 0); - if (s) { - gint width, height; - gint fps_n, fps_d; - gint par_n, par_d; - - if (gst_structure_get_int (s, "width", &width)) - info->width = width; - else - info->width = -1; - - if (gst_structure_get_int (s, "height", &height)) - info->height = height; - else - info->height = -1; - - if (gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d)) { - info->framerate_num = fps_n; - info->framerate_denom = fps_d; - } else { - info->framerate_num = 0; - info->framerate_denom = 1; - } - - - if (gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d)) { - info->par_num = par_n; - info->par_denom = par_d; - } else { - info->par_num = 1; - info->par_denom = 1; - } - } - } else { - info->width = info->height = -1; - info->par_num = info->par_denom = 1; - info->framerate_num = 0; - info->framerate_denom = 1; - } - - if (stream_info->tags) { - guint bitrate, max_bitrate; - - if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_BITRATE, &bitrate)) - info->bitrate = bitrate; - else - info->bitrate = -1; - - if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_MAXIMUM_BITRATE, - &max_bitrate) || gst_tag_list_get_uint (stream_info->tags, - GST_TAG_NOMINAL_BITRATE, &max_bitrate)) - info->max_bitrate = max_bitrate; - else - info->max_bitrate = -1; - } else { - info->bitrate = info->max_bitrate = -1; - } - - GST_DEBUG_OBJECT (self, "width=%d height=%d fps=%.2f par=%d:%d " - "bitrate=%d max_bitrate=%d", info->width, info->height, - (gdouble) info->framerate_num / info->framerate_denom, - info->par_num, info->par_denom, info->bitrate, info->max_bitrate); -} - -static void -gst_player_audio_info_update (GstPlayer * self, - GstPlayerStreamInfo * stream_info) -{ - GstPlayerAudioInfo *info = (GstPlayerAudioInfo *) stream_info; - - if (stream_info->caps) { - GstStructure *s; - - s = gst_caps_get_structure (stream_info->caps, 0); - if (s) { - gint rate, channels; - - if (gst_structure_get_int (s, "rate", &rate)) - info->sample_rate = rate; - else - info->sample_rate = -1; - - if (gst_structure_get_int (s, "channels", &channels)) - info->channels = channels; - else - info->channels = 0; - } - } else { - info->sample_rate = -1; - info->channels = 0; - } - - if (stream_info->tags) { - guint bitrate, max_bitrate; - - if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_BITRATE, &bitrate)) - info->bitrate = bitrate; - else - info->bitrate = -1; - - if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_MAXIMUM_BITRATE, - &max_bitrate) || gst_tag_list_get_uint (stream_info->tags, - GST_TAG_NOMINAL_BITRATE, &max_bitrate)) - info->max_bitrate = max_bitrate; - else - info->max_bitrate = -1; - - /* if we have old language the free it */ - g_free (info->language); - info->language = NULL; - - /* First try to get the language full name from tag, if name is not - * available then try language code. If we find the language code - * then use gstreamer api to translate code to full name. - */ - gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_NAME, - &info->language); - if (!info->language) { - gchar *lang_code = NULL; - - gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_CODE, - &lang_code); - if (lang_code) { - info->language = g_strdup (gst_tag_get_language_name (lang_code)); - g_free (lang_code); - } - } - } else { - g_free (info->language); - info->language = NULL; - info->max_bitrate = info->bitrate = -1; - } - - GST_DEBUG_OBJECT (self, "language=%s rate=%d channels=%d bitrate=%d " - "max_bitrate=%d", info->language, info->sample_rate, info->channels, - info->bitrate, info->max_bitrate); -} - -static GstPlayerStreamInfo * -gst_player_stream_info_find (GstPlayerMediaInfo * media_info, - GType type, gint stream_index) -{ - GList *list, *l; - GstPlayerStreamInfo *info = NULL; - - if (!media_info) - return NULL; - - list = gst_player_media_info_get_stream_list (media_info); - for (l = list; l != NULL; l = l->next) { - info = (GstPlayerStreamInfo *) l->data; - if ((G_OBJECT_TYPE (info) == type) && (info->stream_index == stream_index)) { - return info; - } - } - - return NULL; -} - -static GstPlayerStreamInfo * -gst_player_stream_info_find_from_stream_id (GstPlayerMediaInfo * media_info, - const gchar * stream_id) -{ - GList *list, *l; - GstPlayerStreamInfo *info = NULL; - - if (!media_info) - return NULL; - - list = gst_player_media_info_get_stream_list (media_info); - for (l = list; l != NULL; l = l->next) { - info = (GstPlayerStreamInfo *) l->data; - if (g_str_equal (info->stream_id, stream_id)) { - return info; - } - } - - return NULL; -} - -static gboolean -is_track_enabled (GstPlayer * self, gint pos) -{ - gint flags; - - g_object_get (G_OBJECT (self->playbin), "flags", &flags, NULL); - - if ((flags & pos)) - return TRUE; - - return FALSE; -} - -static GstPlayerStreamInfo * -gst_player_stream_info_get_current (GstPlayer * self, const gchar * prop, - GType type) -{ - gint current; - GstPlayerStreamInfo *info; - - if (!self->media_info) - return NULL; - - g_object_get (G_OBJECT (self->playbin), prop, ¤t, NULL); - g_mutex_lock (&self->lock); - info = gst_player_stream_info_find (self->media_info, type, current); - if (info) - info = gst_player_stream_info_copy (info); - g_mutex_unlock (&self->lock); - - return info; -} - -static GstPlayerStreamInfo * -gst_player_stream_info_get_current_from_stream_id (GstPlayer * self, - const gchar * stream_id, GType type) -{ - GstPlayerStreamInfo *info; - - if (!self->media_info || !stream_id) - return NULL; - - g_mutex_lock (&self->lock); - info = - gst_player_stream_info_find_from_stream_id (self->media_info, stream_id); - if (info && G_OBJECT_TYPE (info) == type) - info = gst_player_stream_info_copy (info); - else - info = NULL; - g_mutex_unlock (&self->lock); - - return info; -} - -static void -stream_notify_cb (GstStreamCollection * collection, GstStream * stream, - GParamSpec * pspec, GstPlayer * self) -{ - GstPlayerStreamInfo *info; - const gchar *stream_id; - gboolean emit_signal = FALSE; - - if (!self->media_info) - return; - - if (G_PARAM_SPEC_VALUE_TYPE (pspec) != GST_TYPE_CAPS && - G_PARAM_SPEC_VALUE_TYPE (pspec) != GST_TYPE_TAG_LIST) - return; - - stream_id = gst_stream_get_stream_id (stream); - g_mutex_lock (&self->lock); - info = - gst_player_stream_info_find_from_stream_id (self->media_info, stream_id); - if (info) { - gst_player_stream_info_update_from_stream (self, info, stream); - emit_signal = TRUE; - } - g_mutex_unlock (&self->lock); - - if (emit_signal) - emit_media_info_updated_signal (self); -} - -static void -gst_player_stream_info_update (GstPlayer * self, GstPlayerStreamInfo * s) -{ - if (GST_IS_PLAYER_VIDEO_INFO (s)) - gst_player_video_info_update (self, s); - else if (GST_IS_PLAYER_AUDIO_INFO (s)) - gst_player_audio_info_update (self, s); - else - gst_player_subtitle_info_update (self, s); -} - -static gchar * -stream_info_get_codec (GstPlayerStreamInfo * s) -{ - const gchar *type; - GstTagList *tags; - gchar *codec = NULL; - - if (GST_IS_PLAYER_VIDEO_INFO (s)) - type = GST_TAG_VIDEO_CODEC; - else if (GST_IS_PLAYER_AUDIO_INFO (s)) - type = GST_TAG_AUDIO_CODEC; - else - type = GST_TAG_SUBTITLE_CODEC; - - tags = gst_player_stream_info_get_tags (s); - if (tags) { - gst_tag_list_get_string (tags, type, &codec); - if (!codec) - gst_tag_list_get_string (tags, GST_TAG_CODEC, &codec); - } - - if (!codec) { - GstCaps *caps; - caps = gst_player_stream_info_get_caps (s); - if (caps) { - codec = gst_pb_utils_get_codec_description (caps); - } - } - - return codec; -} - -static void -gst_player_stream_info_update_tags_and_caps (GstPlayer * self, - GstPlayerStreamInfo * s) -{ - GstTagList *tags; - gint stream_index; - - stream_index = gst_player_stream_info_get_index (s); - - if (GST_IS_PLAYER_VIDEO_INFO (s)) - g_signal_emit_by_name (self->playbin, "get-video-tags", - stream_index, &tags); - else if (GST_IS_PLAYER_AUDIO_INFO (s)) - g_signal_emit_by_name (self->playbin, "get-audio-tags", - stream_index, &tags); - else - g_signal_emit_by_name (self->playbin, "get-text-tags", stream_index, &tags); - - if (s->tags) - gst_tag_list_unref (s->tags); - s->tags = tags; - - if (s->caps) - gst_caps_unref (s->caps); - s->caps = get_caps (self, stream_index, G_OBJECT_TYPE (s)); - - g_free (s->codec); - s->codec = stream_info_get_codec (s); - - GST_DEBUG_OBJECT (self, "%s index: %d tags: %p caps: %p", - gst_player_stream_info_get_stream_type (s), stream_index, - s->tags, s->caps); - - gst_player_stream_info_update (self, s); -} - -static void -gst_player_streams_info_create (GstPlayer * self, - GstPlayerMediaInfo * media_info, const gchar * prop, GType type) -{ - gint i; - gint total = -1; - GstPlayerStreamInfo *s; - - if (!media_info) - return; - - g_object_get (G_OBJECT (self->playbin), prop, &total, NULL); - - GST_DEBUG_OBJECT (self, "%s: %d", prop, total); - - for (i = 0; i < total; i++) { - /* check if stream already exist in the list */ - s = gst_player_stream_info_find (media_info, type, i); - - if (!s) { - /* create a new stream info instance */ - s = gst_player_stream_info_new (i, type); - - /* add the object in stream list */ - media_info->stream_list = g_list_append (media_info->stream_list, s); - - /* based on type, add the object in its corresponding stream_ list */ - if (GST_IS_PLAYER_AUDIO_INFO (s)) - media_info->audio_stream_list = g_list_append - (media_info->audio_stream_list, s); - else if (GST_IS_PLAYER_VIDEO_INFO (s)) - media_info->video_stream_list = g_list_append - (media_info->video_stream_list, s); - else - media_info->subtitle_stream_list = g_list_append - (media_info->subtitle_stream_list, s); - - GST_DEBUG_OBJECT (self, "create %s stream stream_index: %d", - gst_player_stream_info_get_stream_type (s), i); - } - - gst_player_stream_info_update_tags_and_caps (self, s); - } -} - -static void -gst_player_stream_info_update_from_stream (GstPlayer * self, - GstPlayerStreamInfo * s, GstStream * stream) -{ - if (s->tags) - gst_tag_list_unref (s->tags); - s->tags = gst_stream_get_tags (stream); - - if (s->caps) - gst_caps_unref (s->caps); - s->caps = gst_stream_get_caps (stream); - - g_free (s->codec); - s->codec = stream_info_get_codec (s); - - GST_DEBUG_OBJECT (self, "%s index: %d tags: %p caps: %p", - gst_player_stream_info_get_stream_type (s), s->stream_index, - s->tags, s->caps); - - gst_player_stream_info_update (self, s); -} - -static void -gst_player_streams_info_create_from_collection (GstPlayer * self, - GstPlayerMediaInfo * media_info, GstStreamCollection * collection) -{ - guint i; - guint total; - GstPlayerStreamInfo *s; - guint n_audio = 0; - guint n_video = 0; - guint n_text = 0; - - if (!media_info || !collection) - return; - - total = gst_stream_collection_get_size (collection); - - for (i = 0; i < total; i++) { - GstStream *stream = gst_stream_collection_get_stream (collection, i); - GstStreamType stream_type = gst_stream_get_stream_type (stream); - const gchar *stream_id = gst_stream_get_stream_id (stream); - - if (stream_type & GST_STREAM_TYPE_AUDIO) { - s = gst_player_stream_info_new (n_audio, GST_TYPE_PLAYER_AUDIO_INFO); - n_audio++; - } else if (stream_type & GST_STREAM_TYPE_VIDEO) { - s = gst_player_stream_info_new (n_video, GST_TYPE_PLAYER_VIDEO_INFO); - n_video++; - } else if (stream_type & GST_STREAM_TYPE_TEXT) { - s = gst_player_stream_info_new (n_text, GST_TYPE_PLAYER_SUBTITLE_INFO); - n_text++; - } else { - GST_DEBUG_OBJECT (self, "Unknown type stream %d", i); - continue; - } - - s->stream_id = g_strdup (stream_id); - - /* add the object in stream list */ - media_info->stream_list = g_list_append (media_info->stream_list, s); - - /* based on type, add the object in its corresponding stream_ list */ - if (GST_IS_PLAYER_AUDIO_INFO (s)) - media_info->audio_stream_list = g_list_append - (media_info->audio_stream_list, s); - else if (GST_IS_PLAYER_VIDEO_INFO (s)) - media_info->video_stream_list = g_list_append - (media_info->video_stream_list, s); - else - media_info->subtitle_stream_list = g_list_append - (media_info->subtitle_stream_list, s); - - GST_DEBUG_OBJECT (self, "create %s stream stream_index: %d", - gst_player_stream_info_get_stream_type (s), s->stream_index); - - gst_player_stream_info_update_from_stream (self, s, stream); - } -} - -static void -video_changed_cb (G_GNUC_UNUSED GObject * object, gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - - g_mutex_lock (&self->lock); - gst_player_streams_info_create (self, self->media_info, - "n-video", GST_TYPE_PLAYER_VIDEO_INFO); - g_mutex_unlock (&self->lock); -} - -static void -audio_changed_cb (G_GNUC_UNUSED GObject * object, gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - - g_mutex_lock (&self->lock); - gst_player_streams_info_create (self, self->media_info, - "n-audio", GST_TYPE_PLAYER_AUDIO_INFO); - g_mutex_unlock (&self->lock); -} - -static void -subtitle_changed_cb (G_GNUC_UNUSED GObject * object, gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - - g_mutex_lock (&self->lock); - gst_player_streams_info_create (self, self->media_info, - "n-text", GST_TYPE_PLAYER_SUBTITLE_INFO); - g_mutex_unlock (&self->lock); -} - -static void * -get_title (GstTagList * tags) -{ - gchar *title = NULL; - - gst_tag_list_get_string (tags, GST_TAG_TITLE, &title); - if (!title) - gst_tag_list_get_string (tags, GST_TAG_TITLE_SORTNAME, &title); - - return title; -} - -static void * -get_container_format (GstTagList * tags) -{ - gchar *container = NULL; - - gst_tag_list_get_string (tags, GST_TAG_CONTAINER_FORMAT, &container); - - /* TODO: If container is not available then maybe consider - * parsing caps or file extension to guess the container format. - */ - - return container; -} - -static void * -get_from_tags (GstPlayer * self, GstPlayerMediaInfo * media_info, - void *(*func) (GstTagList *)) -{ - GList *l; - void *ret = NULL; - - if (media_info->tags) { - ret = func (media_info->tags); - if (ret) - return ret; - } - - /* if global tag does not exit then try video and audio streams */ - GST_DEBUG_OBJECT (self, "trying video tags"); - for (l = gst_player_media_info_get_video_streams (media_info); l != NULL; - l = l->next) { - GstTagList *tags; - - tags = gst_player_stream_info_get_tags ((GstPlayerStreamInfo *) l->data); - if (tags) - ret = func (tags); - - if (ret) - return ret; - } - - GST_DEBUG_OBJECT (self, "trying audio tags"); - for (l = gst_player_media_info_get_audio_streams (media_info); l != NULL; - l = l->next) { - GstTagList *tags; - - tags = gst_player_stream_info_get_tags ((GstPlayerStreamInfo *) l->data); - if (tags) - ret = func (tags); - - if (ret) - return ret; - } - - GST_DEBUG_OBJECT (self, "failed to get the information from tags"); - return NULL; -} - -static void * -get_cover_sample (GstTagList * tags) -{ - GstSample *cover_sample = NULL; - - gst_tag_list_get_sample (tags, GST_TAG_IMAGE, &cover_sample); - if (!cover_sample) - gst_tag_list_get_sample (tags, GST_TAG_PREVIEW_IMAGE, &cover_sample); - - return cover_sample; -} - -static GstPlayerMediaInfo * -gst_player_media_info_create (GstPlayer * self) -{ - GstPlayerMediaInfo *media_info; - GstQuery *query; - - GST_DEBUG_OBJECT (self, "begin"); - media_info = gst_player_media_info_new (self->uri); - media_info->duration = gst_player_get_duration (self); - media_info->tags = self->global_tags; - media_info->is_live = self->is_live; - self->global_tags = NULL; - - query = gst_query_new_seeking (GST_FORMAT_TIME); - if (gst_element_query (self->playbin, query)) - gst_query_parse_seeking (query, NULL, &media_info->seekable, NULL, NULL); - gst_query_unref (query); - - if (self->use_playbin3 && self->collection) { - gst_player_streams_info_create_from_collection (self, media_info, - self->collection); - } else { - /* create audio/video/sub streams */ - gst_player_streams_info_create (self, media_info, "n-video", - GST_TYPE_PLAYER_VIDEO_INFO); - gst_player_streams_info_create (self, media_info, "n-audio", - GST_TYPE_PLAYER_AUDIO_INFO); - gst_player_streams_info_create (self, media_info, "n-text", - GST_TYPE_PLAYER_SUBTITLE_INFO); - } - - media_info->title = get_from_tags (self, media_info, get_title); - media_info->container = - get_from_tags (self, media_info, get_container_format); - media_info->image_sample = get_from_tags (self, media_info, get_cover_sample); - - GST_DEBUG_OBJECT (self, "uri: %s title: %s duration: %" GST_TIME_FORMAT - " seekable: %s live: %s container: %s image_sample %p", - media_info->uri, media_info->title, GST_TIME_ARGS (media_info->duration), - media_info->seekable ? "yes" : "no", media_info->is_live ? "yes" : "no", - media_info->container, media_info->image_sample); - - GST_DEBUG_OBJECT (self, "end"); - return media_info; -} - -static void -tags_changed_cb (GstPlayer * self, gint stream_index, GType type) -{ - GstPlayerStreamInfo *s; - - if (!self->media_info) - return; - - /* update the stream information */ - g_mutex_lock (&self->lock); - s = gst_player_stream_info_find (self->media_info, type, stream_index); - gst_player_stream_info_update_tags_and_caps (self, s); - g_mutex_unlock (&self->lock); - - emit_media_info_updated_signal (self); -} - -static void -video_tags_changed_cb (G_GNUC_UNUSED GstElement * playbin, gint stream_index, - gpointer user_data) +buffering_cb (GstPlaySignalAdapter * adapter, gint buffering_percent, + GstPlayer * self) { - tags_changed_cb (GST_PLAYER (user_data), stream_index, - GST_TYPE_PLAYER_VIDEO_INFO); + g_signal_emit (self, signals[SIGNAL_BUFFERING], 0, buffering_percent); } static void -audio_tags_changed_cb (G_GNUC_UNUSED GstElement * playbin, gint stream_index, - gpointer user_data) +end_of_stream_cb (GstPlaySignalAdapter * adapter, GstPlayer * self) { - tags_changed_cb (GST_PLAYER (user_data), stream_index, - GST_TYPE_PLAYER_AUDIO_INFO); + g_signal_emit (self, signals[SIGNAL_END_OF_STREAM], 0, NULL); } static void -subtitle_tags_changed_cb (G_GNUC_UNUSED GstElement * playbin, gint stream_index, - gpointer user_data) +error_cb (GstPlaySignalAdapter * adapter, GError * error, + GstStructure * details, GstPlayer * self) { - tags_changed_cb (GST_PLAYER (user_data), stream_index, - GST_TYPE_PLAYER_SUBTITLE_INFO); + g_signal_emit (self, signals[SIGNAL_ERROR], 0, error); } static void -volume_changed_dispatch (gpointer user_data) +dimensions_changed_cb (GstPlaySignalAdapter * adapter, guint width, + guint height, GstPlayer * self) { - GstPlayer *player = user_data; - - if (player->inhibit_sigs) - return; - - g_signal_emit (player, signals[SIGNAL_VOLUME_CHANGED], 0); - g_object_notify_by_pspec (G_OBJECT (player), param_specs[PROP_VOLUME]); + g_signal_emit (self, signals[SIGNAL_VIDEO_DIMENSIONS_CHANGED], 0, width, + height); } static void -volume_notify_cb (G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GParamSpec * pspec, +media_info_cb (GstPlaySignalAdapter * adapter, GstPlayMediaInfo * info, GstPlayer * self) { - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_VOLUME_CHANGED], 0, NULL, NULL, NULL) != 0) { - gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self, - volume_changed_dispatch, g_object_ref (self), - (GDestroyNotify) g_object_unref); - } + GstPlayerMediaInfo *i = gst_player_media_info_wrapped (info); + g_signal_emit (self, signals[SIGNAL_MEDIA_INFO_UPDATED], 0, i); + g_object_unref (i); } static void -mute_changed_dispatch (gpointer user_data) +volume_cb (GstPlaySignalAdapter * adapter, gdouble volume, GstPlayer * self) { - GstPlayer *player = user_data; - - if (player->inhibit_sigs) - return; - - g_signal_emit (player, signals[SIGNAL_MUTE_CHANGED], 0); - g_object_notify_by_pspec (G_OBJECT (player), param_specs[PROP_MUTE]); + g_signal_emit (self, signals[SIGNAL_VOLUME_CHANGED], 0, NULL); } -static void -mute_notify_cb (G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GParamSpec * pspec, - GstPlayer * self) -{ - if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID, - signals[SIGNAL_MUTE_CHANGED], 0, NULL, NULL, NULL) != 0) { - gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self, - mute_changed_dispatch, g_object_ref (self), - (GDestroyNotify) g_object_unref); - } -} static void -source_setup_cb (GstElement * playbin, GstElement * source, GstPlayer * self) +mute_cb (GstPlaySignalAdapter * adapter, gboolean muted, GstPlayer * self) { - gchar *user_agent; - - user_agent = gst_player_config_get_user_agent (self->config); - if (user_agent) { - GParamSpec *prop; - - prop = g_object_class_find_property (G_OBJECT_GET_CLASS (source), - "user-agent"); - if (prop && prop->value_type == G_TYPE_STRING) { - GST_INFO_OBJECT (self, "Setting source user-agent: %s", user_agent); - g_object_set (source, "user-agent", user_agent, NULL); - } - - g_free (user_agent); - } + g_signal_emit (self, signals[SIGNAL_MUTE_CHANGED], 0, NULL); } -static gpointer -gst_player_main (gpointer data) +static void +warning_cb (GstPlaySignalAdapter * adapter, GError * warning, + GstStructure * details, GstPlayer * self) { - GstPlayer *self = GST_PLAYER (data); - GstBus *bus; - GSource *source; - GstElement *scaletempo; - const gchar *env; - - GST_TRACE_OBJECT (self, "Starting main thread"); - - g_main_context_push_thread_default (self->context); - - source = g_idle_source_new (); - g_source_set_callback (source, (GSourceFunc) main_loop_running_cb, self, - NULL); - g_source_attach (source, self->context); - g_source_unref (source); - - env = g_getenv ("GST_PLAYER_USE_PLAYBIN3"); - if (env && g_str_has_prefix (env, "1")) - self->use_playbin3 = TRUE; - - if (self->use_playbin3) { - GST_DEBUG_OBJECT (self, "playbin3 enabled"); - self->playbin = gst_element_factory_make ("playbin3", "playbin3"); - } else { - self->playbin = gst_element_factory_make ("playbin", "playbin"); - } - - if (!self->playbin) { - g_error ("GstPlayer: 'playbin' element not found, please check your setup"); - g_assert_not_reached (); - } - - gst_object_ref_sink (self->playbin); - - if (self->video_renderer) { - GstElement *video_sink = - gst_player_video_renderer_create_video_sink (self->video_renderer, - self); - - if (video_sink) - g_object_set (self->playbin, "video-sink", video_sink, NULL); - } - - scaletempo = gst_element_factory_make ("scaletempo", NULL); - if (scaletempo) { - g_object_set (self->playbin, "audio-filter", scaletempo, NULL); - } else { - g_warning ("GstPlayer: scaletempo element not available. Audio pitch " - "will not be preserved during trick modes"); - } - - self->bus = bus = gst_element_get_bus (self->playbin); - gst_bus_add_signal_watch (bus); - - g_signal_connect (G_OBJECT (bus), "message::error", G_CALLBACK (error_cb), - self); - g_signal_connect (G_OBJECT (bus), "message::warning", G_CALLBACK (warning_cb), - self); - g_signal_connect (G_OBJECT (bus), "message::eos", G_CALLBACK (eos_cb), self); - g_signal_connect (G_OBJECT (bus), "message::state-changed", - G_CALLBACK (state_changed_cb), self); - g_signal_connect (G_OBJECT (bus), "message::buffering", - G_CALLBACK (buffering_cb), self); - g_signal_connect (G_OBJECT (bus), "message::clock-lost", - G_CALLBACK (clock_lost_cb), self); - g_signal_connect (G_OBJECT (bus), "message::duration-changed", - G_CALLBACK (duration_changed_cb), self); - g_signal_connect (G_OBJECT (bus), "message::latency", - G_CALLBACK (latency_cb), self); - g_signal_connect (G_OBJECT (bus), "message::request-state", - G_CALLBACK (request_state_cb), self); - g_signal_connect (G_OBJECT (bus), "message::element", - G_CALLBACK (element_cb), self); - g_signal_connect (G_OBJECT (bus), "message::tag", G_CALLBACK (tags_cb), self); - - if (self->use_playbin3) { - g_signal_connect (G_OBJECT (bus), "message::stream-collection", - G_CALLBACK (stream_collection_cb), self); - g_signal_connect (G_OBJECT (bus), "message::streams-selected", - G_CALLBACK (streams_selected_cb), self); - } else { - g_signal_connect (self->playbin, "video-changed", - G_CALLBACK (video_changed_cb), self); - g_signal_connect (self->playbin, "audio-changed", - G_CALLBACK (audio_changed_cb), self); - g_signal_connect (self->playbin, "text-changed", - G_CALLBACK (subtitle_changed_cb), self); - - g_signal_connect (self->playbin, "video-tags-changed", - G_CALLBACK (video_tags_changed_cb), self); - g_signal_connect (self->playbin, "audio-tags-changed", - G_CALLBACK (audio_tags_changed_cb), self); - g_signal_connect (self->playbin, "text-tags-changed", - G_CALLBACK (subtitle_tags_changed_cb), self); - } - - g_signal_connect (self->playbin, "notify::volume", - G_CALLBACK (volume_notify_cb), self); - g_signal_connect (self->playbin, "notify::mute", - G_CALLBACK (mute_notify_cb), self); - g_signal_connect (self->playbin, "source-setup", - G_CALLBACK (source_setup_cb), self); - - self->target_state = GST_STATE_NULL; - self->current_state = GST_STATE_NULL; - change_state (self, GST_PLAYER_STATE_STOPPED); - self->buffering = 100; - self->is_eos = FALSE; - self->is_live = FALSE; - self->rate = 1.0; - - GST_TRACE_OBJECT (self, "Starting main loop"); - g_main_loop_run (self->loop); - GST_TRACE_OBJECT (self, "Stopped main loop"); - - gst_bus_remove_signal_watch (bus); - gst_object_unref (bus); - - remove_tick_source (self); - remove_ready_timeout_source (self); - - g_mutex_lock (&self->lock); - if (self->media_info) { - g_object_unref (self->media_info); - self->media_info = NULL; - } - - remove_seek_source (self); - g_mutex_unlock (&self->lock); - - g_main_context_pop_thread_default (self->context); - - self->target_state = GST_STATE_NULL; - self->current_state = GST_STATE_NULL; - if (self->playbin) { - gst_element_set_state (self->playbin, GST_STATE_NULL); - gst_object_unref (self->playbin); - self->playbin = NULL; - } - - GST_TRACE_OBJECT (self, "Stopped main thread"); - - return NULL; + g_signal_emit (self, signals[SIGNAL_WARNING], 0, warning); } -static gpointer -gst_player_init_once (G_GNUC_UNUSED gpointer user_data) +static void +seek_done_cb (GstPlaySignalAdapter * adapter, GstClockTime time, + GstPlayer * self) { - gst_init (NULL, NULL); - - GST_DEBUG_CATEGORY_INIT (gst_player_debug, "gst-player", 0, "GstPlayer"); - gst_player_error_quark (); - - return NULL; + g_signal_emit (self, signals[SIGNAL_SEEK_DONE], 0, time); } /** @@ -3051,76 +565,64 @@ gst_player_new (GstPlayerVideoRenderer * video_renderer, { static GOnce once = G_ONCE_INIT; GstPlayer *self; + GstPlayerVideoRenderer *renderer = NULL; g_once (&once, gst_player_init_once, NULL); self = - g_object_new (GST_TYPE_PLAYER, "video-renderer", video_renderer, - "signal-dispatcher", signal_dispatcher, NULL); - gst_object_ref_sink (self); - - if (video_renderer) - g_object_unref (video_renderer); - if (signal_dispatcher) - g_object_unref (signal_dispatcher); - - return self; -} - -static gboolean -gst_player_play_internal (gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - GstStateChangeReturn state_ret; - - GST_DEBUG_OBJECT (self, "Play"); + g_object_new (GST_TYPE_PLAYER, "signal-dispatcher", signal_dispatcher, + NULL); - g_mutex_lock (&self->lock); - if (!self->uri) { - g_mutex_unlock (&self->lock); - return G_SOURCE_REMOVE; + if (video_renderer != NULL) { + renderer = gst_player_wrapped_video_renderer_new (video_renderer, self); } - g_mutex_unlock (&self->lock); - - remove_ready_timeout_source (self); - self->target_state = GST_STATE_PLAYING; + self->play = gst_play_new (GST_PLAY_VIDEO_RENDERER (renderer)); - if (self->current_state < GST_STATE_PAUSED) - change_state (self, GST_PLAYER_STATE_BUFFERING); + if (signal_dispatcher != NULL) { + GMainContext *context = NULL; - if (self->current_state >= GST_STATE_PAUSED && !self->is_eos - && self->buffering >= 100 && !(self->seek_position != GST_CLOCK_TIME_NONE - || self->seek_pending)) { - state_ret = gst_element_set_state (self->playbin, GST_STATE_PLAYING); + g_object_get (signal_dispatcher, "application-context", &context, NULL); + self->signal_adapter = + gst_play_signal_adapter_new_with_main_context (self->play, context); + g_main_context_unref (context); } else { - state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED); + self->signal_adapter = gst_play_signal_adapter_new (self->play); } - if (state_ret == GST_STATE_CHANGE_FAILURE) { - emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED, - "Failed to play")); - return G_SOURCE_REMOVE; - } else if (state_ret == GST_STATE_CHANGE_NO_PREROLL) { - self->is_live = TRUE; - GST_DEBUG_OBJECT (self, "Pipeline is live"); - } + gst_object_ref_sink (self); - if (self->is_eos) { - gboolean ret; - - GST_DEBUG_OBJECT (self, "Was EOS, seeking to beginning"); - self->is_eos = FALSE; - ret = - gst_element_seek_simple (self->playbin, GST_FORMAT_TIME, - GST_SEEK_FLAG_FLUSH, 0); - if (!ret) { - GST_ERROR_OBJECT (self, "Seek to beginning failed"); - gst_player_stop_internal (self, TRUE); - gst_player_play_internal (self); - } - } + g_signal_connect (self->signal_adapter, "uri-loaded", + G_CALLBACK (uri_loaded_cb), self); + g_signal_connect (self->signal_adapter, "position-updated", + G_CALLBACK (position_updated_cb), self); + g_signal_connect (self->signal_adapter, "duration-changed", + G_CALLBACK (duration_changed_cb), self); + g_signal_connect (self->signal_adapter, "state-changed", + G_CALLBACK (state_changed_cb), self); + g_signal_connect (self->signal_adapter, "buffering", + G_CALLBACK (buffering_cb), self); + g_signal_connect (self->signal_adapter, "end-of-stream", + G_CALLBACK (end_of_stream_cb), self); + g_signal_connect (self->signal_adapter, "error", G_CALLBACK (error_cb), self); + g_signal_connect (self->signal_adapter, "video-dimensions-changed", + G_CALLBACK (dimensions_changed_cb), self); + g_signal_connect (self->signal_adapter, "media-info-updated", + G_CALLBACK (media_info_cb), self); + g_signal_connect (self->signal_adapter, "volume-changed", + G_CALLBACK (volume_cb), self); + g_signal_connect (self->signal_adapter, "mute-changed", G_CALLBACK (mute_cb), + self); + g_signal_connect (self->signal_adapter, "warning", G_CALLBACK (warning_cb), + self); + g_signal_connect (self->signal_adapter, "seek-done", + G_CALLBACK (seek_done_cb), self); + + if (video_renderer) + g_object_unref (video_renderer); + if (signal_dispatcher) + g_object_unref (signal_dispatcher); - return G_SOURCE_REMOVE; + return self; } /** @@ -3134,64 +636,7 @@ gst_player_play (GstPlayer * self) { g_return_if_fail (GST_IS_PLAYER (self)); - g_mutex_lock (&self->lock); - self->inhibit_sigs = FALSE; - g_mutex_unlock (&self->lock); - - g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT, - gst_player_play_internal, self, NULL); -} - -static gboolean -gst_player_pause_internal (gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - GstStateChangeReturn state_ret; - - GST_DEBUG_OBJECT (self, "Pause"); - - g_mutex_lock (&self->lock); - if (!self->uri) { - g_mutex_unlock (&self->lock); - return G_SOURCE_REMOVE; - } - g_mutex_unlock (&self->lock); - - tick_cb (self); - remove_tick_source (self); - remove_ready_timeout_source (self); - - self->target_state = GST_STATE_PAUSED; - - if (self->current_state < GST_STATE_PAUSED) - change_state (self, GST_PLAYER_STATE_BUFFERING); - - state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED); - if (state_ret == GST_STATE_CHANGE_FAILURE) { - emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED, - "Failed to pause")); - return G_SOURCE_REMOVE; - } else if (state_ret == GST_STATE_CHANGE_NO_PREROLL) { - self->is_live = TRUE; - GST_DEBUG_OBJECT (self, "Pipeline is live"); - } - - if (self->is_eos) { - gboolean ret; - - GST_DEBUG_OBJECT (self, "Was EOS, seeking to beginning"); - self->is_eos = FALSE; - ret = - gst_element_seek_simple (self->playbin, GST_FORMAT_TIME, - GST_SEEK_FLAG_FLUSH, 0); - if (!ret) { - GST_ERROR_OBJECT (self, "Seek to beginning failed"); - gst_player_stop_internal (self, TRUE); - gst_player_pause_internal (self); - } - } - - return G_SOURCE_REMOVE; + gst_play_play (self->play); } /** @@ -3205,83 +650,9 @@ gst_player_pause (GstPlayer * self) { g_return_if_fail (GST_IS_PLAYER (self)); - g_mutex_lock (&self->lock); - self->inhibit_sigs = FALSE; - g_mutex_unlock (&self->lock); - - g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT, - gst_player_pause_internal, self, NULL); -} - -static void -gst_player_stop_internal (GstPlayer * self, gboolean transient) -{ - /* directly return if we're already stopped */ - if (self->current_state <= GST_STATE_READY && - self->target_state <= GST_STATE_READY) - return; - - GST_DEBUG_OBJECT (self, "Stop (transient %d)", transient); - - tick_cb (self); - remove_tick_source (self); - - add_ready_timeout_source (self); - - self->target_state = GST_STATE_NULL; - self->current_state = GST_STATE_READY; - self->is_live = FALSE; - self->is_eos = FALSE; - gst_bus_set_flushing (self->bus, TRUE); - gst_element_set_state (self->playbin, GST_STATE_READY); - gst_bus_set_flushing (self->bus, FALSE); - change_state (self, transient - && self->app_state != - GST_PLAYER_STATE_STOPPED ? GST_PLAYER_STATE_BUFFERING : - GST_PLAYER_STATE_STOPPED); - self->buffering = 100; - self->cached_duration = GST_CLOCK_TIME_NONE; - g_mutex_lock (&self->lock); - if (self->media_info) { - g_object_unref (self->media_info); - self->media_info = NULL; - } - if (self->global_tags) { - gst_tag_list_unref (self->global_tags); - self->global_tags = NULL; - } - self->seek_pending = FALSE; - remove_seek_source (self); - self->seek_position = GST_CLOCK_TIME_NONE; - self->last_seek_time = GST_CLOCK_TIME_NONE; - self->rate = 1.0; - if (self->collection) { - if (self->stream_notify_id) - g_signal_handler_disconnect (self->collection, self->stream_notify_id); - self->stream_notify_id = 0; - gst_object_unref (self->collection); - self->collection = NULL; - } - g_free (self->video_sid); - g_free (self->audio_sid); - g_free (self->subtitle_sid); - self->video_sid = NULL; - self->audio_sid = NULL; - self->subtitle_sid = NULL; - g_mutex_unlock (&self->lock); -} - -static gboolean -gst_player_stop_internal_dispatch (gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - - gst_player_stop_internal (self, FALSE); - - return G_SOURCE_REMOVE; + gst_play_pause (self->play); } - /** * gst_player_stop: * @player: #GstPlayer instance @@ -3294,97 +665,7 @@ gst_player_stop (GstPlayer * self) { g_return_if_fail (GST_IS_PLAYER (self)); - g_mutex_lock (&self->lock); - self->inhibit_sigs = TRUE; - g_mutex_unlock (&self->lock); - - g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT, - gst_player_stop_internal_dispatch, self, NULL); -} - -/* Must be called with lock from main context, releases lock! */ -static void -gst_player_seek_internal_locked (GstPlayer * self) -{ - gboolean ret; - GstClockTime position; - gdouble rate; - GstStateChangeReturn state_ret; - GstEvent *s_event; - GstSeekFlags flags = 0; - gboolean accurate = FALSE; - - remove_seek_source (self); - - /* Only seek in PAUSED */ - if (self->current_state < GST_STATE_PAUSED) { - return; - } else if (self->current_state != GST_STATE_PAUSED) { - g_mutex_unlock (&self->lock); - state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED); - if (state_ret == GST_STATE_CHANGE_FAILURE) { - emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED, - "Failed to seek")); - g_mutex_lock (&self->lock); - return; - } - g_mutex_lock (&self->lock); - return; - } - - self->last_seek_time = gst_util_get_timestamp (); - position = self->seek_position; - self->seek_position = GST_CLOCK_TIME_NONE; - self->seek_pending = TRUE; - rate = self->rate; - g_mutex_unlock (&self->lock); - - remove_tick_source (self); - self->is_eos = FALSE; - - flags |= GST_SEEK_FLAG_FLUSH; - - accurate = gst_player_config_get_seek_accurate (self->config); - - if (accurate) { - flags |= GST_SEEK_FLAG_ACCURATE; - } else { - flags &= ~GST_SEEK_FLAG_ACCURATE; - } - - if (rate != 1.0) { - flags |= GST_SEEK_FLAG_TRICKMODE; - } - - if (rate >= 0.0) { - s_event = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, - GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE); - } else { - s_event = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, - GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0), GST_SEEK_TYPE_SET, position); - } - - GST_DEBUG_OBJECT (self, "Seek with rate %.2lf to %" GST_TIME_FORMAT, - rate, GST_TIME_ARGS (position)); - - ret = gst_element_send_event (self->playbin, s_event); - if (!ret) - emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED, - "Failed to seek to %" GST_TIME_FORMAT, GST_TIME_ARGS (position))); - - g_mutex_lock (&self->lock); -} - -static gboolean -gst_player_seek_internal (gpointer user_data) -{ - GstPlayer *self = GST_PLAYER (user_data); - - g_mutex_lock (&self->lock); - gst_player_seek_internal_locked (self); - g_mutex_unlock (&self->lock); - - return G_SOURCE_REMOVE; + gst_play_stop (self->play); } /** @@ -3435,58 +716,7 @@ gst_player_seek (GstPlayer * self, GstClockTime position) g_return_if_fail (GST_IS_PLAYER (self)); g_return_if_fail (GST_CLOCK_TIME_IS_VALID (position)); - g_mutex_lock (&self->lock); - if (self->media_info && !self->media_info->seekable) { - GST_DEBUG_OBJECT (self, "Media is not seekable"); - g_mutex_unlock (&self->lock); - return; - } - - self->seek_position = position; - - /* If there is no seek being dispatch to the main context currently do that, - * otherwise we just updated the seek position so that it will be taken by - * the seek handler from the main context instead of the old one. - */ - if (!self->seek_source) { - GstClockTime now = gst_util_get_timestamp (); - - /* If no seek is pending or it was started more than 250 mseconds ago seek - * immediately, otherwise wait until the 250 mseconds have passed */ - if (!self->seek_pending || (now - self->last_seek_time > 250 * GST_MSECOND)) { - self->seek_source = g_idle_source_new (); - g_source_set_callback (self->seek_source, - (GSourceFunc) gst_player_seek_internal, self, NULL); - GST_TRACE_OBJECT (self, "Dispatching seek to position %" GST_TIME_FORMAT, - GST_TIME_ARGS (position)); - g_source_attach (self->seek_source, self->context); - } else { - guint delay = 250000 - (now - self->last_seek_time) / 1000; - - /* Note that last_seek_time must be set to something at this point and - * it must be smaller than 250 mseconds */ - self->seek_source = g_timeout_source_new (delay); - g_source_set_callback (self->seek_source, - (GSourceFunc) gst_player_seek_internal, self, NULL); - - GST_TRACE_OBJECT (self, - "Delaying seek to position %" GST_TIME_FORMAT " by %u us", - GST_TIME_ARGS (position), delay); - g_source_attach (self->seek_source, self->context); - } - } - g_mutex_unlock (&self->lock); -} - -static void -remove_seek_source (GstPlayer * self) -{ - if (!self->seek_source) - return; - - g_source_destroy (self->seek_source); - g_source_unref (self->seek_source); - self->seek_source = NULL; + gst_play_seek (self->play, position); } /** @@ -3704,18 +934,15 @@ gst_player_get_pipeline (GstPlayer * self) GstPlayerMediaInfo * gst_player_get_media_info (GstPlayer * self) { - GstPlayerMediaInfo *info; + GstPlayMediaInfo *info; + GstPlayerMediaInfo *ret; g_return_val_if_fail (GST_IS_PLAYER (self), NULL); - if (!self->media_info) - return NULL; - - g_mutex_lock (&self->lock); - info = gst_player_media_info_copy (self->media_info); - g_mutex_unlock (&self->lock); - - return info; + info = gst_play_get_media_info (self->play); + ret = gst_player_media_info_wrapped (info); + g_object_unref (info); + return ret; } /** @@ -3731,23 +958,17 @@ gst_player_get_media_info (GstPlayer * self) GstPlayerAudioInfo * gst_player_get_current_audio_track (GstPlayer * self) { - GstPlayerAudioInfo *info; + GstPlayAudioInfo *info; + GstPlayerAudioInfo *ret = NULL; g_return_val_if_fail (GST_IS_PLAYER (self), NULL); - if (!is_track_enabled (self, GST_PLAY_FLAG_AUDIO)) - return NULL; - - if (self->use_playbin3) { - info = (GstPlayerAudioInfo *) - gst_player_stream_info_get_current_from_stream_id (self, - self->audio_sid, GST_TYPE_PLAYER_AUDIO_INFO); - } else { - info = (GstPlayerAudioInfo *) gst_player_stream_info_get_current (self, - "current-audio", GST_TYPE_PLAYER_AUDIO_INFO); + info = gst_play_get_current_audio_track (self->play); + if (info != NULL) { + ret = gst_player_audio_info_wrapped (info); + g_object_unref (info); } - - return info; + return ret; } /** @@ -3763,23 +984,17 @@ gst_player_get_current_audio_track (GstPlayer * self) GstPlayerVideoInfo * gst_player_get_current_video_track (GstPlayer * self) { - GstPlayerVideoInfo *info; + GstPlayVideoInfo *info; + GstPlayerVideoInfo *ret = NULL; g_return_val_if_fail (GST_IS_PLAYER (self), NULL); - if (!is_track_enabled (self, GST_PLAY_FLAG_VIDEO)) - return NULL; - - if (self->use_playbin3) { - info = (GstPlayerVideoInfo *) - gst_player_stream_info_get_current_from_stream_id (self, - self->video_sid, GST_TYPE_PLAYER_VIDEO_INFO); - } else { - info = (GstPlayerVideoInfo *) gst_player_stream_info_get_current (self, - "current-video", GST_TYPE_PLAYER_VIDEO_INFO); + info = gst_play_get_current_video_track (self->play); + if (info != NULL) { + ret = gst_player_video_info_wrapped (info); + g_object_unref (info); } - - return info; + return ret; } /** @@ -3795,49 +1010,16 @@ gst_player_get_current_video_track (GstPlayer * self) GstPlayerSubtitleInfo * gst_player_get_current_subtitle_track (GstPlayer * self) { - GstPlayerSubtitleInfo *info; + GstPlaySubtitleInfo *info; + GstPlayerSubtitleInfo *ret = NULL; g_return_val_if_fail (GST_IS_PLAYER (self), NULL); - if (!is_track_enabled (self, GST_PLAY_FLAG_SUBTITLE)) - return NULL; - - if (self->use_playbin3) { - info = (GstPlayerSubtitleInfo *) - gst_player_stream_info_get_current_from_stream_id (self, - self->subtitle_sid, GST_TYPE_PLAYER_SUBTITLE_INFO); - } else { - info = (GstPlayerSubtitleInfo *) gst_player_stream_info_get_current (self, - "current-text", GST_TYPE_PLAYER_SUBTITLE_INFO); - } - - return info; -} - -/* Must be called with lock */ -static gboolean -gst_player_select_streams (GstPlayer * self) -{ - GList *stream_list = NULL; - gboolean ret = FALSE; - - if (self->audio_sid) - stream_list = g_list_append (stream_list, g_strdup (self->audio_sid)); - if (self->video_sid) - stream_list = g_list_append (stream_list, g_strdup (self->video_sid)); - if (self->subtitle_sid) - stream_list = g_list_append (stream_list, g_strdup (self->subtitle_sid)); - - g_mutex_unlock (&self->lock); - if (stream_list) { - ret = gst_element_send_event (self->playbin, - gst_event_new_select_streams (stream_list)); - g_list_free_full (stream_list, g_free); - } else { - GST_ERROR_OBJECT (self, "No available streams for select-streams"); + info = gst_play_get_current_subtitle_track (self->play); + if (info != NULL) { + ret = gst_player_subtitle_info_wrapped (info); + g_object_unref (info); } - g_mutex_lock (&self->lock); - return ret; } @@ -3853,33 +1035,9 @@ gst_player_select_streams (GstPlayer * self) gboolean gst_player_set_audio_track (GstPlayer * self, gint stream_index) { - GstPlayerStreamInfo *info; - gboolean ret = TRUE; - g_return_val_if_fail (GST_IS_PLAYER (self), 0); - g_mutex_lock (&self->lock); - info = gst_player_stream_info_find (self->media_info, - GST_TYPE_PLAYER_AUDIO_INFO, stream_index); - g_mutex_unlock (&self->lock); - if (!info) { - GST_ERROR_OBJECT (self, "invalid audio stream index %d", stream_index); - return FALSE; - } - - if (self->use_playbin3) { - g_mutex_lock (&self->lock); - g_free (self->audio_sid); - self->audio_sid = g_strdup (info->stream_id); - ret = gst_player_select_streams (self); - g_mutex_unlock (&self->lock); - } else { - g_object_set (G_OBJECT (self->playbin), "current-audio", stream_index, - NULL); - } - - GST_DEBUG_OBJECT (self, "set stream index '%d'", stream_index); - return ret; + return gst_play_set_audio_track (self->play, stream_index); } /** @@ -3894,34 +1052,9 @@ gst_player_set_audio_track (GstPlayer * self, gint stream_index) gboolean gst_player_set_video_track (GstPlayer * self, gint stream_index) { - GstPlayerStreamInfo *info; - gboolean ret = TRUE; - g_return_val_if_fail (GST_IS_PLAYER (self), 0); - /* check if stream_index exist in our internal media_info list */ - g_mutex_lock (&self->lock); - info = gst_player_stream_info_find (self->media_info, - GST_TYPE_PLAYER_VIDEO_INFO, stream_index); - g_mutex_unlock (&self->lock); - if (!info) { - GST_ERROR_OBJECT (self, "invalid video stream index %d", stream_index); - return FALSE; - } - - if (self->use_playbin3) { - g_mutex_lock (&self->lock); - g_free (self->video_sid); - self->video_sid = g_strdup (info->stream_id); - ret = gst_player_select_streams (self); - g_mutex_unlock (&self->lock); - } else { - g_object_set (G_OBJECT (self->playbin), "current-video", stream_index, - NULL); - } - - GST_DEBUG_OBJECT (self, "set stream index '%d'", stream_index); - return ret; + return gst_play_set_video_track (self->play, stream_index); } /** @@ -3936,32 +1069,9 @@ gst_player_set_video_track (GstPlayer * self, gint stream_index) gboolean gst_player_set_subtitle_track (GstPlayer * self, gint stream_index) { - GstPlayerStreamInfo *info; - gboolean ret = TRUE; - g_return_val_if_fail (GST_IS_PLAYER (self), 0); - g_mutex_lock (&self->lock); - info = gst_player_stream_info_find (self->media_info, - GST_TYPE_PLAYER_SUBTITLE_INFO, stream_index); - g_mutex_unlock (&self->lock); - if (!info) { - GST_ERROR_OBJECT (self, "invalid subtitle stream index %d", stream_index); - return FALSE; - } - - if (self->use_playbin3) { - g_mutex_lock (&self->lock); - g_free (self->subtitle_sid); - self->subtitle_sid = g_strdup (info->stream_id); - ret = gst_player_select_streams (self); - g_mutex_unlock (&self->lock); - } else { - g_object_set (G_OBJECT (self->playbin), "current-text", stream_index, NULL); - } - - GST_DEBUG_OBJECT (self, "set stream index '%d'", stream_index); - return ret; + return gst_play_set_subtitle_track (self->play, stream_index); } /** @@ -3976,12 +1086,7 @@ gst_player_set_audio_track_enabled (GstPlayer * self, gboolean enabled) { g_return_if_fail (GST_IS_PLAYER (self)); - if (enabled) - player_set_flag (self, GST_PLAY_FLAG_AUDIO); - else - player_clear_flag (self, GST_PLAY_FLAG_AUDIO); - - GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled"); + gst_play_set_audio_track_enabled (self->play, enabled); } /** @@ -3996,12 +1101,7 @@ gst_player_set_video_track_enabled (GstPlayer * self, gboolean enabled) { g_return_if_fail (GST_IS_PLAYER (self)); - if (enabled) - player_set_flag (self, GST_PLAY_FLAG_VIDEO); - else - player_clear_flag (self, GST_PLAY_FLAG_VIDEO); - - GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled"); + gst_play_set_video_track_enabled (self->play, enabled); } /** @@ -4016,12 +1116,7 @@ gst_player_set_subtitle_track_enabled (GstPlayer * self, gboolean enabled) { g_return_if_fail (GST_IS_PLAYER (self)); - if (enabled) - player_set_flag (self, GST_PLAY_FLAG_SUBTITLE); - else - player_clear_flag (self, GST_PLAY_FLAG_SUBTITLE); - - GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled"); + gst_play_set_subtitle_track_enabled (self->play, enabled); } /** @@ -4038,29 +1133,7 @@ gst_player_set_visualization (GstPlayer * self, const gchar * name) { g_return_val_if_fail (GST_IS_PLAYER (self), FALSE); - g_mutex_lock (&self->lock); - if (self->current_vis_element) { - gst_object_unref (self->current_vis_element); - self->current_vis_element = NULL; - } - - if (name) { - self->current_vis_element = gst_element_factory_make (name, NULL); - if (!self->current_vis_element) - goto error_no_element; - gst_object_ref_sink (self->current_vis_element); - } - g_object_set (self->playbin, "vis-plugin", self->current_vis_element, NULL); - - g_mutex_unlock (&self->lock); - GST_DEBUG_OBJECT (self, "set vis-plugin to '%s'", name); - - return TRUE; - -error_no_element: - g_mutex_unlock (&self->lock); - GST_WARNING_OBJECT (self, "could not find visualization '%s'", name); - return FALSE; + return gst_play_set_visualization (self->play, name); } /** @@ -4073,26 +1146,9 @@ error_no_element: gchar * gst_player_get_current_visualization (GstPlayer * self) { - gchar *name = NULL; - GstElement *vis_plugin = NULL; - g_return_val_if_fail (GST_IS_PLAYER (self), NULL); - if (!is_track_enabled (self, GST_PLAY_FLAG_VIS)) - return NULL; - - g_object_get (self->playbin, "vis-plugin", &vis_plugin, NULL); - - if (vis_plugin) { - GstElementFactory *factory = gst_element_get_factory (vis_plugin); - if (factory) - name = g_strdup (gst_plugin_feature_get_name (factory)); - gst_object_unref (vis_plugin); - } - - GST_DEBUG_OBJECT (self, "vis-plugin '%s' %p", name, vis_plugin); - - return name; + return gst_play_get_current_visualization (self->play); } /** @@ -4107,13 +1163,7 @@ gst_player_set_visualization_enabled (GstPlayer * self, gboolean enabled) { g_return_if_fail (GST_IS_PLAYER (self)); - if (enabled) - player_set_flag (self, GST_PLAY_FLAG_VIS); - else - player_clear_flag (self, GST_PLAY_FLAG_VIS); - - GST_DEBUG_OBJECT (self, "visualization is '%s'", - enabled ? "Enabled" : "Disabled"); + gst_play_set_visualization_enabled (self->play, enabled); } struct CBChannelMap @@ -4129,28 +1179,6 @@ static const struct CBChannelMap cb_channel_map[] = { /* GST_PLAYER_COLOR_BALANCE_HUE */ {"HUE", "hue"}, }; -static GstColorBalanceChannel * -gst_player_color_balance_find_channel (GstPlayer * self, - GstPlayerColorBalanceType type) -{ - GstColorBalanceChannel *channel; - const GList *l, *channels; - - if (type < GST_PLAYER_COLOR_BALANCE_BRIGHTNESS || - type > GST_PLAYER_COLOR_BALANCE_HUE) - return NULL; - - channels = - gst_color_balance_list_channels (GST_COLOR_BALANCE (self->playbin)); - for (l = channels; l; l = l->next) { - channel = l->data; - if (g_strrstr (channel->label, cb_channel_map[type].label)) - return channel; - } - - return NULL; -} - /** * gst_player_has_color_balance: * @player:#GstPlayer instance @@ -4163,16 +1191,9 @@ gst_player_color_balance_find_channel (GstPlayer * self, gboolean gst_player_has_color_balance (GstPlayer * self) { - const GList *channels; - g_return_val_if_fail (GST_IS_PLAYER (self), FALSE); - if (!GST_IS_COLOR_BALANCE (self->playbin)) - return FALSE; - - channels = - gst_color_balance_list_channels (GST_COLOR_BALANCE (self->playbin)); - return (channels != NULL); + return gst_play_has_color_balance (self->play); } /** @@ -4188,27 +1209,11 @@ void gst_player_set_color_balance (GstPlayer * self, GstPlayerColorBalanceType type, gdouble value) { - GstColorBalanceChannel *channel; - gdouble new_val; - g_return_if_fail (GST_IS_PLAYER (self)); g_return_if_fail (value >= 0.0 && value <= 1.0); - if (!GST_IS_COLOR_BALANCE (self->playbin)) - return; - - channel = gst_player_color_balance_find_channel (self, type); - if (!channel) - return; - - value = CLAMP (value, 0.0, 1.0); - - /* Convert to channel range */ - new_val = channel->min_value + value * ((gdouble) channel->max_value - - (gdouble) channel->min_value); - - gst_color_balance_set_value (GST_COLOR_BALANCE (self->playbin), channel, - new_val); + gst_play_set_color_balance (self->play, (GstPlayColorBalanceType) type, + value); } /** @@ -4224,24 +1229,10 @@ gst_player_set_color_balance (GstPlayer * self, GstPlayerColorBalanceType type, gdouble gst_player_get_color_balance (GstPlayer * self, GstPlayerColorBalanceType type) { - GstColorBalanceChannel *channel; - gint value; - g_return_val_if_fail (GST_IS_PLAYER (self), -1); - if (!GST_IS_COLOR_BALANCE (self->playbin)) - return -1; - - channel = gst_player_color_balance_find_channel (self, type); - if (!channel) - return -1; - - value = gst_color_balance_get_value (GST_COLOR_BALANCE (self->playbin), - channel); - - return ((gdouble) value - - (gdouble) channel->min_value) / ((gdouble) channel->max_value - - (gdouble) channel->min_value); + return gst_play_get_color_balance (self->play, + (GstPlayColorBalanceType) type); } /** @@ -4558,21 +1549,7 @@ gst_player_set_config (GstPlayer * self, GstStructure * config) g_return_val_if_fail (GST_IS_PLAYER (self), FALSE); g_return_val_if_fail (config != NULL, FALSE); - g_mutex_lock (&self->lock); - - if (self->app_state != GST_PLAYER_STATE_STOPPED) { - GST_INFO_OBJECT (self, "can't change config while player is %s", - gst_player_state_get_name (self->app_state)); - g_mutex_unlock (&self->lock); - return FALSE; - } - - if (self->config) - gst_structure_free (self->config); - self->config = config; - g_mutex_unlock (&self->lock); - - return TRUE; + return gst_play_set_config (self->play, config); } /** @@ -4591,15 +1568,9 @@ gst_player_set_config (GstPlayer * self, GstStructure * config) GstStructure * gst_player_get_config (GstPlayer * self) { - GstStructure *ret; - g_return_val_if_fail (GST_IS_PLAYER (self), NULL); - g_mutex_lock (&self->lock); - ret = gst_structure_copy (self->config); - g_mutex_unlock (&self->lock); - - return ret; + return gst_play_get_config (self->play); } /** @@ -4631,6 +1602,7 @@ gst_player_config_set_user_agent (GstStructure * config, const gchar * agent) * gst_player_config_set_user_agent() if any. * * Returns: (transfer full): the configured agent, or %NULL + * * Since: 1.10 */ gchar * @@ -4653,6 +1625,7 @@ gst_player_config_get_user_agent (const GstStructure * config) * * set interval in milliseconds between two position-updated signals. * pass 0 to stop updating the position. + * * Since: 1.10 */ void @@ -4755,78 +1728,8 @@ GstSample * gst_player_get_video_snapshot (GstPlayer * self, GstPlayerSnapshotFormat format, const GstStructure * config) { - gint video_tracks = 0; - GstSample *sample = NULL; - GstCaps *caps = NULL; - gint width = -1; - gint height = -1; - gint par_n = 1; - gint par_d = 1; g_return_val_if_fail (GST_IS_PLAYER (self), NULL); - g_object_get (self->playbin, "n-video", &video_tracks, NULL); - if (video_tracks == 0) { - GST_DEBUG_OBJECT (self, "total video track num is 0"); - return NULL; - } - - switch (format) { - case GST_PLAYER_THUMBNAIL_RAW_xRGB: - caps = gst_caps_new_simple ("video/x-raw", - "format", G_TYPE_STRING, "xRGB", NULL); - break; - case GST_PLAYER_THUMBNAIL_RAW_BGRx: - caps = gst_caps_new_simple ("video/x-raw", - "format", G_TYPE_STRING, "BGRx", NULL); - break; - case GST_PLAYER_THUMBNAIL_JPG: - caps = gst_caps_new_empty_simple ("image/jpeg"); - break; - case GST_PLAYER_THUMBNAIL_PNG: - caps = gst_caps_new_empty_simple ("image/png"); - break; - case GST_PLAYER_THUMBNAIL_RAW_NATIVE: - default: - caps = gst_caps_new_empty_simple ("video/x-raw"); - break; - } - - if (NULL != config) { - if (!gst_structure_get_int (config, "width", &width)) - width = -1; - if (!gst_structure_get_int (config, "height", &height)) - height = -1; - if (!gst_structure_get_fraction (config, "pixel-aspect-ratio", &par_n, - &par_d)) { - if (format != GST_PLAYER_THUMBNAIL_RAW_NATIVE) { - par_n = 1; - par_d = 1; - } else { - par_n = 0; - par_d = 0; - } - } - } - - if (width > 0 && height > 0) { - gst_caps_set_simple (caps, "width", G_TYPE_INT, width, - "height", G_TYPE_INT, height, NULL); - } - - if (format != GST_PLAYER_THUMBNAIL_RAW_NATIVE) { - gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION, - par_n, par_d, NULL); - } else if (NULL != config && par_n != 0 && par_d != 0) { - gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION, - par_n, par_d, NULL); - } - - g_signal_emit_by_name (self->playbin, "convert-sample", caps, &sample); - gst_caps_unref (caps); - if (!sample) { - GST_WARNING_OBJECT (self, "Failed to retrieve or convert video frame"); - return NULL; - } - - return sample; + return gst_play_get_video_snapshot (self->play, + (GstPlaySnapshotFormat) format, config); } diff --git a/gst-libs/gst/player/meson.build b/gst-libs/gst/player/meson.build index ea8fcc8..1056494 100644 --- a/gst-libs/gst/player/meson.build +++ b/gst-libs/gst/player/meson.build @@ -5,6 +5,7 @@ gstplayer_sources = [ 'gstplayer-media-info.c', 'gstplayer-g-main-context-signal-dispatcher.c', 'gstplayer-video-overlay-video-renderer.c', + 'gstplayer-wrapped-video-renderer.c', 'gstplayer-visualization.c', ] @@ -31,7 +32,7 @@ gstplayer = library('gstplayer-' + api_version, soversion : soversion, darwin_versions : osxversion, install : true, - dependencies : [gstbase_dep, gstvideo_dep, gstaudio_dep, + dependencies : [gstbase_dep, gstvideo_dep, gstaudio_dep, gstplay_dep, gsttag_dep, gstpbutils_dep], ) @@ -56,7 +57,7 @@ if build_gir 'GstAudio-1.0', 'GstTag-1.0'], install : true, extra_args : gir_init_section + ['-DGST_USE_UNSTABLE_API'] + ['--c-include=gst/player/player.h'], - dependencies : [gstbase_dep, gstvideo_dep, gstaudio_dep, + dependencies : [gstbase_dep, gstvideo_dep, gstaudio_dep, gstplay_dep, gsttag_dep, gstpbutils_dep] ) gen_sources += player_gir @@ -65,7 +66,7 @@ endif gstplayer_dep = declare_dependency(link_with : gstplayer, include_directories : [libsinc], sources: gen_sources, - dependencies : [gstbase_dep, gstvideo_dep, gstaudio_dep, + dependencies : [gstbase_dep, gstvideo_dep, gstaudio_dep, gstplay_dep, gsttag_dep, gstpbutils_dep]) meson.override_dependency('gstreamer-player-1.0', gstplayer_dep) -- 2.7.4