From 9f11987dd7af873f13dca745d6816e8f98d51fc1 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Tue, 30 Jan 2018 17:52:57 -0300 Subject: [PATCH] Build WebRTC support * Update libvpx to required libwebrtc version * Build required webcam drivers * Mark wpewebkit as dependending on libvpx * Backport playbin3 from GStreamer 1.14 * Build audio recording support * Implement an alsa based DeviceProvider * gstreamer: Backport latest GstStream* code --- board/raspberrypi/rpi23-linux-4.9.config | 81 + configs/raspberrypi3_wpe_defconfig | 5 + configs/raspberrypi3_wpe_ml_defconfig | 5 + ...0001-alsa-Implement-a-DeviceProvider.patch | 523 ++ ...ase-0001-Backport-playbin3-from-1.14.patch | 8328 +++++++++++++++++ ...ckport-GstStreamCollection-from-1.14.patch | 151 + .../0002-Backport-GstStream-from-1.14.patch | 156 + ...rn-a-group_id-of-0-add-GST_GROUP_ID_.patch | 84 + ...px_mem.h-Fix-compilation-with-uClibc.patch | 30 - package/libvpx/libvpx.mk | 6 +- package/wpe/wpewebkit/Config.in | 6 + package/wpe/wpewebkit/wpewebkit.mk | 6 +- 12 files changed, 9346 insertions(+), 35 deletions(-) create mode 100644 package/gstreamer1/gst1-plugins-base/1.14.4/0001-alsa-Implement-a-DeviceProvider.patch create mode 100644 package/gstreamer1/gst1-plugins-base/gst-plugins-base-0001-Backport-playbin3-from-1.14.patch create mode 100644 package/gstreamer1/gstreamer1/0001-Backport-GstStreamCollection-from-1.14.patch create mode 100644 package/gstreamer1/gstreamer1/0002-Backport-GstStream-from-1.14.patch create mode 100644 package/gstreamer1/gstreamer1/gstreamer-0001-utils-Never-return-a-group_id-of-0-add-GST_GROUP_ID_.patch delete mode 100644 package/libvpx/0001-vpx_mem-vpx_mem.h-Fix-compilation-with-uClibc.patch diff --git a/board/raspberrypi/rpi23-linux-4.9.config b/board/raspberrypi/rpi23-linux-4.9.config index 33a8d9cb73dc..76991d572bc9 100644 --- a/board/raspberrypi/rpi23-linux-4.9.config +++ b/board/raspberrypi/rpi23-linux-4.9.config @@ -191,7 +191,13 @@ CONFIG_REGULATOR=y CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_CAMERA_SUPPORT=y CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_DEV=m +CONFIG_VIDEO_V4L2=y CONFIG_VIDEO_BCM2835=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_V4L_TEST_DRIVERS=y CONFIG_FB=y CONFIG_FIRMWARE_EDID=y CONFIG_FB_BCM2708=y @@ -210,6 +216,12 @@ CONFIG_SND_BCM2835=y # CONFIG_SND_SPI is not set CONFIG_HIDRAW=y CONFIG_UHID=y + +CONFIG_SND_ARM=y +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=y + CONFIG_HID_MULTITOUCH=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y @@ -268,3 +280,72 @@ CONFIG_ARM_CRYPTO=y CONFIG_CRYPTO_SHA1_ARM_NEON=y CONFIG_CRYPTO_SHA512_ARM=y CONFIG_CRYPTO_AES_ARM_BS=y + +# +# Webcam devices +# +CONFIG_USB_VIDEO_CLASS=y +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +CONFIG_USB_GSPCA=m +CONFIG_USB_M5602=m +CONFIG_USB_STV06XX=m +CONFIG_USB_GL860=m +CONFIG_USB_GSPCA_BENQ=m +CONFIG_USB_GSPCA_CONEX=m +CONFIG_USB_GSPCA_CPIA1=m +CONFIG_USB_GSPCA_ETOMS=m +CONFIG_USB_GSPCA_FINEPIX=m +CONFIG_USB_GSPCA_JEILINJ=m +CONFIG_USB_GSPCA_JL2005BCD=m +CONFIG_USB_GSPCA_KINECT=m +CONFIG_USB_GSPCA_KONICA=m +CONFIG_USB_GSPCA_MARS=m +CONFIG_USB_GSPCA_MR97310A=m +CONFIG_USB_GSPCA_NW80X=m +CONFIG_USB_GSPCA_OV519=m +CONFIG_USB_GSPCA_OV534=m +CONFIG_USB_GSPCA_OV534_9=m +CONFIG_USB_GSPCA_PAC207=m +CONFIG_USB_GSPCA_PAC7302=m +CONFIG_USB_GSPCA_PAC7311=m +CONFIG_USB_GSPCA_SE401=m +CONFIG_USB_GSPCA_SN9C2028=m +CONFIG_USB_GSPCA_SN9C20X=m +CONFIG_USB_GSPCA_SONIXB=m +CONFIG_USB_GSPCA_SONIXJ=m +CONFIG_USB_GSPCA_SPCA500=m +CONFIG_USB_GSPCA_SPCA501=m +CONFIG_USB_GSPCA_SPCA505=m +CONFIG_USB_GSPCA_SPCA506=m +CONFIG_USB_GSPCA_SPCA508=m +CONFIG_USB_GSPCA_SPCA561=m +CONFIG_USB_GSPCA_SPCA1528=m +CONFIG_USB_GSPCA_SQ905=m +CONFIG_USB_GSPCA_SQ905C=m +CONFIG_USB_GSPCA_SQ930X=m +CONFIG_USB_GSPCA_STK014=m +CONFIG_USB_GSPCA_STK1135=m +CONFIG_USB_GSPCA_STV0680=m +CONFIG_USB_GSPCA_SUNPLUS=m +CONFIG_USB_GSPCA_T613=m +CONFIG_USB_GSPCA_TOPRO=m +CONFIG_USB_GSPCA_TV8532=m +CONFIG_USB_GSPCA_VC032X=m +CONFIG_USB_GSPCA_VICAM=m +CONFIG_USB_GSPCA_XIRLINK_CIT=m +CONFIG_USB_GSPCA_ZC3XX=m +CONFIG_USB_PWC=m +# CONFIG_USB_PWC_DEBUG is not set +CONFIG_USB_PWC_INPUT_EVDEV=y +CONFIG_VIDEO_CPIA2=m +CONFIG_USB_ZR364XX=m +CONFIG_USB_STKWEBCAM=m +CONFIG_USB_S2255=m +CONFIG_USB_SN9C102=m +CONFIG_VIDEO_USBTV=m + +CONFIG_USB_G_WEBCAM=y +CONFIG_USB_GADGET=y +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_USB_F_UVC=m \ No newline at end of file diff --git a/configs/raspberrypi3_wpe_defconfig b/configs/raspberrypi3_wpe_defconfig index 00f266c662a0..bc7415eec6ce 100644 --- a/configs/raspberrypi3_wpe_defconfig +++ b/configs/raspberrypi3_wpe_defconfig @@ -24,12 +24,17 @@ BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/raspberrypi/rpi23-linux-4.9.config" BR2_LINUX_KERNEL_LZ4=y BR2_LINUX_KERNEL_DTS_SUPPORT=y BR2_LINUX_KERNEL_INTREE_DTS_NAME="bcm2710-rpi-3-b bcm2710-rpi-3-b-plus bcm2710-rpi-cm3" +BR2_PACKAGE_LIBEVENT=y +BR2_PACKAGE_LIBVPX=y BR2_PACKAGE_BUSYBOX_SMP=y BR2_PACKAGE_GSTREAMER1=y BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_APP=y +BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_VIDEORATE=y BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_VIDEOCONVERT=y BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_GIO=y +BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_ALSA=y BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_OPUS=y +BR2_PACKAGE_GST1_PLUGINS_GOOD_PLUGIN_V4L2=y BR2_PACKAGE_GST1_PLUGINS_GOOD_PLUGIN_AUDIOFX=y BR2_PACKAGE_GST1_PLUGINS_GOOD_PLUGIN_AUDIOPARSERS=y BR2_PACKAGE_GST1_PLUGINS_GOOD_PLUGIN_AUTODETECT=y diff --git a/configs/raspberrypi3_wpe_ml_defconfig b/configs/raspberrypi3_wpe_ml_defconfig index 17f6feeda09a..c1695acadaec 100644 --- a/configs/raspberrypi3_wpe_ml_defconfig +++ b/configs/raspberrypi3_wpe_ml_defconfig @@ -24,13 +24,18 @@ BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/raspberrypi/rpi23-linux-4.9.config" BR2_LINUX_KERNEL_LZ4=y BR2_LINUX_KERNEL_DTS_SUPPORT=y BR2_LINUX_KERNEL_INTREE_DTS_NAME="bcm2710-rpi-3-b bcm2710-rpi-3-b-plus bcm2710-rpi-cm3" +BR2_PACKAGE_LIBEVENT=y +BR2_PACKAGE_LIBVPX=y BR2_PACKAGE_BUSYBOX_SMP=y BR2_PACKAGE_GSTREAMER1=y BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_APP=y +BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_ENCODING=y +BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_VIDEORATE=y BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_VIDEOCONVERT=y BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_GIO=y BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_ALSA=y BR2_PACKAGE_GST1_PLUGINS_BASE_PLUGIN_OPUS=y +BR2_PACKAGE_GST1_PLUGINS_GOOD_PLUGIN_V4L2=y BR2_PACKAGE_GST1_PLUGINS_GOOD_PLUGIN_AUDIOFX=y BR2_PACKAGE_GST1_PLUGINS_GOOD_PLUGIN_AUDIOPARSERS=y BR2_PACKAGE_GST1_PLUGINS_GOOD_PLUGIN_AUTODETECT=y diff --git a/package/gstreamer1/gst1-plugins-base/1.14.4/0001-alsa-Implement-a-DeviceProvider.patch b/package/gstreamer1/gst1-plugins-base/1.14.4/0001-alsa-Implement-a-DeviceProvider.patch new file mode 100644 index 000000000000..d2f27aeddb6f --- /dev/null +++ b/package/gstreamer1/gst1-plugins-base/1.14.4/0001-alsa-Implement-a-DeviceProvider.patch @@ -0,0 +1,523 @@ +From b09a9b52107e51cb3d421a16e12b4b31f7424f8d Mon Sep 17 00:00:00 2001 +From: Thibault Saunier +Date: Fri, 31 Aug 2018 18:33:43 -0300 +Subject: [PATCH] alsa: Implement a DeviceProvider + +--- + ext/alsa/Makefile.am | 2 +- + ext/alsa/gstalsadeviceprovider.c | 365 +++++++++++++++++++++++++++++++ + ext/alsa/gstalsadeviceprovider.h | 85 +++++++ + ext/alsa/gstalsaplugin.c | 6 +- + 5 files changed, 457 insertions(+), 3 deletions(-) + create mode 100644 ext/alsa/gstalsadeviceprovider.c + create mode 100644 ext/alsa/gstalsadeviceprovider.h + +diff --git a/ext/alsa/Makefile.am b/ext/alsa/Makefile.am +index 7f885c5be..93a8a2b98 100644 +--- a/ext/alsa/Makefile.am ++++ b/ext/alsa/Makefile.am +@@ -1,7 +1,7 @@ + plugin_LTLIBRARIES = libgstalsa.la + + libgstalsa_la_SOURCES = \ +- gstalsadeviceprobe.c \ ++ gstalsadeviceprovider.c \ + gstalsaplugin.c \ + gstalsasink.c \ + gstalsasrc.c \ +diff --git a/ext/alsa/gstalsadeviceprovider.c b/ext/alsa/gstalsadeviceprovider.c +new file mode 100644 +index 000000000..81d484e20 +--- /dev/null ++++ b/ext/alsa/gstalsadeviceprovider.c +@@ -0,0 +1,365 @@ ++/* GStreamer ++ * Copyright (C) 2012 Olivier Crete ++ * Copyright (C) 2018 Thibault Saunier ++ * ++ * alsadeviceprovider.c: alsa device probing and monitoring ++ * ++ * 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., 59 Temple Place - Suite 330, ++ * Boston, MA 02111-1307, USA. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include "gstalsadeviceprovider.h" ++#include ++#include ++ ++ ++static GstDevice *gst_alsa_device_new (const gchar * device_name, ++ GstCaps * caps, const gchar * internal_name, snd_pcm_stream_t stream, ++ GstStructure * properties); ++ ++G_DEFINE_TYPE (GstAlsaDeviceProvider, gst_alsa_device_provider, ++ GST_TYPE_DEVICE_PROVIDER); ++ ++static void gst_alsa_device_provider_finalize (GObject * object); ++static void gst_alsa_device_provider_set_property (GObject * object, ++ guint prop_id, const GValue * value, GParamSpec * pspec); ++static void gst_alsa_device_provider_get_property (GObject * object, ++ guint prop_id, GValue * value, GParamSpec * pspec); ++ ++ ++static GList *gst_alsa_device_provider_probe (GstDeviceProvider * provider); ++ ++static gboolean ++gst_alsa_device_provider_start (GstDeviceProvider * provider) ++{ ++ ++ g_list_free_full (gst_alsa_device_provider_probe (provider), ++ gst_object_unref); ++ return TRUE; ++} ++ ++static void ++gst_alsa_device_provider_stop (GstDeviceProvider * provider) ++{ ++ return; ++} ++ ++enum ++{ ++ PROP_0, ++ PROP_LAST ++}; ++ ++ ++static void ++gst_alsa_device_provider_class_init (GstAlsaDeviceProviderClass * klass) ++{ ++ GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ++ GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass); ++ ++ gobject_class->set_property = gst_alsa_device_provider_set_property; ++ gobject_class->get_property = gst_alsa_device_provider_get_property; ++ gobject_class->finalize = gst_alsa_device_provider_finalize; ++ ++ dm_class->probe = gst_alsa_device_provider_probe; ++ dm_class->start = gst_alsa_device_provider_start; ++ dm_class->stop = gst_alsa_device_provider_stop; ++ ++ gst_device_provider_class_set_static_metadata (dm_class, ++ "Alsa Device Provider", "Sink/Source/Audio", ++ "List and provider Alsa source and sink devices", ++ "Thibault Saunier "); ++} ++ ++static void ++gst_alsa_device_provider_init (GstAlsaDeviceProvider * self) ++{ ++} ++ ++static void ++gst_alsa_device_provider_finalize (GObject * object) ++{ ++ // GstAlsaDeviceProvider *self = GST_ALSA_DEVICE_PROVIDER (object); ++ ++ G_OBJECT_CLASS (gst_alsa_device_provider_parent_class)->finalize (object); ++} ++ ++ ++static void ++gst_alsa_device_provider_set_property (GObject * object, ++ guint prop_id, const GValue * value, GParamSpec * pspec) ++{ ++ // GstAlsaDeviceProvider *self = GST_ALSA_DEVICE_PROVIDER (object); ++ ++ switch (prop_id) { ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++gst_alsa_device_provider_get_property (GObject * object, ++ guint prop_id, GValue * value, GParamSpec * pspec) ++{ ++ // GstAlsaDeviceProvider *self = GST_ALSA_DEVICE_PROVIDER (object); ++ ++ switch (prop_id) { ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static GstDevice * ++add_device (GstDeviceProvider * provider, snd_ctl_t * info, ++ snd_pcm_stream_t stream, gchar * internal_name) ++{ ++ GstCaps *caps; ++ GstDevice *dev; ++ ++ caps = gst_caps_new_simple ("audio/x-raw", NULL, NULL); ++ ++ dev = gst_alsa_device_new ("Alsa device", ++ caps, internal_name, stream, gst_structure_new_empty ("alsa-proplist")); ++ ++ gst_device_provider_device_add (provider, gst_object_ref (dev)); ++ ++ return dev; ++} ++ ++static GList * ++gst_alsa_device_provider_probe (GstDeviceProvider * provider) ++{ ++ snd_ctl_t *handle; ++ int card, dev; ++ snd_ctl_card_info_t *info; ++ snd_pcm_info_t *pcminfo; ++ GList *list = NULL; ++ gint i; ++ gint streams[] = { SND_PCM_STREAM_CAPTURE, SND_PCM_STREAM_PLAYBACK }; ++ snd_pcm_stream_t stream; ++ ++ GST_ERROR ("Probing my dear"); ++ snd_ctl_card_info_malloc (&info); ++ snd_pcm_info_malloc (&pcminfo); ++ card = -1; ++ ++ if (snd_card_next (&card) < 0 || card < 0) { ++ /* no soundcard found */ ++ GST_WARNING ("No soundcard found"); ++ goto beach; ++ } ++ ++ for (i = 0; i < G_N_ELEMENTS (streams); i++) { ++ stream = streams[i]; ++ ++ while (card >= 0) { ++ gchar name[32]; ++ ++ g_snprintf (name, sizeof (name), "hw:%d", card); ++ if (snd_ctl_open (&handle, name, 0) < 0) { ++ goto next_card; ++ } ++ if (snd_ctl_card_info (handle, info) < 0) { ++ snd_ctl_close (handle); ++ goto next_card; ++ } ++ ++ dev = -1; ++ while (1) { ++ gchar *gst_device; ++ ++ snd_ctl_pcm_next_device (handle, &dev); ++ ++ if (dev < 0) ++ break; ++ snd_pcm_info_set_device (pcminfo, dev); ++ snd_pcm_info_set_subdevice (pcminfo, 0); ++ snd_pcm_info_set_stream (pcminfo, stream); ++ if (snd_ctl_pcm_info (handle, pcminfo) < 0) { ++ continue; ++ } ++ ++ gst_device = g_strdup_printf ("hw:%d,%d", card, dev); ++ list = ++ g_list_prepend (list, add_device (provider, handle, stream, ++ gst_device)); ++ } ++ snd_ctl_close (handle); ++ next_card: ++ if (snd_card_next (&card) < 0) { ++ break; ++ } ++ } ++ } ++ ++beach: ++ snd_ctl_card_info_free (info); ++ snd_pcm_info_free (pcminfo); ++ ++ return list; ++} ++ ++enum ++{ ++ PROP_INTERNAL_NAME = 1, ++}; ++ ++ ++G_DEFINE_TYPE (GstAlsaDevice, gst_alsa_device, GST_TYPE_DEVICE); ++ ++static void gst_alsa_device_finalize (GObject * object); ++static GstElement *gst_alsa_device_create_element (GstDevice * device, ++ const gchar * name); ++static gboolean gst_alsa_device_reconfigure_element (GstDevice * device, ++ GstElement * element); ++ ++static void ++gst_alsa_device_get_property (GObject * object, guint prop_id, ++ GValue * value, GParamSpec * pspec) ++{ ++ GstAlsaDevice *device; ++ ++ device = GST_ALSA_DEVICE_CAST (object); ++ ++ switch (prop_id) { ++ case PROP_INTERNAL_NAME: ++ g_value_set_string (value, device->internal_name); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++gst_alsa_device_set_property (GObject * object, guint prop_id, ++ const GValue * value, GParamSpec * pspec) ++{ ++ GstAlsaDevice *device; ++ ++ device = GST_ALSA_DEVICE_CAST (object); ++ ++ switch (prop_id) { ++ case PROP_INTERNAL_NAME: ++ device->internal_name = g_value_dup_string (value); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++ ++static void ++gst_alsa_device_class_init (GstAlsaDeviceClass * klass) ++{ ++ GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass); ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ dev_class->create_element = gst_alsa_device_create_element; ++ dev_class->reconfigure_element = gst_alsa_device_reconfigure_element; ++ ++ object_class->get_property = gst_alsa_device_get_property; ++ object_class->set_property = gst_alsa_device_set_property; ++ object_class->finalize = gst_alsa_device_finalize; ++ ++ g_object_class_install_property (object_class, PROP_INTERNAL_NAME, ++ g_param_spec_string ("internal-name", "Internal AlsaAudio device name", ++ "The internal name of the AlsaAudio device", "", ++ G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); ++} ++ ++static void ++gst_alsa_device_finalize (GObject * object) ++{ ++ GstAlsaDevice *device = GST_ALSA_DEVICE (object); ++ ++ g_free (device->internal_name); ++ ++ G_OBJECT_CLASS (gst_alsa_device_parent_class)->finalize (object); ++} ++ ++static void ++gst_alsa_device_init (GstAlsaDevice * device) ++{ ++} ++ ++static GstElement * ++gst_alsa_device_create_element (GstDevice * device, const gchar * name) ++{ ++ GstAlsaDevice *alsa_dev = GST_ALSA_DEVICE (device); ++ GstElement *elem; ++ ++ elem = gst_element_factory_make (alsa_dev->element, name); ++ g_object_set (elem, "device", alsa_dev->internal_name, NULL); ++ ++ return elem; ++} ++ ++static gboolean ++gst_alsa_device_reconfigure_element (GstDevice * device, GstElement * element) ++{ ++ /* FIXME */ ++ ++ return TRUE; ++} ++ ++/* Takes ownership of @caps and @props */ ++static GstDevice * ++gst_alsa_device_new (const gchar * device_name, ++ GstCaps * caps, const gchar * internal_name, snd_pcm_stream_t stream, ++ GstStructure * props) ++{ ++ GstAlsaDevice *gstdev; ++ const gchar *element = NULL; ++ const gchar *klass = NULL; ++ ++ g_return_val_if_fail (device_name, NULL); ++ g_return_val_if_fail (internal_name, NULL); ++ g_return_val_if_fail (caps, NULL); ++ ++ switch (stream) { ++ case SND_PCM_STREAM_CAPTURE: ++ element = "alsasrc"; ++ klass = "Audio/Source"; ++ break; ++ case SND_PCM_STREAM_PLAYBACK: ++ element = "alsasink"; ++ klass = "Audio/Sink"; ++ break; ++ default: ++ g_assert_not_reached (); ++ break; ++ } ++ ++ ++ gstdev = g_object_new (GST_TYPE_ALSA_DEVICE, ++ "display-name", device_name, "caps", caps, "device-class", klass, ++ "internal-name", internal_name, "properties", props, NULL); ++ ++ gstdev->stream = stream; ++ gstdev->element = element; ++ ++ gst_structure_free (props); ++ gst_caps_unref (caps); ++ ++ return GST_DEVICE (gstdev); ++} +diff --git a/ext/alsa/gstalsadeviceprovider.h b/ext/alsa/gstalsadeviceprovider.h +new file mode 100644 +index 000000000..a812346c9 +--- /dev/null ++++ b/ext/alsa/gstalsadeviceprovider.h +@@ -0,0 +1,85 @@ ++/* GStreamer ++ * Copyright (C) 2012 Olivier Crete ++ * ++ * alsadeviceprovider.c: alsa device probing and monitoring ++ * ++ * 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., 59 Temple Place - Suite 330, ++ * Boston, MA 02111-1307, USA. ++ */ ++ ++ ++#ifndef __GST_ALSA_DEVICE_PROVIDER_H__ ++#define __GST_ALSA_DEVICE_PROVIDER_H__ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include "gstalsa.h" ++#include ++ ++G_BEGIN_DECLS ++ ++typedef struct _GstAlsaDeviceProvider GstAlsaDeviceProvider; ++typedef struct _GstAlsaDeviceProviderClass GstAlsaDeviceProviderClass; ++ ++#define GST_TYPE_ALSA_DEVICE_PROVIDER (gst_alsa_device_provider_get_type()) ++#define GST_IS_ALSA_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_ALSA_DEVICE_PROVIDER)) ++#define GST_IS_ALSA_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_ALSA_DEVICE_PROVIDER)) ++#define GST_ALSA_DEVICE_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_ALSA_DEVICE_PROVIDER, GstAlsaDeviceProviderClass)) ++#define GST_ALSA_DEVICE_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_ALSA_DEVICE_PROVIDER, GstAlsaDeviceProvider)) ++#define GST_ALSA_DEVICE_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE_PROVIDER, GstAlsaDeviceProviderClass)) ++#define GST_ALSA_DEVICE_PROVIDER_CAST(obj) ((GstAlsaDeviceProvider *)(obj)) ++ ++struct _GstAlsaDeviceProvider { ++ GstDeviceProvider parent; ++}; ++ ++struct _GstAlsaDeviceProviderClass { ++ GstDeviceProviderClass parent_class; ++}; ++ ++GType gst_alsa_device_provider_get_type (void); ++ ++ ++typedef struct _GstAlsaDevice GstAlsaDevice; ++typedef struct _GstAlsaDeviceClass GstAlsaDeviceClass; ++ ++#define GST_TYPE_ALSA_DEVICE (gst_alsa_device_get_type()) ++#define GST_IS_ALSA_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_ALSA_DEVICE)) ++#define GST_IS_ALSA_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_ALSA_DEVICE)) ++#define GST_ALSA_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_ALSA_DEVICE, GstAlsaDeviceClass)) ++#define GST_ALSA_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_ALSA_DEVICE, GstAlsaDevice)) ++#define GST_ALSA_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_DEVICE, GstAlsaDeviceClass)) ++#define GST_ALSA_DEVICE_CAST(obj) ((GstAlsaDevice *)(obj)) ++ ++struct _GstAlsaDevice { ++ GstDevice parent; ++ ++ snd_pcm_stream_t stream; ++ gchar *internal_name; ++ const gchar *element; ++}; ++ ++struct _GstAlsaDeviceClass { ++ GstDeviceClass parent_class; ++}; ++ ++GType gst_alsa_device_get_type (void); ++ ++G_END_DECLS ++ ++#endif /* __GST_ALSA_DEVICE_PROVIDER_H__ */ ++ +diff --git a/ext/alsa/gstalsaplugin.c b/ext/alsa/gstalsaplugin.c +index 6c1a67cc9..90899fe61 100644 +--- a/ext/alsa/gstalsaplugin.c ++++ b/ext/alsa/gstalsaplugin.c +@@ -26,6 +26,7 @@ + #include "gstalsasink.h" + #include "gstalsasrc.h" + #include "gstalsamidisrc.h" ++#include "gstalsadeviceprovider.h" + + #include + +@@ -46,7 +47,7 @@ gst_alsa_error_wrapper (const char *file, int line, const char *function, + va_start (args, fmt); + str = g_strdup_vprintf (fmt, args); + va_end (args); +- /* FIXME: use GST_LEVEL_ERROR here? Currently warning is used because we're ++ /* FIXME: use GST_LEVEL_ERROR here? Currently warning is used because we're + * able to catch enough of the errors that would be printed otherwise + */ + gst_debug_log (alsa_debug, GST_LEVEL_WARNING, file, function, line, NULL, +@@ -70,6 +71,9 @@ plugin_init (GstPlugin * plugin) + if (!gst_element_register (plugin, "alsamidisrc", GST_RANK_PRIMARY, + GST_TYPE_ALSA_MIDI_SRC)) + return FALSE; ++ if (!gst_device_provider_register (plugin, "alsadeviceprovider", ++ GST_RANK_PRIMARY, GST_TYPE_ALSA_DEVICE_PROVIDER)) ++ return FALSE; + + GST_DEBUG_CATEGORY_INIT (alsa_debug, "alsa", 0, "alsa plugins"); + +-- +2.18.0 + diff --git a/package/gstreamer1/gst1-plugins-base/gst-plugins-base-0001-Backport-playbin3-from-1.14.patch b/package/gstreamer1/gst1-plugins-base/gst-plugins-base-0001-Backport-playbin3-from-1.14.patch new file mode 100644 index 000000000000..81557eb57c1b --- /dev/null +++ b/package/gstreamer1/gst1-plugins-base/gst-plugins-base-0001-Backport-playbin3-from-1.14.patch @@ -0,0 +1,8328 @@ +From 4c01c4dfb78ea6e71ba2ecea019d73cd316289fb Mon Sep 17 00:00:00 2001 +From: Thibault Saunier +Date: Wed, 29 Aug 2018 15:16:53 -0300 +Subject: [PATCH] Backport playbin3 from 1.14 + +--- + gst/playback/Makefile.am | 1 + + gst/playback/gstdecodebin3-parse.c | 122 +- + gst/playback/gstdecodebin3.c | 472 +++++- + gst/playback/gstparsebin.c | 350 ++-- + gst/playback/gstplayback.c | 1 + + gst/playback/gstplayback.h | 1 + + gst/playback/gstplaybin3.c | 2462 ++++++++++++---------------- + gst/playback/gsturidecodebin3.c | 1087 ++++++++++++ + gst/playback/gsturisourcebin.c | 963 +++++------ + 10 files changed, 3236 insertions(+), 2224 deletions(-) + create mode 100644 gst/playback/gsturidecodebin3.c + +diff --git a/gst/playback/Makefile.am b/gst/playback/Makefile.am +index 9603dab8f..180bf5f7c 100644 +--- a/gst/playback/Makefile.am ++++ b/gst/playback/Makefile.am +@@ -6,6 +6,7 @@ libgstplayback_la_SOURCES = \ + gstdecodebin2.c \ + gstdecodebin3.c \ + gsturidecodebin.c \ ++ gsturidecodebin3.c \ + gsturisourcebin.c \ + gstparsebin.c \ + gstplayback.c \ +diff --git a/gst/playback/gstdecodebin3-parse.c b/gst/playback/gstdecodebin3-parse.c +index 9797be6d7..aea0f666d 100644 +--- a/gst/playback/gstdecodebin3-parse.c ++++ b/gst/playback/gstdecodebin3-parse.c +@@ -35,6 +35,20 @@ + #include "gstplayback.h" + #endif + ++#define CUSTOM_EOS_QUARK _custom_eos_quark_get () ++#define CUSTOM_EOS_QUARK_DATA "custom-eos" ++static GQuark ++_custom_eos_quark_get (void) ++{ ++ static gsize g_quark; ++ ++ if (g_once_init_enter (&g_quark)) { ++ gsize quark = (gsize) g_quark_from_static_string ("decodebin3-custom-eos"); ++ g_once_init_leave (&g_quark, quark); ++ } ++ return g_quark; ++} ++ + /* Streams that come from demuxers (input/upstream) */ + /* FIXME : All this is hardcoded. Switch to tree of chains */ + struct _DecodebinInputStream +@@ -56,9 +70,6 @@ struct _DecodebinInputStream + /* Whether we saw an EOS on input. This should be treated accordingly + * when the stream is no longer used */ + gboolean saw_eos; +- /* TRUE if the EOS being pushed is only for draining and does not represent +- * the full media EOS */ +- gboolean drain_eos; + }; + + static void parsebin_pad_added_cb (GstElement * demux, GstPad * pad, +@@ -66,6 +77,7 @@ static void parsebin_pad_added_cb (GstElement * demux, GstPad * pad, + static void parsebin_pad_removed_cb (GstElement * demux, GstPad * pad, + DecodebinInput * input); + ++/* WITH SELECTION_LOCK TAKEN! */ + static gboolean + pending_pads_are_eos (DecodebinInput * input) + { +@@ -80,6 +92,7 @@ pending_pads_are_eos (DecodebinInput * input) + return TRUE; + } + ++/* WITH SELECTION_LOCK TAKEN! */ + static gboolean + all_inputs_are_eos (GstDecodebin3 * dbin) + { +@@ -102,6 +115,7 @@ all_inputs_are_eos (GstDecodebin3 * dbin) + return TRUE; + } + ++/* WITH SELECTION_LOCK TAKEN! */ + static void + check_all_streams_for_eos (GstDecodebin3 * dbin) + { +@@ -208,8 +222,14 @@ parse_chain_output_probe (GstPad * pad, GstPadProbeInfo * info, + case GST_EVENT_STREAM_START: + { + GstStream *stream = NULL; +- guint group_id = G_MAXUINT32; +- gst_event_parse_group_id (ev, &group_id); ++ guint group_id = GST_GROUP_ID_INVALID; ++ ++ if (!gst_event_parse_group_id (ev, &group_id)) { ++ GST_FIXME_OBJECT (pad, ++ "Consider implementing group-id handling on stream-start event"); ++ group_id = gst_util_group_id_next (); ++ } ++ + GST_DEBUG_OBJECT (pad, "Got stream-start, group_id:%d, input %p", + group_id, input->input); + if (set_input_group_id (input->input, &group_id)) { +@@ -228,10 +248,10 @@ parse_chain_output_probe (GstPad * pad, GstPadProbeInfo * info, + gst_object_unref (input->active_stream); + input->active_stream = stream; + /* We have the beginning of a stream, get a multiqueue slot and link to it */ +- g_mutex_lock (&input->dbin->selection_lock); ++ SELECTION_LOCK (input->dbin); + slot = get_slot_for_input (input->dbin, input); + link_input_to_slot (input, slot); +- g_mutex_unlock (&input->dbin->selection_lock); ++ SELECTION_UNLOCK (input->dbin); + } else + gst_object_unref (stream); + } +@@ -247,32 +267,31 @@ parse_chain_output_probe (GstPad * pad, GstPadProbeInfo * info, + } + break; + case GST_EVENT_EOS: +- /* FIXME : Make sure this makes sense ... */ +- if (TRUE) { ++ input->saw_eos = TRUE; ++ if (all_inputs_are_eos (input->dbin)) { + GST_DEBUG_OBJECT (pad, "real input pad, marking as EOS"); +- input->saw_eos = TRUE; ++ SELECTION_LOCK (input->dbin); + check_all_streams_for_eos (input->dbin); +- ret = GST_PAD_PROBE_DROP; ++ SELECTION_UNLOCK (input->dbin); + } else { +- MultiQueueSlot *slot; +- +- g_mutex_lock (&input->dbin->selection_lock); +- slot = get_slot_for_input (input->dbin, input); +- g_mutex_unlock (&input->dbin->selection_lock); ++ GstPad *peer = gst_pad_get_peer (input->srcpad); ++ if (peer) { ++ /* Send custom-eos event to multiqueue slot */ ++ GstEvent *event; + +- slot->drain_eos = input->drain_eos; +- +- if (input->drain_eos) { + GST_DEBUG_OBJECT (pad, +- "Got EOS at end of input stream (drain_eos:%d) Dropping.", +- input->drain_eos); +- ret = GST_PAD_PROBE_DROP; ++ "Got EOS end of input stream, post custom-eos"); ++ event = gst_event_new_eos (); ++ gst_event_set_seqnum (event, gst_event_get_seqnum (ev)); ++ gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (event), ++ CUSTOM_EOS_QUARK, (gchar *) CUSTOM_EOS_QUARK_DATA, NULL); ++ gst_pad_send_event (peer, event); ++ gst_object_unref (peer); + } else { +- GST_DEBUG_OBJECT (pad, +- "Got EOS at end of input stream (drain_eos:%d) Passing.", +- input->drain_eos); ++ GST_FIXME_OBJECT (pad, "No peer, what should we do ?"); + } + } ++ ret = GST_PAD_PROBE_DROP; + break; + case GST_EVENT_FLUSH_STOP: + GST_DEBUG_OBJECT (pad, "Clear saw_eos flag"); +@@ -338,12 +357,15 @@ create_input_stream (GstDecodebin3 * dbin, GstStream * stream, GstPad * pad, + (GstPadProbeCallback) parse_chain_output_probe, res, NULL); + + /* Add to list of current input streams */ ++ SELECTION_LOCK (dbin); + dbin->input_streams = g_list_append (dbin->input_streams, res); ++ SELECTION_UNLOCK (dbin); + GST_DEBUG_OBJECT (pad, "Done creating input stream"); + + return res; + } + ++/* WITH SELECTION_LOCK TAKEN! */ + static void + remove_input_stream (GstDecodebin3 * dbin, DecodebinInputStream * stream) + { +@@ -363,9 +385,7 @@ remove_input_stream (GstDecodebin3 * dbin, DecodebinInputStream * stream) + } + } + +- g_mutex_lock (&dbin->selection_lock); + slot = get_slot_for_input (dbin, stream); +- g_mutex_unlock (&dbin->selection_lock); + if (slot) { + slot->pending_stream = NULL; + slot->input = NULL; +@@ -391,8 +411,6 @@ parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info, + GstDecodebin3 *dbin = input->dbin; + GList *tmp, *unused_slot = NULL; + +- GST_FIXME_OBJECT (dbin, "Need a lock !"); +- + GST_DEBUG_OBJECT (pad, "Got a buffer ! UNBLOCK !"); + + /* Any data out the demuxer means it's not creating pads +@@ -403,11 +421,17 @@ parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info, + + /* 2. Remove unused streams (push EOS) */ + GST_DEBUG_OBJECT (dbin, "Removing unused streams"); ++ SELECTION_LOCK (dbin); + tmp = dbin->input_streams; + while (tmp != NULL) { + DecodebinInputStream *input_stream = (DecodebinInputStream *) tmp->data; + GList *next = tmp->next; + ++ if (input_stream->input != input) { ++ tmp = next; ++ continue; ++ } ++ + GST_DEBUG_OBJECT (dbin, "Checking input stream %p", input_stream); + if (input_stream->input_buffer_probe_id) { + GST_DEBUG_OBJECT (dbin, +@@ -424,6 +448,7 @@ parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info, + } else + tmp = next; + } ++ SELECTION_UNLOCK (dbin); + + GST_DEBUG_OBJECT (dbin, "Creating new streams (if needed)"); + /* 3. Create new streams */ +@@ -443,10 +468,10 @@ parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info, + /* See if we can link it straight away */ + input_stream->active_stream = stream; + +- g_mutex_lock (&dbin->selection_lock); ++ SELECTION_LOCK (dbin); + slot = get_slot_for_input (dbin, input_stream); + link_input_to_slot (input_stream, slot); +- g_mutex_unlock (&dbin->selection_lock); ++ SELECTION_UNLOCK (dbin); + + /* Remove the buffer and event probe */ + gst_pad_remove_probe (ppad->pad, ppad->buffer_probe); +@@ -459,17 +484,16 @@ parsebin_buffer_probe (GstPad * pad, GstPadProbeInfo * info, + input->pending_pads = NULL; + + /* Weed out unused multiqueue slots */ +- g_mutex_lock (&dbin->selection_lock); ++ SELECTION_LOCK (dbin); + for (tmp = dbin->slots; tmp; tmp = tmp->next) { + MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data; +- GST_LOG_OBJECT (dbin, "Slot %d input:%p drain_eos:%d", +- slot->id, slot->input, slot->drain_eos); ++ GST_LOG_OBJECT (dbin, "Slot %d input:%p", slot->id, slot->input); + if (slot->input == NULL) { + unused_slot = + g_list_append (unused_slot, gst_object_ref (slot->sink_pad)); + } + } +- g_mutex_unlock (&dbin->selection_lock); ++ SELECTION_UNLOCK (dbin); + + for (tmp = unused_slot; tmp; tmp = tmp->next) { + GstPad *sink_pad = (GstPad *) tmp->data; +@@ -538,11 +562,11 @@ parsebin_pad_added_cb (GstElement * demux, GstPad * pad, DecodebinInput * input) + + input->pending_pads = g_list_append (input->pending_pads, ppad); + +- /* FIXME : ONLY DO FOR THIS PARSEBIN/INPUT ! */ + /* Check if all existing input streams have a buffer probe set */ + for (tmp = dbin->input_streams; tmp; tmp = tmp->next) { + DecodebinInputStream *input_stream = (DecodebinInputStream *) tmp->data; +- if (input_stream->input_buffer_probe_id == 0) { ++ if (input_stream->input == input && ++ input_stream->input_buffer_probe_id == 0) { + GST_DEBUG_OBJECT (input_stream->srcpad, "Adding blocking buffer probe"); + input_stream->input_buffer_probe_id = + gst_pad_add_probe (input_stream->srcpad, +@@ -571,8 +595,32 @@ parsebin_pad_removed_cb (GstElement * demux, GstPad * pad, DecodebinInput * inp) + if (input) { + GST_DEBUG_OBJECT (pad, "stream %p", input); + if (inp->pending_pads == NULL) { ++ MultiQueueSlot *slot; ++ + GST_DEBUG_OBJECT (pad, "Remove input stream %p", input); ++ ++ SELECTION_LOCK (dbin); ++ slot = get_slot_for_input (dbin, input); ++ + remove_input_stream (dbin, input); ++ if (slot && g_list_find (dbin->slots, slot) && slot->is_drained) { ++ /* if slot is still there and already drained, remove it in here */ ++ if (slot->output) { ++ DecodebinOutputStream *output = slot->output; ++ GST_DEBUG_OBJECT (pad, ++ "Multiqueue was drained, Remove output stream"); ++ ++ dbin->output_streams = g_list_remove (dbin->output_streams, output); ++ free_output_stream (dbin, output); ++ } ++ GST_DEBUG_OBJECT (pad, "No pending pad, Remove multiqueue slot"); ++ if (slot->probe_id) ++ gst_pad_remove_probe (slot->src_pad, slot->probe_id); ++ slot->probe_id = 0; ++ dbin->slots = g_list_remove (dbin->slots, slot); ++ free_multiqueue_slot_async (dbin, slot); ++ } ++ SELECTION_UNLOCK (dbin); + } else { + input->srcpad = NULL; + if (input->input_buffer_probe_id) +diff --git a/gst/playback/gstdecodebin3.c b/gst/playback/gstdecodebin3.c +index 5b33f85a7..07b61048e 100644 +--- a/gst/playback/gstdecodebin3.c ++++ b/gst/playback/gstdecodebin3.c +@@ -36,6 +36,7 @@ + + /** + * SECTION:element-decodebin3 ++ * @title: decodebin3 + * + * #GstBin that auto-magically constructs a decoding pipeline using available + * decoders and demuxers via auto-plugging. The output is raw audio, video +@@ -43,27 +44,20 @@ + * + * decodebin3 differs from the previous decodebin (decodebin2) in important ways: + * +- * +- * +- * supports publication and selection of stream information via ++ * * supports publication and selection of stream information via + * GstStreamCollection messages and #GST_EVENT_SELECT_STREAM events. +- * +- * +- * dynamically switches stream connections internally, and ++ * ++ * * dynamically switches stream connections internally, and + * reuses decoder elements when stream selections change, so that in + * the normal case it maintains 1 decoder of each type (video/audio/subtitle) + * and only creates new elements when streams change and an existing decoder + * is not capable of handling the new format. +- * +- * +- * supports multiple input pads for the parallel decoding of auxilliary streams ++ * ++ * * supports multiple input pads for the parallel decoding of auxilliary streams + * not muxed with the primary stream. +- * +- * +- * does not handle network stream buffering. decodebin3 expects that network stream ++ * ++ * * does not handle network stream buffering. decodebin3 expects that network stream + * buffering is handled upstream, before data is passed to it. +- * +- * + * + * decodebin3 is still experimental API and a technology preview. + * Its behaviour and exposed API is subject to change. +@@ -74,7 +68,7 @@ + * Global design + * + * 1) From sink pad to elementary streams (GstParseBin) +- * ++ * + * The input sink pads are fed to GstParseBin. GstParseBin will feed them + * through typefind. When the caps are detected (or changed) we recursively + * figure out which demuxer, parser or depayloader is needed until we get to +@@ -115,7 +109,6 @@ + * * STREAM_START : + * a new stream is starting => link it further if needed + * +- * + * 3) Gradual replacement + * + * If the caps change at any point in decodebin (input sink pad, demuxer output, +@@ -133,8 +126,6 @@ + * b.1) The new CAPS are accepted, keep current configuration + * b.2) The new CAPS are not accepted, remove following elements then do a) + * +- * +- * + * Components: + * + * MultiQ Output +@@ -190,6 +181,21 @@ GST_DEBUG_CATEGORY_STATIC (decodebin3_debug); + + #define EXTRA_DEBUG 1 + ++#define CUSTOM_FINAL_EOS_QUARK _custom_final_eos_quark_get () ++#define CUSTOM_FINAL_EOS_QUARK_DATA "custom-final-eos" ++static GQuark ++_custom_final_eos_quark_get (void) ++{ ++ static gsize g_quark; ++ ++ if (g_once_init_enter (&g_quark)) { ++ gsize quark = ++ (gsize) g_quark_from_static_string ("decodebin3-custom-final-eos"); ++ g_once_init_leave (&g_quark, quark); ++ } ++ return g_quark; ++} ++ + typedef struct _GstDecodebin3 GstDecodebin3; + typedef struct _GstDecodebin3Class GstDecodebin3Class; + +@@ -209,23 +215,22 @@ struct _GstDecodebin3 + GList *other_inputs; + /* counter for input */ + guint32 input_counter; +- /* Current stream group_id (default : G_MAXUINT32) */ ++ /* Current stream group_id (default : GST_GROUP_ID_INVALID) */ + /* FIXME : Needs to be resetted appropriately (when upstream changes ?) */ + guint32 current_group_id; + /* End of variables protected by input_lock */ + + GstElement *multiqueue; + +- /* FIXME : Mutex for protecting values below */ +- GstStreamCollection *collection; /* Active collection */ +- ++ /* selection_lock protects access to following variables */ ++ GMutex selection_lock; + GList *input_streams; /* List of DecodebinInputStream for active collection */ + GList *output_streams; /* List of DecodebinOutputStream used for output */ + GList *slots; /* List of MultiQueueSlot */ + guint slot_id; + +- /* selection_lock protects access to following variables */ +- GMutex selection_lock; ++ /* Active collection */ ++ GstStreamCollection *collection; + /* requested selection of stream-id to activate post-multiqueue */ + GList *requested_selection; + /* list of stream-id currently activated in output */ +@@ -245,7 +250,6 @@ struct _GstDecodebin3 + * FIXME : Is this really needed ? */ + GList *pending_collection; + +- + /* Factories */ + GMutex factories_lock; + guint32 factories_cookie; +@@ -289,6 +293,12 @@ struct _DecodebinInput + + gulong pad_added_sigid; + gulong pad_removed_sigid; ++ gulong drained_sigid; ++ ++ /* TRUE if the input got drained ++ * FIXME : When do we reset it if re-used ? ++ */ ++ gboolean drained; + + /* HACK : Remove these fields */ + /* List of PendingPad structures */ +@@ -317,7 +327,7 @@ typedef struct _MultiQueueSlot + /* id of the MQ src_pad event probe */ + gulong probe_id; + +- gboolean drain_eos; ++ gboolean is_drained; + + DecodebinOutputStream *output; + } MultiQueueSlot; +@@ -358,8 +368,6 @@ typedef struct _PendingPad + } PendingPad; + + /* properties */ +-#define DEFAULT_CAPS (gst_static_caps_get (&default_raw_caps)) +- + enum + { + PROP_0, +@@ -370,6 +378,7 @@ enum + enum + { + SIGNAL_SELECT_STREAM, ++ SIGNAL_ABOUT_TO_FINISH, + LAST_SIGNAL + }; + static guint gst_decodebin3_signals[LAST_SIGNAL] = { 0 }; +@@ -482,6 +491,7 @@ static gboolean have_factory (GstDecodebin3 * dbin, GstCaps * caps, + #endif + + static void free_input (GstDecodebin3 * dbin, DecodebinInput * input); ++static void free_input_async (GstDecodebin3 * dbin, DecodebinInput * input); + static DecodebinInput *create_new_input (GstDecodebin3 * dbin, gboolean main); + static gboolean set_input_group_id (DecodebinInput * input, guint32 * group_id); + +@@ -500,6 +510,11 @@ static MultiQueueSlot *get_slot_for_input (GstDecodebin3 * dbin, + static void link_input_to_slot (DecodebinInputStream * input, + MultiQueueSlot * slot); + static void free_multiqueue_slot (GstDecodebin3 * dbin, MultiQueueSlot * slot); ++static void free_multiqueue_slot_async (GstDecodebin3 * dbin, ++ MultiQueueSlot * slot); ++ ++static GstStreamCollection *get_merged_collection (GstDecodebin3 * dbin); ++static void update_requested_selection (GstDecodebin3 * dbin); + + /* FIXME: Really make all the parser stuff a self-contained helper object */ + #include "gstdecodebin3-parse.c" +@@ -555,6 +570,17 @@ gst_decodebin3_class_init (GstDecodebin3Class * klass) + _gst_int_accumulator, NULL, g_cclosure_marshal_generic, + G_TYPE_INT, 2, GST_TYPE_STREAM_COLLECTION, GST_TYPE_STREAM); + ++ /** ++ * GstDecodebin3::about-to-finish: ++ * ++ * This signal is emitted when the data for the selected URI is ++ * entirely buffered and it is safe to specify anothe URI. ++ */ ++ gst_decodebin3_signals[SIGNAL_ABOUT_TO_FINISH] = ++ g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, ++ 0, G_TYPE_NONE); ++ + + element_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_decodebin3_request_new_pad); +@@ -595,7 +621,7 @@ gst_decodebin3_init (GstDecodebin3 * dbin) + "max-size-buffers", 0, "use-interleave", TRUE, NULL); + gst_bin_add ((GstBin *) dbin, dbin->multiqueue); + +- dbin->current_group_id = G_MAXUINT32; ++ dbin->current_group_id = GST_GROUP_ID_INVALID; + + g_mutex_init (&dbin->factories_lock); + g_mutex_init (&dbin->selection_lock); +@@ -618,7 +644,7 @@ gst_decodebin3_dispose (GObject * object) + g_list_free (dbin->decoder_factories); + if (dbin->decodable_factories) + g_list_free (dbin->decodable_factories); +- g_list_free (dbin->requested_selection); ++ g_list_free_full (dbin->requested_selection, g_free); + g_list_free (dbin->active_selection); + g_list_free (dbin->to_activate); + g_list_free (dbin->pending_select_streams); +@@ -700,7 +726,7 @@ set_input_group_id (DecodebinInput * input, guint32 * group_id) + GstDecodebin3 *dbin = input->dbin; + + if (input->group_id != *group_id) { +- if (input->group_id != G_MAXUINT32) ++ if (input->group_id != GST_GROUP_ID_INVALID) + GST_WARNING_OBJECT (dbin, + "Group id changed (%" G_GUINT32_FORMAT " -> %" G_GUINT32_FORMAT + ") on input %p ", input->group_id, *group_id, input); +@@ -708,7 +734,7 @@ set_input_group_id (DecodebinInput * input, guint32 * group_id) + } + + if (*group_id != dbin->current_group_id) { +- if (dbin->current_group_id == G_MAXUINT32) { ++ if (dbin->current_group_id == GST_GROUP_ID_INVALID) { + GST_DEBUG_OBJECT (dbin, "Setting current group id to %" G_GUINT32_FORMAT, + *group_id); + dbin->current_group_id = *group_id; +@@ -720,6 +746,30 @@ set_input_group_id (DecodebinInput * input, guint32 * group_id) + return FALSE; + } + ++static void ++parsebin_drained_cb (GstElement * parsebin, DecodebinInput * input) ++{ ++ GstDecodebin3 *dbin = input->dbin; ++ gboolean all_drained; ++ GList *tmp; ++ ++ GST_WARNING_OBJECT (dbin, "input %p drained", input); ++ input->drained = TRUE; ++ ++ all_drained = dbin->main_input->drained; ++ for (tmp = dbin->other_inputs; tmp; tmp = tmp->next) { ++ DecodebinInput *data = (DecodebinInput *) tmp->data; ++ ++ all_drained &= data->drained; ++ } ++ ++ if (all_drained) { ++ GST_WARNING_OBJECT (dbin, "All inputs drained. Posting about-to-finish"); ++ g_signal_emit (dbin, gst_decodebin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, ++ NULL); ++ } ++} ++ + /* Call with INPUT_LOCK taken */ + static gboolean + ensure_input_parsebin (GstDecodebin3 * dbin, DecodebinInput * input) +@@ -738,6 +788,9 @@ ensure_input_parsebin (GstDecodebin3 * dbin, DecodebinInput * input) + input->pad_removed_sigid = + g_signal_connect (input->parsebin, "pad-removed", + (GCallback) parsebin_pad_removed_cb, input); ++ input->drained_sigid = ++ g_signal_connect (input->parsebin, "drained", ++ (GCallback) parsebin_drained_cb, input); + g_signal_connect (input->parsebin, "autoplug-continue", + (GCallback) parsebin_autoplug_continue_cb, dbin); + } +@@ -787,6 +840,24 @@ fail: + return GST_PAD_LINK_REFUSED; + } + ++/* Drop duration query during _input_pad_unlink */ ++static GstPadProbeReturn ++query_duration_drop_probe (GstPad * pad, GstPadProbeInfo * info, ++ DecodebinInput * input) ++{ ++ GstPadProbeReturn ret = GST_PAD_PROBE_OK; ++ ++ if (GST_IS_QUERY (GST_PAD_PROBE_INFO_DATA (info))) { ++ GstQuery *query = GST_PAD_PROBE_INFO_QUERY (info); ++ if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION) { ++ GST_LOG_OBJECT (pad, "stop forwarding query duration"); ++ ret = GST_PAD_PROBE_HANDLED; ++ } ++ } ++ ++ return ret; ++} ++ + static void + gst_decodebin3_input_pad_unlink (GstPad * pad, GstObject * parent) + { +@@ -806,8 +877,54 @@ gst_decodebin3_input_pad_unlink (GstPad * pad, GstObject * parent) + } + + if (GST_OBJECT_PARENT (GST_OBJECT (input->parsebin)) == GST_OBJECT (dbin)) { ++ GstStreamCollection *collection = NULL; ++ gulong probe_id = gst_pad_add_probe (input->parsebin_sink, ++ GST_PAD_PROBE_TYPE_QUERY_UPSTREAM, ++ (GstPadProbeCallback) query_duration_drop_probe, input, NULL); ++ ++ /* Clear stream-collection corresponding to current INPUT and post new ++ * stream-collection message, if needed */ ++ if (input->collection) { ++ gst_object_unref (input->collection); ++ input->collection = NULL; ++ } ++ ++ SELECTION_LOCK (dbin); ++ collection = get_merged_collection (dbin); ++ if (collection && collection != dbin->collection) { ++ GstMessage *msg; ++ GST_DEBUG_OBJECT (dbin, "Update Stream Collection"); ++ ++ if (dbin->collection) ++ gst_object_unref (dbin->collection); ++ dbin->collection = collection; ++ ++ msg = ++ gst_message_new_stream_collection ((GstObject *) dbin, ++ dbin->collection); ++ ++ SELECTION_UNLOCK (dbin); ++ gst_element_post_message (GST_ELEMENT_CAST (dbin), msg); ++ update_requested_selection (dbin); ++ } else ++ SELECTION_UNLOCK (dbin); ++ + gst_bin_remove (GST_BIN (dbin), input->parsebin); + gst_element_set_state (input->parsebin, GST_STATE_NULL); ++ g_signal_handler_disconnect (input->parsebin, input->pad_removed_sigid); ++ g_signal_handler_disconnect (input->parsebin, input->pad_added_sigid); ++ g_signal_handler_disconnect (input->parsebin, input->drained_sigid); ++ gst_pad_remove_probe (input->parsebin_sink, probe_id); ++ gst_object_unref (input->parsebin); ++ gst_object_unref (input->parsebin_sink); ++ ++ input->parsebin = NULL; ++ input->parsebin_sink = NULL; ++ ++ if (!input->is_main) { ++ dbin->other_inputs = g_list_remove (dbin->other_inputs, input); ++ free_input_async (dbin, input); ++ } + } + INPUT_UNLOCK (dbin); + return; +@@ -826,6 +943,7 @@ free_input (GstDecodebin3 * dbin, DecodebinInput * input) + if (input->parsebin) { + g_signal_handler_disconnect (input->parsebin, input->pad_removed_sigid); + g_signal_handler_disconnect (input->parsebin, input->pad_added_sigid); ++ g_signal_handler_disconnect (input->parsebin, input->drained_sigid); + gst_element_set_state (input->parsebin, GST_STATE_NULL); + gst_object_unref (input->parsebin); + gst_object_unref (input->parsebin_sink); +@@ -835,6 +953,14 @@ free_input (GstDecodebin3 * dbin, DecodebinInput * input) + g_free (input); + } + ++static void ++free_input_async (GstDecodebin3 * dbin, DecodebinInput * input) ++{ ++ GST_LOG_OBJECT (dbin, "pushing input %p on thread pool to free", input); ++ gst_element_call_async (GST_ELEMENT_CAST (dbin), ++ (GstElementCallAsyncFunc) free_input, input, NULL); ++} ++ + /* Call with INPUT_LOCK taken */ + static DecodebinInput * + create_new_input (GstDecodebin3 * dbin, gboolean main) +@@ -844,7 +970,7 @@ create_new_input (GstDecodebin3 * dbin, gboolean main) + input = g_new0 (DecodebinInput, 1); + input->dbin = dbin; + input->is_main = main; +- input->group_id = G_MAXUINT32; ++ input->group_id = GST_GROUP_ID_INVALID; + if (main) + input->ghost_sink = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK); + else { +@@ -874,13 +1000,13 @@ gst_decodebin3_request_new_pad (GstElement * element, GstPadTemplate * temp, + + /* We are ignoring names for the time being, not sure it makes any sense + * within the context of decodebin3 ... */ +- INPUT_LOCK (dbin); + input = create_new_input (dbin, FALSE); + if (input) { ++ INPUT_LOCK (dbin); + dbin->other_inputs = g_list_append (dbin->other_inputs, input); + res = input->ghost_sink; ++ INPUT_UNLOCK (dbin); + } +- INPUT_UNLOCK (dbin); + + return res; + } +@@ -923,7 +1049,7 @@ gst_decode_bin_update_factories_list (GstDecodebin3 * dbin) + } + + /* Must be called with appropriate lock if list is a protected variable */ +-static gboolean ++static const gchar * + stream_in_list (GList * list, const gchar * sid) + { + GList *tmp; +@@ -936,23 +1062,21 @@ stream_in_list (GList * list, const gchar * sid) + #endif + + for (tmp = list; tmp; tmp = tmp->next) { +- gchar *osid = (gchar *) tmp->data; ++ const gchar *osid = (gchar *) tmp->data; + if (!g_strcmp0 (sid, osid)) +- return TRUE; ++ return osid; + } + +- return FALSE; ++ return NULL; + } + + static void +-update_requested_selection (GstDecodebin3 * dbin, +- GstStreamCollection * collection) ++update_requested_selection (GstDecodebin3 * dbin) + { + guint i, nb; + GList *tmp = NULL; + GstStreamType used_types = 0; +- +- nb = gst_stream_collection_get_size (collection); ++ GstStreamCollection *collection; + + /* 1. Is there a pending SELECT_STREAMS we can return straight away since + * the switch handler will take care of the pending selection */ +@@ -963,6 +1087,13 @@ update_requested_selection (GstDecodebin3 * dbin, + goto beach; + } + ++ collection = dbin->collection; ++ if (G_UNLIKELY (collection == NULL)) { ++ GST_DEBUG_OBJECT (dbin, "No current GstStreamCollection"); ++ goto beach; ++ } ++ nb = gst_stream_collection_get_size (collection); ++ + /* 2. If not, are we in EXPOSE_ALL_MODE ? If so, match everything */ + GST_FIXME_OBJECT (dbin, "Implement EXPOSE_ALL_MODE"); + +@@ -1013,10 +1144,12 @@ beach: + if (dbin->requested_selection) { + GST_FIXME_OBJECT (dbin, + "Replacing non-NULL requested_selection, what should we do ??"); +- g_list_free (dbin->requested_selection); ++ g_list_free_full (dbin->requested_selection, g_free); + } +- dbin->requested_selection = tmp; ++ dbin->requested_selection = ++ g_list_copy_deep (tmp, (GCopyFunc) g_strdup, NULL); + dbin->selection_updated = TRUE; ++ g_list_free (tmp); + } + SELECTION_UNLOCK (dbin); + } +@@ -1046,7 +1179,7 @@ get_merged_collection (GstDecodebin3 * dbin) + + if (!needs_merge) { + GST_DEBUG_OBJECT (dbin, "No need to merge, returning %p", res); +- return gst_object_ref (res); ++ return res ? gst_object_ref (res) : NULL; + } + + /* We really need to create a new collection */ +@@ -1117,22 +1250,22 @@ find_message_parsebin (GstDecodebin3 * dbin, GstElement * child) + return input; + } + +-static gboolean ++static const gchar * + stream_in_collection (GstDecodebin3 * dbin, gchar * sid) + { + guint i, len; + + if (dbin->collection == NULL) +- return FALSE; ++ return NULL; + len = gst_stream_collection_get_size (dbin->collection); + for (i = 0; i < len; i++) { + GstStream *stream = gst_stream_collection_get_stream (dbin->collection, i); + const gchar *osid = gst_stream_get_stream_id (stream); + if (!g_strcmp0 (sid, osid)) +- return TRUE; ++ return osid; + } + +- return FALSE; ++ return NULL; + } + + /* Call with INPUT_LOCK taken */ +@@ -1189,6 +1322,7 @@ handle_stream_collection (GstDecodebin3 * dbin, + #endif + + /* Store collection for later usage */ ++ SELECTION_LOCK (dbin); + if (dbin->collection == NULL) { + dbin->collection = collection; + } else { +@@ -1204,6 +1338,7 @@ handle_stream_collection (GstDecodebin3 * dbin, + /* dbin->pending_collection = */ + /* g_list_append (dbin->pending_collection, collection); */ + } ++ SELECTION_UNLOCK (dbin); + } + + static void +@@ -1226,6 +1361,8 @@ gst_decodebin3_handle_message (GstBin * bin, GstMessage * message) + posting_collection = TRUE; + INPUT_UNLOCK (dbin); + } ++ ++ SELECTION_LOCK (dbin); + if (dbin->collection && collection != dbin->collection) { + /* Replace collection message, we most likely aggregated it */ + GstMessage *new_msg; +@@ -1235,6 +1372,8 @@ gst_decodebin3_handle_message (GstBin * bin, GstMessage * message) + gst_message_unref (message); + message = new_msg; + } ++ SELECTION_UNLOCK (dbin); ++ + if (collection) + gst_object_unref (collection); + break; +@@ -1247,7 +1386,7 @@ gst_decodebin3_handle_message (GstBin * bin, GstMessage * message) + + if (posting_collection) { + /* Figure out a selection for that collection */ +- update_requested_selection (dbin, dbin->collection); ++ update_requested_selection (dbin); + } + } + +@@ -1281,6 +1420,7 @@ get_output_for_slot (MultiQueueSlot * slot) + DecodebinOutputStream *output = NULL; + const gchar *stream_id; + GstCaps *caps; ++ gchar *id_in_list = NULL; + + /* If we already have a configured output, just use it */ + if (slot->output != NULL) +@@ -1288,7 +1428,7 @@ get_output_for_slot (MultiQueueSlot * slot) + + /* + * FIXME +- * ++ * + * This method needs to be split into multiple parts + * + * 1) Figure out whether stream should be exposed or not +@@ -1331,7 +1471,8 @@ get_output_for_slot (MultiQueueSlot * slot) + #endif + + /* 3. In default mode check if we should expose */ +- if (stream_in_list (dbin->requested_selection, stream_id)) { ++ id_in_list = (gchar *) stream_in_list (dbin->requested_selection, stream_id); ++ if (id_in_list) { + /* Check if we can steal an existing output stream we could re-use. + * that is: + * * an output stream whose slot->stream is not in requested +@@ -1343,7 +1484,8 @@ get_output_for_slot (MultiQueueSlot * slot) + dbin->to_activate = + g_list_append (dbin->to_activate, (gchar *) stream_id); + dbin->requested_selection = +- g_list_remove (dbin->requested_selection, stream_id); ++ g_list_remove (dbin->requested_selection, id_in_list); ++ g_free (id_in_list); + SELECTION_UNLOCK (dbin); + gst_pad_add_probe (output->slot->src_pad, GST_PAD_PROBE_TYPE_IDLE, + (GstPadProbeCallback) slot_unassign_probe, output->slot, NULL); +@@ -1408,6 +1550,84 @@ is_selection_done (GstDecodebin3 * dbin) + return msg; + } + ++/* Must be called with SELECTION_LOCK taken */ ++static void ++check_all_slot_for_eos (GstDecodebin3 * dbin) ++{ ++ gboolean all_drained = TRUE; ++ GList *iter; ++ ++ GST_DEBUG_OBJECT (dbin, "check slot for eos"); ++ ++ for (iter = dbin->slots; iter; iter = iter->next) { ++ MultiQueueSlot *slot = iter->data; ++ ++ if (!slot->output) ++ continue; ++ ++ if (slot->is_drained) { ++ GST_LOG_OBJECT (slot->sink_pad, "slot %p is drained", slot); ++ continue; ++ } ++ ++ all_drained = FALSE; ++ break; ++ } ++ ++ if (all_drained) { ++ INPUT_LOCK (dbin); ++ if (!pending_pads_are_eos (dbin->main_input)) ++ all_drained = FALSE; ++ ++ if (all_drained) { ++ for (iter = dbin->other_inputs; iter; iter = iter->next) { ++ if (!pending_pads_are_eos ((DecodebinInput *) iter->data)) { ++ all_drained = FALSE; ++ break; ++ } ++ } ++ } ++ INPUT_UNLOCK (dbin); ++ } ++ ++ if (all_drained) { ++ GST_DEBUG_OBJECT (dbin, ++ "All active slots are drained, and no pending input, push EOS"); ++ ++ for (iter = dbin->input_streams; iter; iter = iter->next) { ++ DecodebinInputStream *input = (DecodebinInputStream *) iter->data; ++ GstPad *peer = gst_pad_get_peer (input->srcpad); ++ ++ /* Send EOS to all slots */ ++ if (peer) { ++ GstEvent *stream_start, *eos; ++ ++ stream_start = ++ gst_pad_get_sticky_event (input->srcpad, GST_EVENT_STREAM_START, 0); ++ ++ /* First forward a custom STREAM_START event to reset the EOS status (if any) */ ++ if (stream_start) { ++ GstStructure *s; ++ GstEvent *custom_stream_start = gst_event_copy (stream_start); ++ gst_event_unref (stream_start); ++ s = (GstStructure *) gst_event_get_structure (custom_stream_start); ++ gst_structure_set (s, "decodebin3-flushing-stream-start", ++ G_TYPE_BOOLEAN, TRUE, NULL); ++ gst_pad_send_event (peer, custom_stream_start); ++ } ++ ++ eos = gst_event_new_eos (); ++ gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (eos), ++ CUSTOM_FINAL_EOS_QUARK, (gchar *) CUSTOM_FINAL_EOS_QUARK_DATA, ++ NULL); ++ gst_pad_send_event (peer, eos); ++ gst_object_unref (peer); ++ } else ++ GST_DEBUG_OBJECT (dbin, "no output"); ++ } ++ } ++} ++ + static GstPadProbeReturn + multiqueue_src_probe (GstPad * pad, GstPadProbeInfo * info, + MultiQueueSlot * slot) +@@ -1423,7 +1643,16 @@ multiqueue_src_probe (GstPad * pad, GstPadProbeInfo * info, + case GST_EVENT_STREAM_START: + { + GstStream *stream = NULL; +- const gchar *stream_id; ++ const GstStructure *s = gst_event_get_structure (ev); ++ ++ /* Drop STREAM_START events used to cleanup multiqueue */ ++ if (s ++ && gst_structure_has_field (s, ++ "decodebin3-flushing-stream-start")) { ++ ret = GST_PAD_PROBE_HANDLED; ++ gst_event_unref (ev); ++ break; ++ } + + gst_event_parse_stream (ev, &stream); + if (stream == NULL) { +@@ -1431,8 +1660,9 @@ multiqueue_src_probe (GstPad * pad, GstPadProbeInfo * info, + "Got a STREAM_START event without a GstStream"); + break; + } +- stream_id = gst_stream_get_stream_id (stream); +- GST_DEBUG_OBJECT (pad, "Stream Start '%s'", stream_id); ++ slot->is_drained = FALSE; ++ GST_DEBUG_OBJECT (pad, "Stream Start '%s'", ++ gst_stream_get_stream_id (stream)); + if (slot->active_stream == NULL) { + slot->active_stream = stream; + } else if (slot->active_stream != stream) { +@@ -1487,7 +1717,40 @@ multiqueue_src_probe (GstPad * pad, GstPadProbeInfo * info, + } + break; + case GST_EVENT_EOS: +- /* FIXME : Figure out */ ++ { ++ gboolean was_drained = slot->is_drained; ++ slot->is_drained = TRUE; ++ ++ /* Custom EOS handling first */ ++ if (gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (ev), ++ CUSTOM_EOS_QUARK)) { ++ /* remove custom-eos */ ++ gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (ev), ++ CUSTOM_EOS_QUARK, NULL, NULL); ++ GST_LOG_OBJECT (pad, "Received custom EOS"); ++ ret = GST_PAD_PROBE_HANDLED; ++ SELECTION_LOCK (dbin); ++ if (slot->input == NULL) { ++ GST_DEBUG_OBJECT (pad, ++ "Got custom-eos from null input stream, remove output stream"); ++ /* Remove the output */ ++ if (slot->output) { ++ DecodebinOutputStream *output = slot->output; ++ dbin->output_streams = ++ g_list_remove (dbin->output_streams, output); ++ free_output_stream (dbin, output); ++ } ++ slot->probe_id = 0; ++ dbin->slots = g_list_remove (dbin->slots, slot); ++ free_multiqueue_slot_async (dbin, slot); ++ ret = GST_PAD_PROBE_REMOVE; ++ } else if (!was_drained) { ++ check_all_slot_for_eos (dbin); ++ } ++ SELECTION_UNLOCK (dbin); ++ break; ++ } ++ + GST_FIXME_OBJECT (pad, "EOS on multiqueue source pad. input:%p", + slot->input); + if (slot->input == NULL) { +@@ -1510,9 +1773,25 @@ multiqueue_src_probe (GstPad * pad, GstPadProbeInfo * info, + dbin->output_streams = g_list_remove (dbin->output_streams, output); + free_output_stream (dbin, output); + } ++ slot->probe_id = 0; ++ dbin->slots = g_list_remove (dbin->slots, slot); ++ SELECTION_UNLOCK (dbin); ++ ++ free_multiqueue_slot_async (dbin, slot); ++ ret = GST_PAD_PROBE_REMOVE; ++ } else if (gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (ev), ++ CUSTOM_FINAL_EOS_QUARK)) { ++ GST_DEBUG_OBJECT (pad, "Got final eos, propagating downstream"); ++ } else { ++ GST_DEBUG_OBJECT (pad, "Got regular eos (all_inputs_are_eos)"); ++ /* drop current event as eos will be sent in check_all_slot_for_eos ++ * when all output streams are also eos */ ++ ret = GST_PAD_PROBE_DROP; ++ SELECTION_LOCK (dbin); ++ check_all_slot_for_eos (dbin); + SELECTION_UNLOCK (dbin); +- ret = GST_PAD_PROBE_HANDLED; + } ++ } + break; + default: + break; +@@ -1559,11 +1838,14 @@ create_new_slot (GstDecodebin3 * dbin, GstStreamType type) + gst_stream_type_get_name (type)); + slot = g_new0 (MultiQueueSlot, 1); + slot->dbin = dbin; ++ + slot->id = dbin->slot_id++; ++ + slot->type = type; + slot->sink_pad = gst_element_get_request_pad (dbin->multiqueue, "sink_%u"); + if (slot->sink_pad == NULL) + goto fail; ++ + it = gst_pad_iterate_internal_links (slot->sink_pad); + if (!it || (gst_iterator_next (it, &item)) != GST_ITERATOR_OK + || ((slot->src_pad = g_value_dup_object (&item)) == NULL)) { +@@ -1584,7 +1866,9 @@ create_new_slot (GstDecodebin3 * dbin, GstStreamType type) + + GST_DEBUG ("Created new slot %u (%p) (%s:%s)", slot->id, slot, + GST_DEBUG_PAD_NAME (slot->src_pad)); ++ + dbin->slots = g_list_append (dbin->slots, slot); ++ + return slot; + + /* ERRORS */ +@@ -1661,7 +1945,6 @@ get_slot_for_input (GstDecodebin3 * dbin, DecodebinInputStream * input) + static void + link_input_to_slot (DecodebinInputStream * input, MultiQueueSlot * slot) + { +- GstEvent *event; + if (slot->input != NULL && slot->input != input) { + GST_ERROR_OBJECT (slot->dbin, + "Trying to link input to an already used slot"); +@@ -1670,9 +1953,6 @@ link_input_to_slot (DecodebinInputStream * input, MultiQueueSlot * slot) + gst_pad_link_full (input->srcpad, slot->sink_pad, GST_PAD_LINK_CHECK_NOTHING); + slot->pending_stream = input->active_stream; + slot->input = input; +- event = gst_pad_get_sticky_event (input->srcpad, GST_EVENT_STREAM_START, 0); +- if (event) +- gst_pad_send_event (slot->sink_pad, event); + } + + #if 0 +@@ -2030,7 +2310,7 @@ reassign_slot (GstDecodebin3 * dbin, MultiQueueSlot * slot) + tsid = tmp->data; + /* Pass target stream id to requested selection */ + dbin->requested_selection = +- g_list_append (dbin->requested_selection, tmp->data); ++ g_list_append (dbin->requested_selection, g_strdup (tmp->data)); + dbin->to_activate = g_list_remove (dbin->to_activate, tmp->data); + break; + } +@@ -2159,7 +2439,9 @@ handle_stream_switch (GstDecodebin3 * dbin, GList * select_streams, + if (slot_to_deactivate) { + GST_DEBUG_OBJECT (dbin, + "Slot %p (%s) should be deactivated, no longer used", slot, +- gst_stream_get_stream_id (slot->active_stream)); ++ slot-> ++ active_stream ? gst_stream_get_stream_id (slot->active_stream) : ++ "NULL"); + to_deactivate = g_list_append (to_deactivate, slot); + } + } +@@ -2228,27 +2510,41 @@ handle_stream_switch (GstDecodebin3 * dbin, GList * select_streams, + if (to_activate == NULL && pending_streams != NULL) { + GST_DEBUG_OBJECT (dbin, "Stream switch requested for future collection"); + if (dbin->requested_selection) +- g_list_free (dbin->requested_selection); +- dbin->requested_selection = select_streams; ++ g_list_free_full (dbin->requested_selection, g_free); ++ dbin->requested_selection = ++ g_list_copy_deep (select_streams, (GCopyFunc) g_strdup, NULL); + g_list_free (to_deactivate); + g_list_free (pending_streams); + to_deactivate = NULL; ++ pending_streams = NULL; + } else { + if (dbin->requested_selection) +- g_list_free (dbin->requested_selection); +- dbin->requested_selection = future_request_streams; ++ g_list_free_full (dbin->requested_selection, g_free); ++ dbin->requested_selection = ++ g_list_copy_deep (future_request_streams, (GCopyFunc) g_strdup, NULL); + dbin->requested_selection = +- g_list_concat (dbin->requested_selection, pending_streams); ++ g_list_concat (dbin->requested_selection, ++ g_list_copy_deep (pending_streams, (GCopyFunc) g_strdup, NULL)); + if (dbin->to_activate) + g_list_free (dbin->to_activate); +- dbin->to_activate = to_reassign; ++ dbin->to_activate = g_list_copy (to_reassign); + } + + dbin->selection_updated = TRUE; + SELECTION_UNLOCK (dbin); + +- if (unknown) ++ if (unknown) { + GST_FIXME_OBJECT (dbin, "Got request for an unknown stream"); ++ g_list_free (unknown); ++ } ++ ++ if (to_activate && !slots_to_reassign) { ++ for (tmp = to_activate; tmp; tmp = tmp->next) { ++ MultiQueueSlot *slot = (MultiQueueSlot *) tmp->data; ++ gst_pad_add_probe (slot->src_pad, GST_PAD_PROBE_TYPE_IDLE, ++ (GstPadProbeCallback) idle_reconfigure, slot, NULL); ++ } ++ } + + /* For all streams to deactivate, add an idle probe where we will do + * the unassignment and switch over */ +@@ -2258,6 +2554,19 @@ handle_stream_switch (GstDecodebin3 * dbin, GList * select_streams, + (GstPadProbeCallback) slot_unassign_probe, slot, NULL); + } + ++ if (to_deactivate) ++ g_list_free (to_deactivate); ++ if (to_activate) ++ g_list_free (to_activate); ++ if (to_reassign) ++ g_list_free (to_reassign); ++ if (future_request_streams) ++ g_list_free (future_request_streams); ++ if (pending_streams) ++ g_list_free (pending_streams); ++ if (slots_to_reassign) ++ g_list_free (slots_to_reassign); ++ + return ret; + } + +@@ -2283,6 +2592,8 @@ ghost_pad_event_probe (GstPad * pad, GstPadProbeInfo * info, + SELECTION_UNLOCK (dbin); + GST_DEBUG_OBJECT (pad, + "Already handled/handling that SELECT_STREAMS event"); ++ gst_event_unref (event); ++ ret = GST_PAD_PROBE_HANDLED; + break; + } + dbin->select_streams_seqnum = seqnum; +@@ -2303,8 +2614,10 @@ ghost_pad_event_probe (GstPad * pad, GstPadProbeInfo * info, + gst_event_unref (event); + } + /* Finally handle the switch */ +- if (streams) ++ if (streams) { + handle_stream_switch (dbin, streams, seqnum); ++ g_list_free_full (streams, g_free); ++ } + ret = GST_PAD_PROBE_HANDLED; + } + break; +@@ -2350,8 +2663,10 @@ gst_decodebin3_send_event (GstElement * element, GstEvent * event) + } + #endif + /* Finally handle the switch */ +- if (streams) ++ if (streams) { + handle_stream_switch (dbin, streams, seqnum); ++ g_list_free_full (streams, g_free); ++ } + + gst_event_unref (event); + return TRUE; +@@ -2377,6 +2692,14 @@ free_multiqueue_slot (GstDecodebin3 * dbin, MultiQueueSlot * slot) + g_free (slot); + } + ++static void ++free_multiqueue_slot_async (GstDecodebin3 * dbin, MultiQueueSlot * slot) ++{ ++ GST_LOG_OBJECT (dbin, "pushing multiqueue slot on thread pool to free"); ++ gst_element_call_async (GST_ELEMENT_CAST (dbin), ++ (GstElementCallAsyncFunc) free_multiqueue_slot, slot, NULL); ++} ++ + /* Create a DecodebinOutputStream for a given type + * Note: It will be empty initially, it needs to be configured + * afterwards */ +@@ -2493,6 +2816,7 @@ gst_decodebin3_change_state (GstElement * element, GstStateChange transition) + } + g_list_free (dbin->slots); + dbin->slots = NULL; ++ dbin->current_group_id = GST_GROUP_ID_INVALID; + /* Free inputs */ + } + break; +diff --git a/gst/playback/gstparsebin.c b/gst/playback/gstparsebin.c +index 40ffa4b81..87ee11f63 100644 +--- a/gst/playback/gstparsebin.c ++++ b/gst/playback/gstparsebin.c +@@ -27,6 +27,7 @@ + + /** + * SECTION:element-parsebin ++ * @title: parsebin + * + * #GstBin that auto-magically constructs a parsing pipeline + * using available parsers and demuxers via auto-plugging. +@@ -105,15 +106,16 @@ + #include "gstplay-enum.h" + #include "gstplayback.h" + #include "gstplaybackutils.h" ++#include "gstrawcaps.h" + + /* generic templates */ +-static GstStaticPadTemplate decoder_bin_sink_template = ++static GstStaticPadTemplate parse_bin_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +-static GstStaticPadTemplate decoder_bin_src_template = ++static GstStaticPadTemplate parse_bin_src_template = + GST_STATIC_PAD_TEMPLATE ("src_%u", + GST_PAD_SRC, + GST_PAD_SOMETIMES, +@@ -168,8 +170,6 @@ struct _GstParseBin + gboolean have_type; /* if we received the have_type signal */ + guint have_type_id; /* signal id for have-type from typefind */ + +- gboolean async_pending; /* async-start has been emitted */ +- + GMutex dyn_lock; /* lock protecting pad blocking */ + gboolean shutdown; /* if we are shutting down */ + GList *blocked_pads; /* pads that have set to block */ +@@ -178,16 +178,23 @@ struct _GstParseBin + + GList *filtered; /* elements for which error messages are filtered */ + GList *filtered_errors; /* filtered error messages */ ++ ++ GMutex cleanup_lock; /* Mutex used to protect the cleanup thread */ ++ GThread *cleanup_thread; /* thread used to free chains asynchronously. ++ * We store it to make sure we end up joining it ++ * before stopping the element. ++ * Protected by the object lock */ ++ + }; + + struct _GstParseBinClass + { + GstBinClass parent_class; + +- /* signal fired when we found a pad that we cannot decode */ ++ /* signal fired when we found a pad that we cannot parse */ + void (*unknown_type) (GstElement * element, GstPad * pad, GstCaps * caps); + +- /* signal fired to know if we continue trying to decode the given caps */ ++ /* signal fired to know if we continue trying to parse the given caps */ + gboolean (*autoplug_continue) (GstElement * element, GstPad * pad, + GstCaps * caps); + /* signal fired to get a list of factories to try to autoplug */ +@@ -242,9 +249,6 @@ enum + static GstBinClass *parent_class; + static guint gst_parse_bin_signals[LAST_SIGNAL] = { 0 }; + +-static void do_async_start (GstParseBin * parsebin); +-static void do_async_done (GstParseBin * parsebin); +- + static void type_found (GstElement * typefind, guint probability, + GstCaps * caps, GstParseBin * parse_bin); + +@@ -278,6 +282,7 @@ static void gst_parse_pad_update_stream_collection (GstParsePad * parsepad, + GstStreamCollection * collection); + + static GstCaps *get_pad_caps (GstPad * pad); ++static GstStreamType guess_stream_type_from_caps (GstCaps * caps); + + #define EXPOSE_LOCK(parsebin) G_STMT_START { \ + GST_LOG_OBJECT (parsebin, \ +@@ -397,7 +402,7 @@ struct _GstParseChain + * once it can be exposed */ + GstParsePad *endpad; /* Pad of this chain that could be exposed */ + gboolean deadend; /* This chain is incomplete and can't be completed, +- e.g. no suitable decoder could be found ++ e.g. no suitable parser could be found + e.g. stream got EOS without buffers + */ + gchar *deadend_details; +@@ -631,7 +636,7 @@ gst_parse_bin_class_init (GstParseBinClass * klass) + * @caps: The #GstCaps of the pad that cannot be resolved. + * + * This signal is emitted when a pad for which there is no further possible +- * decoding is added to the ParseBin. ++ * parsing is added to the ParseBin. + */ + gst_parse_bin_signals[SIGNAL_UNKNOWN_TYPE] = + g_signal_new ("unknown-type", G_TYPE_FROM_CLASS (klass), +@@ -648,14 +653,12 @@ gst_parse_bin_class_init (GstParseBinClass * klass) + * This signal is emitted whenever ParseBin finds a new stream. It is + * emitted before looking for any elements that can handle that stream. + * +- * +- * Invocation of signal handlers stops after the first signal handler +- * returns #FALSE. Signal handlers are invoked in the order they were +- * connected in. +- * ++ * > Invocation of signal handlers stops after the first signal handler ++ * > returns %FALSE. Signal handlers are invoked in the order they were ++ * > connected in. + * +- * Returns: #TRUE if you wish ParseBin to look for elements that can +- * handle the given @caps. If #FALSE, those caps will be considered as ++ * Returns: %TRUE if you wish ParseBin to look for elements that can ++ * handle the given @caps. If %FALSE, those caps will be considered as + * final and the pad will be exposed as such (see 'pad-added' signal of + * #GstElement). + */ +@@ -680,11 +683,9 @@ gst_parse_bin_class_init (GstParseBinClass * klass) + * If this function returns an empty array, the pad will be considered as + * having an unhandled type media type. + * +- * +- * Only the signal handler that is connected first will ever by invoked. +- * Don't connect signal handlers with the #G_CONNECT_AFTER flag to this +- * signal, they will never be invoked! +- * ++ * > Only the signal handler that is connected first will ever by invoked. ++ * > Don't connect signal handlers with the #G_CONNECT_AFTER flag to this ++ * > signal, they will never be invoked! + * + * Returns: a #GValueArray* with a list of factories to try. The factories are + * by default tried in the returned order or based on the index returned by +@@ -709,16 +710,14 @@ gst_parse_bin_class_init (GstParseBinClass * klass) + * the application to perform additional sorting or filtering on the element + * factory array. + * +- * The callee should copy and modify @factories or return #NULL if the ++ * The callee should copy and modify @factories or return %NULL if the + * order should not change. + * +- * +- * Invocation of signal handlers stops after one signal handler has +- * returned something else than #NULL. Signal handlers are invoked in +- * the order they were connected in. +- * Don't connect signal handlers with the #G_CONNECT_AFTER flag to this +- * signal, they will never be invoked! +- * ++ * > Invocation of signal handlers stops after one signal handler has ++ * > returned something else than %NULL. Signal handlers are invoked in ++ * > the order they were connected in. ++ * > Don't connect signal handlers with the #G_CONNECT_AFTER flag to this ++ * > signal, they will never be invoked! + * + * Returns: A new sorted array of #GstElementFactory objects. + */ +@@ -752,13 +751,11 @@ gst_parse_bin_class_init (GstParseBinClass * klass) + * A value of #GST_AUTOPLUG_SELECT_SKIP will skip @factory and move to the + * next factory. + * +- * +- * The signal handler will not be invoked if any of the previously +- * registered signal handlers (if any) return a value other than +- * GST_AUTOPLUG_SELECT_TRY. Which also means that if you return +- * GST_AUTOPLUG_SELECT_TRY from one signal handler, handlers that get +- * registered next (again, if any) can override that decision. +- * ++ * > The signal handler will not be invoked if any of the previously ++ * > registered signal handlers (if any) return a value other than ++ * > GST_AUTOPLUG_SELECT_TRY. Which also means that if you return ++ * > GST_AUTOPLUG_SELECT_TRY from one signal handler, handlers that get ++ * > registered next (again, if any) can override that decision. + * + * Returns: a #GST_TYPE_AUTOPLUG_SELECT_RESULT that indicates the required + * operation. the default handler will always return +@@ -785,7 +782,7 @@ gst_parse_bin_class_init (GstParseBinClass * klass) + * be used to tell the element about the downstream supported caps + * for example. + * +- * Returns: #TRUE if the query was handled, #FALSE otherwise. ++ * Returns: %TRUE if the query was handled, %FALSE otherwise. + */ + gst_parse_bin_signals[SIGNAL_AUTOPLUG_QUERY] = + g_signal_new ("autoplug-query", G_TYPE_FROM_CLASS (klass), +@@ -798,7 +795,7 @@ gst_parse_bin_class_init (GstParseBinClass * klass) + * GstParseBin::drained + * @bin: The ParseBin + * +- * This signal is emitted once ParseBin has finished decoding all the data. ++ * This signal is emitted once ParseBin has finished parsing all the data. + */ + gst_parse_bin_signals[SIGNAL_DRAINED] = + g_signal_new ("drained", G_TYPE_FROM_CLASS (klass), +@@ -823,9 +820,9 @@ gst_parse_bin_class_init (GstParseBinClass * klass) + * + * Expose streams of unknown type. + * +- * If set to %FALSE, then only the streams that can be decoded to the final ++ * If set to %FALSE, then only the streams that can be parsed to the final + * caps (see 'caps' property) will have a pad exposed. Streams that do not +- * match those caps but could have been decoded will not have decoder plugged ++ * match those caps but could have been parsed will not have parser plugged + * in internally and will not have a pad exposed. + */ + g_object_class_install_property (gobject_klass, PROP_EXPOSE_ALL_STREAMS, +@@ -856,9 +853,9 @@ gst_parse_bin_class_init (GstParseBinClass * klass) + klass->autoplug_query = GST_DEBUG_FUNCPTR (gst_parse_bin_autoplug_query); + + gst_element_class_add_pad_template (gstelement_klass, +- gst_static_pad_template_get (&decoder_bin_sink_template)); ++ gst_static_pad_template_get (&parse_bin_sink_template)); + gst_element_class_add_pad_template (gstelement_klass, +- gst_static_pad_template_get (&decoder_bin_src_template)); ++ gst_static_pad_template_get (&parse_bin_src_template)); + + gst_element_class_set_static_metadata (gstelement_klass, + "Parse Bin", "Generic/Bin/Parser", +@@ -920,7 +917,7 @@ gst_parse_bin_init (GstParseBin * parse_bin) + pad = gst_element_get_static_pad (parse_bin->typefind, "sink"); + + /* get the pad template */ +- pad_tmpl = gst_static_pad_template_get (&decoder_bin_sink_template); ++ pad_tmpl = gst_static_pad_template_get (&parse_bin_sink_template); + + /* ghost the sink pad to ourself */ + gpad = gst_ghost_pad_new_from_template ("sink", pad, pad_tmpl); +@@ -945,6 +942,9 @@ gst_parse_bin_init (GstParseBin * parse_bin) + parse_bin->expose_allstreams = DEFAULT_EXPOSE_ALL_STREAMS; + parse_bin->connection_speed = DEFAULT_CONNECTION_SPEED; + ++ g_mutex_init (&parse_bin->cleanup_lock); ++ parse_bin->cleanup_thread = NULL; ++ + GST_OBJECT_FLAG_SET (parse_bin, GST_BIN_FLAG_STREAMS_AWARE); + } + +@@ -983,6 +983,7 @@ gst_parse_bin_finalize (GObject * object) + g_mutex_clear (&parse_bin->dyn_lock); + g_mutex_clear (&parse_bin->subtitle_lock); + g_mutex_clear (&parse_bin->factories_lock); ++ g_mutex_clear (&parse_bin->cleanup_lock); + + G_OBJECT_CLASS (parent_class)->finalize (object); + } +@@ -1106,9 +1107,18 @@ static gboolean + gst_parse_bin_autoplug_continue (GstElement * element, GstPad * pad, + GstCaps * caps) + { ++ static GstStaticCaps raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS); ++ ++ GST_DEBUG_OBJECT (element, "caps %" GST_PTR_FORMAT, caps); ++ ++ /* If it matches our target caps, expose it */ ++ if (gst_caps_can_intersect (caps, gst_static_caps_get (&raw_caps))) { ++ GST_DEBUG_OBJECT (element, "autoplug-continue returns FALSE"); ++ return FALSE; ++ } ++ + GST_DEBUG_OBJECT (element, "autoplug-continue returns TRUE"); + +- /* by default we always continue */ + return TRUE; + } + +@@ -1179,7 +1189,7 @@ static gboolean is_demuxer_element (GstElement * srcelement); + static gboolean connect_pad (GstParseBin * parsebin, GstElement * src, + GstParsePad * parsepad, GstPad * pad, GstCaps * caps, + GValueArray * factories, GstParseChain * chain, gchar ** deadend_details); +-static GList *connect_element (GstParseBin * parsebin, GstParseElement * delem, ++static GList *connect_element (GstParseBin * parsebin, GstParseElement * pelem, + GstParseChain * chain); + static void expose_pad (GstParseBin * parsebin, GstElement * src, + GstParsePad * parsepad, GstPad * pad, GstCaps * caps, +@@ -1397,7 +1407,7 @@ analyze_new_pad (GstParseBin * parsebin, GstElement * src, GstPad * pad, + if (factories == NULL) + goto expose_pad; + +- /* if the array is empty, we have a type for which we have no decoder */ ++ /* if the array is empty, we have a type for which we have no parser */ + if (factories->n_values == 0) { + /* if not we have a unhandled type with no compatible factories */ + g_value_array_free (factories); +@@ -1421,10 +1431,10 @@ analyze_new_pad (GstParseBin * parsebin, GstElement * src, GstPad * pad, + GstCaps *filter_caps; + gint i; + GstPad *p; +- GstParseElement *delem; ++ GstParseElement *pelem; + + g_assert (chain->elements != NULL); +- delem = (GstParseElement *) chain->elements->data; ++ pelem = (GstParseElement *) chain->elements->data; + + filter_caps = gst_caps_new_empty (); + for (i = 0; i < factories->n_values; i++) { +@@ -1459,17 +1469,17 @@ analyze_new_pad (GstParseBin * parsebin, GstElement * src, GstPad * pad, + /* Append the parser caps to prevent any not-negotiated errors */ + filter_caps = gst_caps_merge (filter_caps, gst_caps_ref (caps)); + +- delem->capsfilter = gst_element_factory_make ("capsfilter", NULL); +- g_object_set (G_OBJECT (delem->capsfilter), "caps", filter_caps, NULL); ++ pelem->capsfilter = gst_element_factory_make ("capsfilter", NULL); ++ g_object_set (G_OBJECT (pelem->capsfilter), "caps", filter_caps, NULL); + gst_caps_unref (filter_caps); +- gst_element_set_state (delem->capsfilter, GST_STATE_PAUSED); +- gst_bin_add (GST_BIN_CAST (parsebin), gst_object_ref (delem->capsfilter)); ++ gst_element_set_state (pelem->capsfilter, GST_STATE_PAUSED); ++ gst_bin_add (GST_BIN_CAST (parsebin), gst_object_ref (pelem->capsfilter)); + + parse_pad_set_target (parsepad, NULL); +- p = gst_element_get_static_pad (delem->capsfilter, "sink"); ++ p = gst_element_get_static_pad (pelem->capsfilter, "sink"); + gst_pad_link_full (pad, p, GST_PAD_LINK_CHECK_NOTHING); + gst_object_unref (p); +- p = gst_element_get_static_pad (delem->capsfilter, "src"); ++ p = gst_element_get_static_pad (pelem->capsfilter, "src"); + parse_pad_set_target (parsepad, p); + pad = p; + +@@ -1521,6 +1531,7 @@ unknown_type: + + chain->deadend_details = deadend_details; + chain->deadend = TRUE; ++ chain->drained = TRUE; + chain->endcaps = caps; + gst_object_replace ((GstObject **) & chain->current_pad, NULL); + +@@ -1544,7 +1555,6 @@ unknown_type: + GST_ELEMENT_ERROR (parsebin, STREAM, TYPE_NOT_FOUND, + (_("Could not determine type of stream")), (NULL)); + } +- do_async_done (parsebin); + } + return; + } +@@ -1749,7 +1759,7 @@ connect_pad (GstParseBin * parsebin, GstElement * src, GstParsePad * parsepad, + while (factories->n_values > 0) { + GstAutoplugSelectResult ret; + GstElementFactory *factory; +- GstParseElement *delem; ++ GstParseElement *pelem; + GstElement *element; + GstPad *sinkpad; + GParamSpec *pspec; +@@ -1777,10 +1787,10 @@ connect_pad (GstParseBin * parsebin, GstElement * src, GstParsePad * parsepad, + * FIXME: Only do this for fixed caps here. Non-fixed caps + * can happen if a Parser/Converter was autoplugged before + * this. We then assume that it will be able to convert to +- * everything that the decoder would want. ++ * everything that the parser would want. + * + * A subset check will fail here because the parser caps +- * will be generic and while the decoder will only ++ * will be generic and while the parser will only + * support a subset of the parser caps. + */ + if (gst_caps_is_fixed (caps)) { +@@ -1831,8 +1841,8 @@ connect_pad (GstParseBin * parsebin, GstElement * src, GstParsePad * parsepad, + + CHAIN_MUTEX_LOCK (chain); + for (l = chain->elements; l; l = l->next) { +- GstParseElement *delem = (GstParseElement *) l->data; +- GstElement *otherelement = delem->element; ++ GstParseElement *pelem = (GstParseElement *) l->data; ++ GstElement *otherelement = pelem->element; + + if (gst_element_get_factory (otherelement) == factory) { + skip = TRUE; +@@ -2005,10 +2015,10 @@ connect_pad (GstParseBin * parsebin, GstElement * src, GstParsePad * parsepad, + GST_LOG_OBJECT (parsebin, "linked on pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + CHAIN_MUTEX_LOCK (chain); +- delem = g_slice_new0 (GstParseElement); +- delem->element = gst_object_ref (element); +- delem->capsfilter = NULL; +- chain->elements = g_list_prepend (chain->elements, delem); ++ pelem = g_slice_new0 (GstParseElement); ++ pelem->element = gst_object_ref (element); ++ pelem->capsfilter = NULL; ++ chain->elements = g_list_prepend (chain->elements, pelem); + chain->demuxer = is_demuxer_element (element); + + /* If we plugging a parser, mark the chain as parsed */ +@@ -2075,7 +2085,7 @@ connect_pad (GstParseBin * parsebin, GstElement * src, GstParsePad * parsepad, + } + + /* link this element further */ +- to_connect = connect_element (parsebin, delem, chain); ++ to_connect = connect_element (parsebin, pelem, chain); + + if ((is_simple_demuxer || is_parser_converter) && to_connect) { + GList *l; +@@ -2084,7 +2094,7 @@ connect_pad (GstParseBin * parsebin, GstElement * src, GstParsePad * parsepad, + GstCaps *ocaps; + + ocaps = get_pad_caps (opad); +- analyze_new_pad (parsebin, delem->element, opad, ocaps, chain); ++ analyze_new_pad (parsebin, pelem->element, opad, ocaps, chain); + if (ocaps) + gst_caps_unref (ocaps); + +@@ -2209,7 +2219,7 @@ connect_pad (GstParseBin * parsebin, GstElement * src, GstParsePad * parsepad, + GstCaps *ocaps; + + ocaps = get_pad_caps (opad); +- analyze_new_pad (parsebin, delem->element, opad, ocaps, chain); ++ analyze_new_pad (parsebin, pelem->element, opad, ocaps, chain); + if (ocaps) + gst_caps_unref (ocaps); + +@@ -2254,10 +2264,10 @@ get_pad_caps (GstPad * pad) + /* Returns a list of pads that can be connected to already and + * connects to pad-added and related signals */ + static GList * +-connect_element (GstParseBin * parsebin, GstParseElement * delem, ++connect_element (GstParseBin * parsebin, GstParseElement * pelem, + GstParseChain * chain) + { +- GstElement *element = delem->element; ++ GstElement *element = pelem->element; + GList *pads; + gboolean dynamic = FALSE; + GList *to_connect = NULL; +@@ -2330,11 +2340,11 @@ connect_element (GstParseBin * parsebin, GstParseElement * delem, + if (dynamic) { + GST_LOG_OBJECT (parsebin, "Adding signals to element %s in chain %p", + GST_ELEMENT_NAME (element), chain); +- delem->pad_added_id = g_signal_connect (element, "pad-added", ++ pelem->pad_added_id = g_signal_connect (element, "pad-added", + G_CALLBACK (pad_added_cb), chain); +- delem->pad_removed_id = g_signal_connect (element, "pad-removed", ++ pelem->pad_removed_id = g_signal_connect (element, "pad-removed", + G_CALLBACK (pad_removed_cb), chain); +- delem->no_more_pads_id = g_signal_connect (element, "no-more-pads", ++ pelem->no_more_pads_id = g_signal_connect (element, "no-more-pads", + G_CALLBACK (no_more_pads_cb), chain); + } + +@@ -2345,7 +2355,7 @@ connect_element (GstParseBin * parsebin, GstParseElement * delem, + + /* expose_pad: + * +- * Expose the given pad on the chain as a decoded pad. ++ * Expose the given pad on the chain as a parsed pad. + */ + static void + expose_pad (GstParseBin * parsebin, GstElement * src, GstParsePad * parsepad, +@@ -2376,7 +2386,7 @@ type_found (GstElement * typefind, guint probability, + if (gst_structure_has_name (gst_caps_get_structure (caps, 0), "text/plain")) { + GST_ELEMENT_ERROR (parse_bin, STREAM, WRONG_TYPE, + (_("This appears to be a text file")), +- ("ParseBin cannot decode plain text files")); ++ ("ParseBin cannot parse plain text files")); + goto exit; + } + +@@ -2710,26 +2720,26 @@ gst_parse_chain_free_internal (GstParseChain * chain, gboolean hide) + chain->pending_pads = NULL; + + for (l = chain->elements; l; l = l->next) { +- GstParseElement *delem = l->data; +- GstElement *element = delem->element; +- +- if (delem->pad_added_id) +- g_signal_handler_disconnect (element, delem->pad_added_id); +- delem->pad_added_id = 0; +- if (delem->pad_removed_id) +- g_signal_handler_disconnect (element, delem->pad_removed_id); +- delem->pad_removed_id = 0; +- if (delem->no_more_pads_id) +- g_signal_handler_disconnect (element, delem->no_more_pads_id); +- delem->no_more_pads_id = 0; +- +- if (delem->capsfilter) { +- if (GST_OBJECT_PARENT (delem->capsfilter) == ++ GstParseElement *pelem = l->data; ++ GstElement *element = pelem->element; ++ ++ if (pelem->pad_added_id) ++ g_signal_handler_disconnect (element, pelem->pad_added_id); ++ pelem->pad_added_id = 0; ++ if (pelem->pad_removed_id) ++ g_signal_handler_disconnect (element, pelem->pad_removed_id); ++ pelem->pad_removed_id = 0; ++ if (pelem->no_more_pads_id) ++ g_signal_handler_disconnect (element, pelem->no_more_pads_id); ++ pelem->no_more_pads_id = 0; ++ ++ if (pelem->capsfilter) { ++ if (GST_OBJECT_PARENT (pelem->capsfilter) == + GST_OBJECT_CAST (chain->parsebin)) +- gst_bin_remove (GST_BIN_CAST (chain->parsebin), delem->capsfilter); ++ gst_bin_remove (GST_BIN_CAST (chain->parsebin), pelem->capsfilter); + if (!hide) { + set_to_null = +- g_list_append (set_to_null, gst_object_ref (delem->capsfilter)); ++ g_list_append (set_to_null, gst_object_ref (pelem->capsfilter)); + } + } + +@@ -2746,15 +2756,15 @@ gst_parse_chain_free_internal (GstParseChain * chain, gboolean hide) + SUBTITLE_UNLOCK (chain->parsebin); + + if (!hide) { +- if (delem->capsfilter) { +- gst_object_unref (delem->capsfilter); +- delem->capsfilter = NULL; ++ if (pelem->capsfilter) { ++ gst_object_unref (pelem->capsfilter); ++ pelem->capsfilter = NULL; + } + + gst_object_unref (element); + l->data = NULL; + +- g_slice_free (GstParseElement, delem); ++ g_slice_free (GstParseElement, pelem); + } + } + if (!hide) { +@@ -2951,11 +2961,19 @@ gst_parse_chain_start_free_hidden_groups_thread (GstParseChain * chain) + GThread *thread; + GError *error = NULL; + GList *old_groups; ++ GstParseBin *parsebin = chain->parsebin; + + old_groups = chain->old_groups; + if (!old_groups) + return; + ++ /* If we already have a thread running, wait for it to finish */ ++ g_mutex_lock (&parsebin->cleanup_lock); ++ if (parsebin->cleanup_thread) { ++ g_thread_join (parsebin->cleanup_thread); ++ parsebin->cleanup_thread = NULL; ++ } ++ + chain->old_groups = NULL; + thread = g_thread_try_new ("free-hidden-groups", + (GThreadFunc) gst_parse_chain_free_hidden_groups, old_groups, &error); +@@ -2964,11 +2982,14 @@ gst_parse_chain_start_free_hidden_groups_thread (GstParseChain * chain) + error ? error->message : "unknown reason"); + g_clear_error (&error); + chain->old_groups = old_groups; ++ g_mutex_unlock (&parsebin->cleanup_lock); + return; + } ++ ++ parsebin->cleanup_thread = thread; ++ g_mutex_unlock (&parsebin->cleanup_lock); ++ + GST_DEBUG_OBJECT (chain->parsebin, "Started free-hidden-groups thread"); +- /* We do not need to wait for it or get any results from it */ +- g_thread_unref (thread); + } + + /* gst_parse_group_new: +@@ -3223,14 +3244,13 @@ drain_and_switch_chains (GstParseChain * chain, GstParsePad * drainpad, + beach: + CHAIN_MUTEX_UNLOCK (chain); + +- GST_DEBUG ("Chain %p (handled:%d, last_group:%d, drained:%d, switched:%d)", +- chain, handled, *last_group, chain->drained, *switched); ++ GST_DEBUG ++ ("Chain %p (%s:%s handled:%d, last_group:%d, drained:%d, switched:%d, deadend:%d)", ++ chain, GST_DEBUG_PAD_NAME (chain->pad), handled, *last_group, ++ chain->drained, *switched, chain->deadend); + + *drained = chain->drained; + +- if (*drained) +- g_signal_emit (parsebin, gst_parse_bin_signals[SIGNAL_DRAINED], 0, NULL); +- + return handled; + } + +@@ -3251,11 +3271,18 @@ gst_parse_pad_handle_eos (GstParsePad * pad) + drain_and_switch_chains (parsebin->parse_chain, pad, &last_group, &drained, + &switched); + ++ GST_LOG_OBJECT (parsebin, "drained:%d switched:%d", drained, switched); + if (switched) { + /* If we resulted in a group switch, expose what's needed */ + if (gst_parse_chain_is_complete (parsebin->parse_chain)) + gst_parse_bin_expose (parsebin); + } ++ ++ if (drained) { ++ GST_DEBUG_OBJECT (parsebin, "We are fully drained, emitting signal"); ++ g_signal_emit (parsebin, gst_parse_bin_signals[SIGNAL_DRAINED], 0, NULL); ++ } ++ + } + EXPOSE_UNLOCK (parsebin); + +@@ -3492,7 +3519,6 @@ retry: + } + } + +- do_async_done (parsebin); + return FALSE; + } + +@@ -3538,6 +3564,15 @@ retry: + /* re-order pads : video, then audio, then others */ + endpads = g_list_sort (endpads, (GCompareFunc) sort_end_pads); + ++ /* Don't expose if we're currently shutting down */ ++ DYN_LOCK (parsebin); ++ if (G_UNLIKELY (parsebin->shutdown)) { ++ GST_WARNING_OBJECT (parsebin, ++ "Currently, shutting down, aborting exposing"); ++ DYN_UNLOCK (parsebin); ++ return FALSE; ++ } ++ + /* Expose pads */ + for (tmp = endpads; tmp; tmp = tmp->next) { + GstParsePad *parsepad = (GstParsePad *) tmp->data; +@@ -3574,9 +3609,11 @@ retry: + #endif + } + +- GST_INFO_OBJECT (parsepad, "added new decoded pad"); ++ GST_INFO_OBJECT (parsepad, "added new parsed pad"); + } + ++ DYN_UNLOCK (parsebin); ++ + /* Unblock internal pads. The application should have connected stuff now + * so that streaming can continue. */ + for (tmp = endpads; tmp; tmp = tmp->next) { +@@ -3604,7 +3641,6 @@ retry: + /* Remove old groups */ + chain_remove_old_groups (parsebin->parse_chain); + +- do_async_done (parsebin); + GST_DEBUG_OBJECT (parsebin, "Exposed everything"); + return TRUE; + } +@@ -3634,7 +3670,7 @@ gst_parse_chain_expose (GstParseChain * chain, GList ** endpads, + gchar *desc = gst_pb_utils_get_codec_description (chain->endcaps); + gchar *caps_str = gst_caps_to_string (chain->endcaps); + g_string_append_printf (missing_plugin_details, +- "Missing decoder: %s (%s)\n", desc, caps_str); ++ "Missing parser: %s (%s)\n", desc, caps_str); + g_free (caps_str); + g_free (desc); + } +@@ -3714,6 +3750,20 @@ build_fallback_collection (GstParseChain * chain, + + if (p->active_stream != NULL && p->active_collection == NULL) { + GST_DEBUG_OBJECT (p, "Adding stream to fallback collection"); ++ if (G_UNLIKELY (gst_stream_get_stream_type (p->active_stream) == ++ GST_STREAM_TYPE_UNKNOWN)) { ++ GstCaps *caps; ++ caps = get_pad_caps (GST_PAD_CAST (p)); ++ ++ if (caps) { ++ GstStreamType type = guess_stream_type_from_caps (caps); ++ if (type != GST_STREAM_TYPE_UNKNOWN) { ++ gst_stream_set_stream_type (p->active_stream, type); ++ gst_stream_set_caps (p->active_stream, caps); ++ } ++ gst_caps_unref (caps); ++ } ++ } + gst_stream_collection_add_stream (collection, + gst_object_ref (p->active_stream)); + p->in_a_fallback_collection = TRUE; +@@ -4112,15 +4162,15 @@ gst_parse_pad_query (GstPad * pad, GstObject * parent, GstQuery * query) + CHAIN_MUTEX_LOCK (parsepad->chain); + if (!parsepad->exposed && !parsepad->parsebin->shutdown + && !parsepad->chain->deadend && parsepad->chain->elements) { +- GstParseElement *delem = parsepad->chain->elements->data; ++ GstParseElement *pelem = parsepad->chain->elements->data; + + ret = FALSE; + GST_DEBUG_OBJECT (parsepad->parsebin, + "calling autoplug-query for %s (element %s): %" GST_PTR_FORMAT, +- GST_PAD_NAME (parsepad), GST_ELEMENT_NAME (delem->element), query); ++ GST_PAD_NAME (parsepad), GST_ELEMENT_NAME (pelem->element), query); + g_signal_emit (G_OBJECT (parsepad->parsebin), + gst_parse_bin_signals[SIGNAL_AUTOPLUG_QUERY], 0, parsepad, +- delem->element, query, &ret); ++ pelem->element, query, &ret); + + if (ret) + GST_DEBUG_OBJECT (parsepad->parsebin, +@@ -4148,8 +4198,8 @@ gst_parse_pad_new (GstParseBin * parsebin, GstParseChain * chain) + GstProxyPad *ppad; + GstPadTemplate *pad_tmpl; + +- GST_DEBUG_OBJECT (parsebin, "making new decodepad"); +- pad_tmpl = gst_static_pad_template_get (&decoder_bin_src_template); ++ GST_DEBUG_OBJECT (parsebin, "making new parsepad"); ++ pad_tmpl = gst_static_pad_template_get (&parse_bin_src_template); + parsepad = + g_object_new (GST_TYPE_PARSE_PAD, "direction", GST_PAD_SRC, + "template", pad_tmpl, NULL); +@@ -4189,32 +4239,6 @@ gst_pending_pad_free (GstPendingPad * ppad) + * Element add/remove + *****/ + +-static void +-do_async_start (GstParseBin * parsebin) +-{ +- GstMessage *message; +- +- parsebin->async_pending = TRUE; +- +- message = gst_message_new_async_start (GST_OBJECT_CAST (parsebin)); +- parent_class->handle_message (GST_BIN_CAST (parsebin), message); +-} +- +-static void +-do_async_done (GstParseBin * parsebin) +-{ +- GstMessage *message; +- +- if (parsebin->async_pending) { +- message = +- gst_message_new_async_done (GST_OBJECT_CAST (parsebin), +- GST_CLOCK_TIME_NONE); +- parent_class->handle_message (GST_BIN_CAST (parsebin), message); +- +- parsebin->async_pending = FALSE; +- } +-} +- + /* call with dyn_lock held */ + static void + unblock_pads (GstParseBin * parsebin) +@@ -4274,8 +4298,6 @@ gst_parse_bin_change_state (GstElement * element, GstStateChange transition) + parsebin->shutdown = FALSE; + DYN_UNLOCK (parsebin); + parsebin->have_type = FALSE; +- ret = GST_STATE_CHANGE_ASYNC; +- do_async_start (parsebin); + + + /* connect a signal to find out when the typefind element found +@@ -4298,20 +4320,12 @@ gst_parse_bin_change_state (GstElement * element, GstStateChange transition) + break; + } + +- { +- GstStateChangeReturn bret; +- +- bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); +- if (G_UNLIKELY (bret == GST_STATE_CHANGE_FAILURE)) +- goto activate_failed; +- else if (G_UNLIKELY (bret == GST_STATE_CHANGE_NO_PREROLL)) { +- do_async_done (parsebin); +- ret = bret; +- } +- } ++ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); ++ if (G_UNLIKELY (ret == GST_STATE_CHANGE_FAILURE)) ++ goto activate_failed; ++ + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: +- do_async_done (parsebin); + EXPOSE_LOCK (parsebin); + if (parsebin->parse_chain) { + chain_to_free = parsebin->parse_chain; +@@ -4323,6 +4337,12 @@ gst_parse_bin_change_state (GstElement * element, GstStateChange transition) + gst_parse_chain_free (chain_to_free); + break; + case GST_STATE_CHANGE_READY_TO_NULL: ++ g_mutex_lock (&parsebin->cleanup_lock); ++ if (parsebin->cleanup_thread) { ++ g_thread_join (parsebin->cleanup_thread); ++ parsebin->cleanup_thread = NULL; ++ } ++ g_mutex_unlock (&parsebin->cleanup_lock); + default: + break; + } +@@ -4342,7 +4362,6 @@ activate_failed: + { + GST_DEBUG_OBJECT (element, + "element failed to change states -- activation problem?"); +- do_async_done (parsebin); + return GST_STATE_CHANGE_FAILURE; + } + } +@@ -4355,12 +4374,23 @@ gst_parse_bin_handle_message (GstBin * bin, GstMessage * msg) + + switch (GST_MESSAGE_TYPE (msg)) { + case GST_MESSAGE_ERROR:{ +- GST_OBJECT_LOCK (parsebin); +- drop = (g_list_find (parsebin->filtered, GST_MESSAGE_SRC (msg)) != NULL); +- if (drop) +- parsebin->filtered_errors = +- g_list_prepend (parsebin->filtered_errors, gst_message_ref (msg)); +- GST_OBJECT_UNLOCK (parsebin); ++ /* Don't pass errors when shutting down. Sometimes, ++ * elements can generate spurious errors because we set the ++ * output pads to flushing, and they can't detect that if they ++ * send an event at exactly the wrong moment */ ++ DYN_LOCK (parsebin); ++ drop = parsebin->shutdown; ++ DYN_UNLOCK (parsebin); ++ ++ if (!drop) { ++ GST_OBJECT_LOCK (parsebin); ++ drop = ++ (g_list_find (parsebin->filtered, GST_MESSAGE_SRC (msg)) != NULL); ++ if (drop) ++ parsebin->filtered_errors = ++ g_list_prepend (parsebin->filtered_errors, gst_message_ref (msg)); ++ GST_OBJECT_UNLOCK (parsebin); ++ } + break; + } + default: +diff --git a/gst/playback/gstplayback.c b/gst/playback/gstplayback.c +index 4789db6df..3c3efa52e 100644 +--- a/gst/playback/gstplayback.c ++++ b/gst/playback/gstplayback.c +@@ -63,6 +63,7 @@ plugin_init (GstPlugin * plugin) + res &= gst_decode_bin_plugin_init (plugin); + res &= gst_decodebin3_plugin_init (plugin); + res &= gst_uri_decode_bin_plugin_init (plugin); ++ res &= gst_uri_decode_bin3_plugin_init (plugin); + res &= gst_uri_source_bin_plugin_init (plugin); + res &= gst_parse_bin_plugin_init (plugin); + +diff --git a/gst/playback/gstplayback.h b/gst/playback/gstplayback.h +index eaa0564ba..26e3683cf 100644 +--- a/gst/playback/gstplayback.h ++++ b/gst/playback/gstplayback.h +@@ -26,6 +26,7 @@ + gboolean gst_decode_bin_plugin_init (GstPlugin * plugin); + gboolean gst_decodebin3_plugin_init (GstPlugin * plugin); + gboolean gst_uri_decode_bin_plugin_init (GstPlugin * plugin); ++gboolean gst_uri_decode_bin3_plugin_init (GstPlugin * plugin); + gboolean gst_uri_source_bin_plugin_init (GstPlugin * plugin); + gboolean gst_parse_bin_plugin_init (GstPlugin * plugin); + +diff --git a/gst/playback/gstplaybin3.c b/gst/playback/gstplaybin3.c +index d1c28d5e1..027766bb3 100644 +--- a/gst/playback/gstplaybin3.c ++++ b/gst/playback/gstplaybin3.c +@@ -20,9 +20,9 @@ + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +- + /** + * SECTION:element-playbin3 ++ * @title: playbin3 + * + * playbin3 provides a stand-alone everything-in-one abstraction for an + * audio and/or video player. It differs from the previous playbin (playbin2) +@@ -33,41 +33,22 @@ + * Its behaviour and exposed API is subject to change. + * + * playbin3 can handle both audio and video files and features +- * +- * +- * automatic file type recognition and based on that automatic ++ * ++ * * automatic file type recognition and based on that automatic + * selection and usage of the right audio/video/subtitle demuxers/decoders +- * +- * +- * auxilliary files - such as external subtitles and audio tracks +- * +- * +- * visualisations for audio files +- * +- * +- * subtitle support for video files. Subtitles can be store in external +- * files. +- * +- * +- * stream selection between different video/audio/subtitles streams +- * +- * +- * meta info (tag) extraction +- * +- * +- * easy access to the last video sample +- * +- * +- * buffering when playing streams over a network +- * +- * +- * volume control with mute option +- * +- * + * +- * +- * Usage +- * ++ * * auxilliary files - such as external subtitles and audio tracks ++ * * visualisations for audio files ++ * * subtitle support for video files. Subtitles can be store in external ++ * files. ++ * * stream selection between different video/audio/subtitles streams ++ * * meta info (tag) extraction ++ * * easy access to the last video sample ++ * * buffering when playing streams over a network ++ * * volume control with mute option ++ * ++ * ## Usage ++ * + * A playbin element can be created just like any other element using + * gst_element_factory_make(). The file/URI to play should be set via the #GstPlayBin3:uri + * property. This must be an absolute URI, relative file paths are not allowed. +@@ -103,11 +84,9 @@ + * via gst_element_query_position() and gst_element_query_duration() and + * setting the format passed to GST_FORMAT_TIME. If the query was successful, + * the duration or position will have been returned in units of nanoseconds. +- * +- * +- * +- * Advanced Usage: specifying the audio and video sink +- * ++ * ++ * ## Advanced Usage: specifying the audio and video sink ++ * + * By default, if no audio sink or video sink has been specified via the + * #GstPlayBin3:audio-sink or #GstPlayBin3:video-sink property, playbin3 will use the autoaudiosink + * and autovideosink elements to find the first-best available output method. +@@ -137,21 +116,17 @@ + * It is also possible to 'suppress' audio and/or video output by using + * 'fakesink' elements (or capture it from there using the fakesink element's + * "handoff" signal, which, nota bene, is fired from the streaming thread!). +- * +- * +- * +- * Retrieving Tags and Other Meta Data +- * ++ * ++ * ## Retrieving Tags and Other Meta Data ++ * + * Most of the common meta data (artist, title, etc.) can be retrieved by + * watching for TAG messages on the pipeline's bus (see above). + * + * Other more specific meta information like width/height/framerate of video + * streams or samplerate/number of channels of audio streams can be obtained + * from the negotiated caps on the sink pads of the sinks. +- * +- * +- * +- * Buffering ++ * ++ * ## Buffering + * Playbin3 handles buffering automatically for the most part, but applications + * need to handle parts of the buffering process as well. Whenever playbin3 is + * buffering, it will post BUFFERING messages on the bus with a percentage +@@ -170,65 +145,60 @@ + * ... + * } + * ]| ++ * + * Note that applications should keep/set the pipeline in the PAUSED state when + * a BUFFERING message is received with a buffer percent value < 100 and set + * the pipeline back to PLAYING state when a BUFFERING message with a value + * of 100 percent is received (if PLAYING is the desired state, that is). +- * +- * +- * Embedding the video window in your application ++ * ++ * ## Embedding the video window in your application + * By default, playbin3 (or rather the video sinks used) will create their own + * window. Applications will usually want to force output to a window of their + * own, however. This can be done using the #GstVideoOverlay interface, which most + * video sinks implement. See the documentation there for more details. +- * +- * +- * Specifying which CD/DVD device to use +- * The device to use for CDs/DVDs needs to be set on the source element +- * playbin3 creates before it is opened. The most generic way of doing this +- * is to connect to playbin3's "source-setup" (or "notify::source") signal, +- * which will be emitted by playbin3 when it has created the source element +- * for a particular URI. In the signal callback you can check if the source +- * element has a "device" property and set it appropriately. In some cases +- * the device can also be set as part of the URI, but it depends on the +- * elements involved if this will work or not. For example, for DVD menu +- * playback, the following syntax might work (if the resindvd plugin is used): +- * dvd://[/path/to/device] +- * +- * +- * Handling redirects +- * ++ * ++ * ## Specifying which CD/DVD device to use ++ * ++ * The device to use for CDs/DVDs needs to be set on the source element playbin3 ++ * creates before it is opened. The most generic way of doing this is to connect ++ * to playbin3's "source-setup" signal, which will be emitted by playbin3 when ++ * it has created the source element for a particular URI. In the signal ++ * callback you can check if the source element has a "device" property and set ++ * it appropriately. In some cases the device can also be set as part of the ++ * URI, but it depends on the elements involved if this will work or not. For ++ * example, for DVD menu playback, the following syntax might work (if the ++ * resindvd plugin is used): dvd://[/path/to/device] ++ * ++ * ## Handling redirects ++ * + * Some elements may post 'redirect' messages on the bus to tell the + * application to open another location. These are element messages containing + * a structure named 'redirect' along with a 'new-location' field of string + * type. The new location may be a relative or an absolute URI. Examples + * for such redirects can be found in many quicktime movie trailers. +- * +- * +- * +- * Examples ++ * ++ * ## Examples + * |[ + * gst-launch-1.0 -v playbin3 uri=file:///path/to/somefile.mp4 +- * ]| This will play back the given AVI video file, given that the video and ++ * ]| ++ * This will play back the given AVI video file, given that the video and + * audio decoders required to decode the content are installed. Since no + * special audio sink or video sink is supplied (via playbin3's audio-sink or + * video-sink properties) playbin3 will try to find a suitable audio and + * video sink automatically using the autoaudiosink and autovideosink elements. + * |[ + * gst-launch-1.0 -v playbin3 uri=cdda://4 +- * ]| This will play back track 4 on an audio CD in your disc drive (assuming ++ * ]| ++ * This will play back track 4 on an audio CD in your disc drive (assuming + * the drive is detected automatically by the plugin). + * |[ + * gst-launch-1.0 -v playbin3 uri=dvd:// +- * ]| This will play back the DVD in your disc drive (assuming ++ * ]| ++ * This will play back the DVD in your disc drive (assuming + * the drive is detected automatically by the plugin). +- * ++ * + */ + +-/* FIXME 0.11: suppress warnings for deprecated API such as GValueArray +- * with newer GLib versions (>= 2.31.0) */ +-#define GLIB_DISABLE_DEPRECATION_WARNINGS +- + #ifdef HAVE_CONFIG_H + #include "config.h" + #endif +@@ -268,30 +238,34 @@ typedef struct _GstPlayBin3 GstPlayBin3; + typedef struct _GstPlayBin3Class GstPlayBin3Class; + typedef struct _GstSourceGroup GstSourceGroup; + typedef struct _GstSourceCombine GstSourceCombine; ++typedef struct _SourcePad SourcePad; + + typedef GstCaps *(*SourceCombineGetMediaCapsFunc) (void); + +-/* has the info for a combiner and provides the link to the sink */ ++/* GstSourceCombine controls all the information regarding a certain ++ * media type. ++ * ++ * It can control a custom combiner element (by default none) ++ */ + struct _GstSourceCombine + { + const gchar *media_type; /* the media type for the combiner */ + SourceCombineGetMediaCapsFunc get_media_caps; /* more complex caps for the combiner */ + GstPlaySinkType type; /* the sink pad type of the combiner */ ++ GstStreamType stream_type; /* The GstStreamType of the combiner */ + + GstElement *combiner; /* the combiner */ +- GPtrArray *channels; ++ GPtrArray *channels; /* Array of GstPad ? */ ++ + GstPad *srcpad; /* the source pad of the combiner */ + GstPad *sinkpad; /* the sinkpad of the sink when the combiner +- * is linked +- */ +- gulong block_id; ++ * is linked */ + + GPtrArray *streams; /* Sorted array of GstStream for the given type */ +- gint current_stream; /* Currently selected GstStream */ + + gboolean has_active_pad; /* stream combiner has the "active-pad" property */ + +- gboolean has_always_ok; /* stream combiner's sink pads have the "always-ok" property */ ++ gboolean is_concat; /* The stream combiner is the 'concat' element */ + }; + + #define GST_SOURCE_GROUP_GET_LOCK(group) (&((GstSourceGroup*)(group))->lock) +@@ -311,9 +285,19 @@ static const gchar *stream_type_names[] = { + "audio", "video", "text" + }; + ++ ++#define STREAM_TYPES_FORMAT "s%s%s" ++#define STREAM_TYPES_ARGS(s) (s) & GST_STREAM_TYPE_AUDIO ? "audio " : "", \ ++ (s) & GST_STREAM_TYPE_VIDEO ? "video " : "", \ ++ (s) & GST_STREAM_TYPE_TEXT ? "text " : "" ++ ++ ++ ++#if 0 /* AUTOPLUG DISABLED */ + static void avelements_free (gpointer data); + static GSequence *avelements_create (GstPlayBin3 * playbin, + gboolean isaudioelement); ++#endif + + /* The GstAudioVideoElement structure holding the audio/video decoder + * and the audio/video sink factories together with field indicating +@@ -325,6 +309,14 @@ typedef struct + guint n_comm_cf; /* number of common caps features */ + } GstAVElement; + ++/* a structure to hold information about a uridecodebin pad */ ++struct _SourcePad ++{ ++ GstPad *pad; /* The controlled pad */ ++ GstStreamType stream_type; /* stream type of the controlled pad */ ++ gulong event_probe_id; ++}; ++ + /* a structure to hold the objects for decoding a uri and the subtitle uri + */ + struct _GstSourceGroup +@@ -336,17 +328,30 @@ struct _GstSourceGroup + gboolean valid; /* the group has valid info to start playback */ + gboolean active; /* the group is active */ + ++ gboolean playing; /* the group is currently playing ++ * (outputted on the sinks) */ ++ + /* properties */ + gchar *uri; + gchar *suburi; +- GValueArray *streaminfo; +- GstElement *source; + ++ /* The currently outputted group_id */ ++ guint group_id; ++ ++ /* Bit-wise set of stream types we have requested from uridecodebin3 */ ++ GstStreamType selected_stream_types; ++ ++ /* Bit-wise set of stream types for which pads are present */ ++ GstStreamType present_stream_types; ++ ++ /* TRUE if a 'about-to-finish' needs to be posted once we have ++ * got source pads for all requested stream types ++ * ++ * FIXME : Move this logic to uridecodebin3 later */ ++ gboolean pending_about_to_finish; + +- /* urisourcebins for uri and subtitle uri */ +- /* FIXME: Just make this an array of uris */ +- GstElement *urisourcebin; +- GstElement *suburisourcebin; ++ /* uridecodebin to handle uri and suburi */ ++ GstElement *uridecodebin; + + /* Active sinks for each media type. These are initialized with + * the configured or currently used sink, otherwise +@@ -356,28 +361,28 @@ struct _GstSourceGroup + GstElement *video_sink; + GstElement *text_sink; + +- gint pending; +- gboolean sub_pending; ++ /* List of source pads */ ++ GList *source_pads; ++ ++ /* uridecodebin signals */ ++ gulong pad_added_id; ++ gulong pad_removed_id; ++ gulong select_stream_id; ++ gulong source_setup_id; ++ gulong about_to_finish_id; + +- /* primary uri signals */ +- gulong urisrc_pad_added_id; +- gulong urisrc_pad_removed_id; +- gulong notify_source_id; ++#if 0 /* AUTOPLUG DISABLED */ + gulong autoplug_factories_id; + gulong autoplug_select_id; + gulong autoplug_continue_id; + gulong autoplug_query_id; ++#endif + +- /* subtitle uri signals */ +- gulong sub_pad_added_id; +- gulong sub_pad_removed_id; +- gulong sub_autoplug_continue_id; +- gulong sub_autoplug_query_id; ++ gboolean stream_changed_pending; + +- gulong block_id; ++ /* Active stream collection */ ++ GstStreamCollection *collection; + +- GMutex stream_changed_pending_lock; +- gboolean stream_changed_pending; + + /* buffering message stored for after switching */ + GstMessage *pending_buffering_msg; +@@ -392,20 +397,20 @@ struct _GstSourceGroup + #define GST_PLAY_BIN3_DYN_UNLOCK(bin) g_mutex_unlock (&(bin)->dyn_lock) + + /* lock for shutdown */ +-#define GST_PLAY_BIN3_SHUTDOWN_LOCK(bin,label) \ +-G_STMT_START { \ +- if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) \ +- goto label; \ +- GST_PLAY_BIN3_DYN_LOCK (bin); \ +- if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) { \ +- GST_PLAY_BIN3_DYN_UNLOCK (bin); \ +- goto label; \ +- } \ +-} G_STMT_END ++#define GST_PLAY_BIN3_SHUTDOWN_LOCK(bin,label) \ ++ G_STMT_START { \ ++ if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) \ ++ goto label; \ ++ GST_PLAY_BIN3_DYN_LOCK (bin); \ ++ if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) { \ ++ GST_PLAY_BIN3_DYN_UNLOCK (bin); \ ++ goto label; \ ++ } \ ++ } G_STMT_END + + /* unlock for shutdown */ +-#define GST_PLAY_BIN3_SHUTDOWN_UNLOCK(bin) \ +- GST_PLAY_BIN3_DYN_UNLOCK (bin); \ ++#define GST_PLAY_BIN3_SHUTDOWN_UNLOCK(bin) \ ++ GST_PLAY_BIN3_DYN_UNLOCK (bin); \ + + /** + * GstPlayBin3: +@@ -423,25 +428,20 @@ struct _GstPlayBin3 + GstSourceGroup *curr_group; /* pointer to the currently playing group */ + GstSourceGroup *next_group; /* pointer to the next group */ + +- /* combiners for different streams */ ++ /* Array of GstPad controlled by each combiner */ + GPtrArray *channels[PLAYBIN_STREAM_LAST]; /* links to combiner pads */ ++ ++ /* combiners for different streams */ + GstSourceCombine combiner[PLAYBIN_STREAM_LAST]; + +- /* A global decodebin3 that's used to actually do decoding */ +- gboolean decodebin_active; +- GstElement *decodebin; +- /* Bit-wise set of stream types we have +- * requested from decodebin vs stream types +- * decodebin has provided */ ++ /* Bit-wise set of stream types we have requested from uridecodebin3. ++ * Calculated as the combination of the 'selected_stream_types' of ++ * each sourcegroup */ + GstStreamType selected_stream_types; +- GstStreamType active_stream_types; + +- /* Decodebin signals */ +- gulong db_pad_added_id; +- gulong db_pad_removed_id; +- gulong db_no_more_pads_id; +- gulong db_drained_id; +- gulong db_select_stream_id; ++ /* Bit-wise set of configured output stream types (i.e. active ++ playsink inputs and combiners) */ ++ GstStreamType active_stream_types; + + /* properties */ + guint64 connection_speed; /* connection speed in bits/sec (0 = unknown) */ +@@ -463,8 +463,9 @@ struct _GstPlayBin3 + /* our play sink */ + GstPlaySink *playsink; + +- /* the last activated source */ +- GstElement *source; ++ /* Task for (de)activating groups, protected by the activation lock */ ++ GstTask *activation_task; ++ GRecMutex activation_lock; + + /* lock protecting dynamic adding/removing */ + GMutex dyn_lock; +@@ -501,19 +502,7 @@ struct _GstPlayBin3 + GSequence *aelements; /* a list of GstAVElements for audio stream */ + GSequence *velements; /* a list of GstAVElements for video stream */ + +- struct +- { +- gboolean valid; +- GstFormat format; +- gint64 duration; +- } duration[5]; /* cached durations */ +- + guint64 ring_buffer_max_size; /* 0 means disabled */ +- +- GList *contexts; +- +- /* Active stream collection */ +- GstStreamCollection *collection; + }; + + struct _GstPlayBin3Class +@@ -531,14 +520,12 @@ struct _GstPlayBin3Class + /* props */ + #define DEFAULT_URI NULL + #define DEFAULT_SUBURI NULL +-#define DEFAULT_SOURCE NULL + #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \ + GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_DEINTERLACE | \ +- GST_PLAY_FLAG_SOFT_COLORBALANCE ++ GST_PLAY_FLAG_SOFT_COLORBALANCE | GST_PLAY_FLAG_BUFFERING + #define DEFAULT_CURRENT_VIDEO -1 + #define DEFAULT_CURRENT_AUDIO -1 + #define DEFAULT_CURRENT_TEXT -1 +-#define DEFAULT_AUTO_SELECT_STREAMS TRUE + #define DEFAULT_SUBTITLE_ENCODING NULL + #define DEFAULT_AUDIO_SINK NULL + #define DEFAULT_VIDEO_SINK NULL +@@ -560,7 +547,6 @@ enum + PROP_CURRENT_URI, + PROP_SUBURI, + PROP_CURRENT_SUBURI, +- PROP_SOURCE, + PROP_FLAGS, + PROP_SUBTITLE_ENCODING, + PROP_AUDIO_SINK, +@@ -596,8 +582,10 @@ enum + LAST_SIGNAL + }; + ++#if 0 /* AUTOPLUG DISABLED */ + static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw(ANY)"); + static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw(ANY)"); ++#endif + + static void gst_play_bin3_class_init (GstPlayBin3Class * klass); + static void gst_play_bin3_init (GstPlayBin3 * playbin); +@@ -614,37 +602,35 @@ static GstStateChangeReturn gst_play_bin3_change_state (GstElement * element, + static void gst_play_bin3_handle_message (GstBin * bin, GstMessage * message); + static void gst_play_bin3_deep_element_added (GstBin * playbin, + GstBin * sub_bin, GstElement * child); +-static gboolean gst_play_bin3_query (GstElement * element, GstQuery * query); +-static void gst_play_bin3_set_context (GstElement * element, +- GstContext * context); + static gboolean gst_play_bin3_send_event (GstElement * element, + GstEvent * event); + + static GstSample *gst_play_bin3_convert_sample (GstPlayBin3 * playbin, + GstCaps * caps); + +-static GstStateChangeReturn setup_next_source (GstPlayBin3 * playbin, +- GstState target); ++static GstStateChangeReturn setup_next_source (GstPlayBin3 * playbin); + +-static void no_more_pads_cb (GstElement * decodebin, GstPlayBin3 * playbin); ++static void gst_play_bin3_check_group_status (GstPlayBin3 * playbin); ++static void emit_about_to_finish (GstPlayBin3 * playbin); ++static void reconfigure_output (GstPlayBin3 * playbin); + static void pad_removed_cb (GstElement * decodebin, GstPad * pad, +- GstPlayBin3 * playbin); ++ GstSourceGroup * group); + + static gint select_stream_cb (GstElement * decodebin, + GstStreamCollection * collection, GstStream * stream, +- GstPlayBin3 * playbin); ++ GstSourceGroup * group); + +-static void do_stream_selection (GstPlayBin3 * playbin); ++static void do_stream_selection (GstPlayBin3 * playbin, GstSourceGroup * group); + + static GstElementClass *parent_class; + + static guint gst_play_bin3_signals[LAST_SIGNAL] = { 0 }; + +-#define REMOVE_SIGNAL(obj,id) \ +-if (id) { \ +- g_signal_handler_disconnect (obj, id); \ +- id = 0; \ +-} ++#define REMOVE_SIGNAL(obj,id) \ ++ if (id) { \ ++ g_signal_handler_disconnect (obj, id); \ ++ id = 0; \ ++ } + + static void gst_play_bin3_overlay_init (gpointer g_iface, + gpointer g_iface_data); +@@ -731,7 +717,7 @@ gst_play_bin3_class_init (GstPlayBin3Class * klass) + g_param_spec_string ("uri", "URI", "URI of the media to play", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + +- /** ++ /** + * GstPlayBin3:current-uri + * + * The currently playing uri. +@@ -761,10 +747,6 @@ gst_play_bin3_class_init (GstPlayBin3Class * klass) + "The currently playing URI of a subtitle", + NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + +- g_object_class_install_property (gobject_klass, PROP_SOURCE, +- g_param_spec_object ("source", "Source", "Source element", +- GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); +- + /** + * GstPlayBin3:flags + * +@@ -826,32 +808,34 @@ gst_play_bin3_class_init (GstPlayBin3Class * klass) + /** + * GstPlayBin3:video-stream-combiner + * +- * Get or set the current video stream combiner. By default, an input-selector +- * is created and deleted as-needed. ++ * Get or set the current video stream combiner. By default, no ++ * element is used and the selected stream is used directly. + */ + g_object_class_install_property (gobject_klass, PROP_VIDEO_STREAM_COMBINER, + g_param_spec_object ("video-stream-combiner", "Video stream combiner", +- "Current video stream combiner (NULL = input-selector)", ++ "Current video stream combiner (default: none)", + GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ + /** + * GstPlayBin3:audio-stream-combiner + * +- * Get or set the current audio stream combiner. By default, an input-selector +- * is created and deleted as-needed. ++ * Get or set the current audio stream combiner. By default, no ++ * element is used and the selected stream is used directly. + */ + g_object_class_install_property (gobject_klass, PROP_AUDIO_STREAM_COMBINER, + g_param_spec_object ("audio-stream-combiner", "Audio stream combiner", +- "Current audio stream combiner (NULL = input-selector)", ++ "Current audio stream combiner (default: none))", + GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ + /** + * GstPlayBin3:text-stream-combiner + * +- * Get or set the current text stream combiner. By default, an input-selector +- * is created and deleted as-needed. ++ * Get or set the current text stream combiner. By default, no ++ * element is used and the selected stream is used directly. + */ + g_object_class_install_property (gobject_klass, PROP_TEXT_STREAM_COMBINER, + g_param_spec_object ("text-stream-combiner", "Text stream combiner", +- "Current text stream combiner (NULL = input-selector)", ++ "Current text stream combiner (default: none)", + GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** +@@ -1040,7 +1024,7 @@ gst_play_bin3_class_init (GstPlayBin3Class * klass) + * Action signal to retrieve the currently playing video frame in the format + * specified by @caps. + * If @caps is %NULL, no conversion will be performed and this function is +- * equivalent to the #GstPlayBin3::frame property. ++ * equivalent to the #GstPlayBin3::sample property. + * + * Returns: a #GstSample of the current video frame converted to #caps. + * The caps on the sample will describe the final layout of the buffer data. +@@ -1062,8 +1046,6 @@ gst_play_bin3_class_init (GstPlayBin3Class * klass) + + gstelement_klass->change_state = + GST_DEBUG_FUNCPTR (gst_play_bin3_change_state); +- gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_play_bin3_query); +- gstelement_klass->set_context = GST_DEBUG_FUNCPTR (gst_play_bin3_set_context); + gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_bin3_send_event); + + gstbin_klass->handle_message = +@@ -1114,35 +1096,38 @@ init_combiners (GstPlayBin3 * playbin) + + playbin->combiner[PLAYBIN_STREAM_AUDIO].media_type = "audio"; + playbin->combiner[PLAYBIN_STREAM_AUDIO].type = GST_PLAY_SINK_TYPE_AUDIO; ++ playbin->combiner[PLAYBIN_STREAM_AUDIO].stream_type = GST_STREAM_TYPE_AUDIO; + playbin->combiner[PLAYBIN_STREAM_AUDIO].channels = playbin->channels[0]; + playbin->combiner[PLAYBIN_STREAM_AUDIO].streams = + g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); +- playbin->combiner[PLAYBIN_STREAM_AUDIO].current_stream = -1; + + playbin->combiner[PLAYBIN_STREAM_VIDEO].media_type = "video"; + playbin->combiner[PLAYBIN_STREAM_VIDEO].type = GST_PLAY_SINK_TYPE_VIDEO; ++ playbin->combiner[PLAYBIN_STREAM_VIDEO].stream_type = GST_STREAM_TYPE_VIDEO; + playbin->combiner[PLAYBIN_STREAM_VIDEO].channels = playbin->channels[1]; + playbin->combiner[PLAYBIN_STREAM_VIDEO].streams = + g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); +- playbin->combiner[PLAYBIN_STREAM_VIDEO].current_stream = -1; + + playbin->combiner[PLAYBIN_STREAM_TEXT].media_type = "text"; + playbin->combiner[PLAYBIN_STREAM_TEXT].get_media_caps = + gst_subtitle_overlay_create_factory_caps; + playbin->combiner[PLAYBIN_STREAM_TEXT].type = GST_PLAY_SINK_TYPE_TEXT; ++ playbin->combiner[PLAYBIN_STREAM_TEXT].stream_type = GST_STREAM_TYPE_TEXT; + playbin->combiner[PLAYBIN_STREAM_TEXT].channels = playbin->channels[2]; + playbin->combiner[PLAYBIN_STREAM_TEXT].streams = + g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); +- playbin->combiner[PLAYBIN_STREAM_TEXT].current_stream = -1; + } + +-/* Update the combiner information to be in sync with the current collection */ ++/* Update the combiner information to be in sync with the current collection ++ * ++ * FIXME : "current" collection doesn't mean anything until we have a "combined" ++ * collection of all groups */ + static void +-update_combiner_info (GstPlayBin3 * playbin) ++update_combiner_info (GstPlayBin3 * playbin, GstStreamCollection * collection) + { + guint i, len; + +- if (playbin->collection == NULL) ++ if (collection == NULL) + return; + + GST_DEBUG_OBJECT (playbin, "Updating combiner info"); +@@ -1153,18 +1138,14 @@ update_combiner_info (GstPlayBin3 * playbin) + g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_TEXT].streams, TRUE); + playbin->combiner[PLAYBIN_STREAM_AUDIO].streams = + g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); +- playbin->combiner[PLAYBIN_STREAM_AUDIO].current_stream = -1; + playbin->combiner[PLAYBIN_STREAM_VIDEO].streams = + g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); +- playbin->combiner[PLAYBIN_STREAM_VIDEO].current_stream = -1; + playbin->combiner[PLAYBIN_STREAM_TEXT].streams = + g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); +- playbin->combiner[PLAYBIN_STREAM_TEXT].current_stream = -1; + +- len = gst_stream_collection_get_size (playbin->collection); ++ len = gst_stream_collection_get_size (collection); + for (i = 0; i < len; i++) { +- GstStream *stream = +- gst_stream_collection_get_stream (playbin->collection, i); ++ GstStream *stream = gst_stream_collection_get_stream (collection, i); + GstStreamType stype = gst_stream_get_stream_type (stream); + + if (stype & GST_STREAM_TYPE_AUDIO) { +@@ -1187,40 +1168,24 @@ update_combiner_info (GstPlayBin3 * playbin) + playbin->combiner[PLAYBIN_STREAM_TEXT].streams->len); + } + +-/* Set the given stream as the selected stream */ +-static void +-set_selected_stream (GstPlayBin3 * playbin, GstStream * stream) +-{ +- GstSourceCombine *combine = NULL; +- GstStreamType stype = gst_stream_get_stream_type (stream); +- +- if (stype & GST_STREAM_TYPE_AUDIO) +- combine = &playbin->combiner[PLAYBIN_STREAM_AUDIO]; +- else if (stype & GST_STREAM_TYPE_VIDEO) +- combine = &playbin->combiner[PLAYBIN_STREAM_VIDEO]; +- else if (stype & GST_STREAM_TYPE_TEXT) +- combine = &playbin->combiner[PLAYBIN_STREAM_TEXT]; +- +- if (combine) { +- if (combine->combiner == NULL) { +- guint i, len; +- +- GST_DEBUG_OBJECT (playbin, "Called for %s (%p)", +- gst_stream_get_stream_id (stream), combine->combiner); +- +- combine->current_stream = -1; +- len = combine->streams->len; +- for (i = 0; i < len; i++) { +- GstStream *cand = g_ptr_array_index (combine->streams, i); +- if (cand == stream) { +- GST_DEBUG_OBJECT (playbin, "Setting current to %d", i); +- combine->current_stream = i; +- break; +- } +- } +- } +- } +-} ++#ifndef GST_DISABLE_GST_DEBUG ++#define debug_groups(playbin) G_STMT_START { \ ++ guint i; \ ++ \ ++ for (i = 0; i < 2; i++) { \ ++ GstSourceGroup *group = &playbin->groups[i]; \ ++ \ ++ GST_DEBUG ("GstSourceGroup #%d (%s)", i, (group == playbin->curr_group) ? "current" : (group == playbin->next_group) ? "next" : "unused"); \ ++ GST_DEBUG (" valid:%d , active:%d , playing:%d", group->valid, group->active, group->playing); \ ++ GST_DEBUG (" uri:%s", group->uri); \ ++ GST_DEBUG (" suburi:%s", group->suburi); \ ++ GST_DEBUG (" group_id:%d", group->group_id); \ ++ GST_DEBUG (" pending_about_to_finish:%d", group->pending_about_to_finish); \ ++ } \ ++ } G_STMT_END ++#else ++#define debug_groups(p) {} ++#endif + + static void + init_group (GstPlayBin3 * playbin, GstSourceGroup * group) +@@ -1228,7 +1193,7 @@ init_group (GstPlayBin3 * playbin, GstSourceGroup * group) + g_mutex_init (&group->lock); + + group->stream_changed_pending = FALSE; +- g_mutex_init (&group->stream_changed_pending_lock); ++ group->group_id = GST_GROUP_ID_INVALID; + + group->playbin = playbin; + } +@@ -1241,12 +1206,13 @@ free_group (GstPlayBin3 * playbin, GstSourceGroup * group) + + g_mutex_clear (&group->lock); + group->stream_changed_pending = FALSE; +- g_mutex_clear (&group->stream_changed_pending_lock); + + if (group->pending_buffering_msg) + gst_message_unref (group->pending_buffering_msg); + group->pending_buffering_msg = NULL; + ++ gst_object_replace ((GstObject **) & group->collection, NULL); ++ + gst_object_replace ((GstObject **) & group->audio_sink, NULL); + gst_object_replace ((GstObject **) & group->video_sink, NULL); + gst_object_replace ((GstObject **) & group->text_sink, NULL); +@@ -1271,6 +1237,7 @@ colorbalance_value_changed_cb (GstColorBalance * balance, + gst_color_balance_value_changed (GST_COLOR_BALANCE (playbin), channel, value); + } + ++#if 0 /* AUTOPLUG DISABLED */ + static gint + compare_factories_func (gconstpointer p1, gconstpointer p2) + { +@@ -1345,6 +1312,7 @@ gst_play_bin3_update_elements_list (GstPlayBin3 * playbin) + + playbin->elements_cookie = cookie; + } ++#endif + + static void + gst_play_bin3_init (GstPlayBin3 * playbin) +@@ -1366,6 +1334,8 @@ gst_play_bin3_init (GstPlayBin3 * playbin) + /* first filter out the interesting element factories */ + g_mutex_init (&playbin->elements_lock); + ++ g_rec_mutex_init (&playbin->activation_lock); ++ + /* add sink */ + playbin->playsink = + g_object_new (GST_TYPE_PLAY_SINK, "name", "playsink", "send-event-mode", +@@ -1408,9 +1378,6 @@ gst_play_bin3_finalize (GObject * object) + for (i = 0; i < PLAYBIN_STREAM_LAST; i++) + g_ptr_array_free (playbin->channels[i], TRUE); + +- if (playbin->source) +- gst_object_unref (playbin->source); +- + /* Setting states to NULL is safe here because playsink + * will already be gone and none of these sinks will be + * a child of playsink +@@ -1445,9 +1412,6 @@ gst_play_bin3_finalize (GObject * object) + g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_VIDEO].streams, TRUE); + g_ptr_array_free (playbin->combiner[PLAYBIN_STREAM_TEXT].streams, TRUE); + +- if (playbin->decodebin) +- gst_object_unref (playbin->decodebin); +- + if (playbin->elements) + gst_plugin_feature_list_free (playbin->elements); + +@@ -1457,11 +1421,7 @@ gst_play_bin3_finalize (GObject * object) + if (playbin->velements) + g_sequence_free (playbin->velements); + +- if (playbin->collection) +- gst_object_unref (playbin->collection); +- +- g_list_free_full (playbin->contexts, (GDestroyNotify) gst_context_unref); +- ++ g_rec_mutex_clear (&playbin->activation_lock); + g_rec_mutex_clear (&playbin->lock); + g_mutex_clear (&playbin->dyn_lock); + g_mutex_clear (&playbin->elements_lock); +@@ -1592,43 +1552,6 @@ gst_play_bin3_convert_sample (GstPlayBin3 * playbin, GstCaps * caps) + return gst_play_sink_convert_sample (playbin->playsink, caps); + } + +-/* Returns current stream number, or -1 if none has been selected yet */ +-static int +-get_current_stream_number (GstPlayBin3 * playbin, GstSourceCombine * combine, +- GPtrArray * channels) +-{ +- /* Internal API cleanup would make this easier... */ +- int i; +- GstPad *pad, *current; +- GstObject *combiner = NULL; +- int ret = -1; +- +- if (!combine->has_active_pad) { +- GST_WARNING_OBJECT (playbin, +- "combiner doesn't have the \"active-pad\" property"); +- return ret; +- } +- +- for (i = 0; i < channels->len; i++) { +- pad = g_ptr_array_index (channels, i); +- if ((combiner = gst_pad_get_parent (pad))) { +- g_object_get (combiner, "active-pad", ¤t, NULL); +- gst_object_unref (combiner); +- +- if (pad == current) { +- gst_object_unref (current); +- ret = i; +- break; +- } +- +- if (current) +- gst_object_unref (current); +- } +- } +- +- return ret; +-} +- + static gboolean + gst_play_bin3_send_custom_event (GstObject * combiner, const gchar * event_name) + { +@@ -1672,10 +1595,10 @@ gst_play_bin3_set_current_stream (GstPlayBin3 * playbin, + GST_DEBUG_OBJECT (playbin, "Changing current %s stream %d -> %d", + stream_type_names[stream_type], *current_value, stream); + +- if (combine->combiner == NULL) { ++ if (combine->combiner == NULL || combine->is_concat) { + /* FIXME: Check that the current_value is within range */ + *current_value = stream; +- do_stream_selection (playbin); ++ do_stream_selection (playbin, playbin->curr_group); + GST_PLAY_BIN3_UNLOCK (playbin); + return TRUE; + } +@@ -1765,35 +1688,6 @@ gst_play_bin3_set_current_text_stream (GstPlayBin3 * playbin, gint stream) + &playbin->current_text, stream, &playbin->text_pending_flush_finish); + } + +-static void +-source_combine_remove_pads (GstPlayBin3 * playbin, GstSourceCombine * combine) +-{ +- if (combine->sinkpad) { +- GST_LOG_OBJECT (playbin, "unlinking from sink"); +- gst_pad_unlink (combine->srcpad, combine->sinkpad); +- +- /* release back */ +- GST_LOG_OBJECT (playbin, "release sink pad"); +- gst_play_sink_release_pad (playbin->playsink, combine->sinkpad); +- gst_object_unref (combine->sinkpad); +- combine->sinkpad = NULL; +- } +- gst_object_unref (combine->srcpad); +- combine->srcpad = NULL; +-} +- +-static GstPadProbeReturn +-block_serialized_data_cb (GstPad * pad, GstPadProbeInfo * info, +- gpointer user_data) +-{ +- if (GST_IS_EVENT (info->data) && !GST_EVENT_IS_SERIALIZED (info->data)) { +- GST_DEBUG_OBJECT (pad, "Letting non-serialized event %s pass", +- GST_EVENT_TYPE_NAME (info->data)); +- return GST_PAD_PROBE_PASS; +- } +- +- return GST_PAD_PROBE_OK; +-} + + static void + gst_play_bin3_set_sink (GstPlayBin3 * playbin, GstPlaySinkType type, +@@ -1835,14 +1729,7 @@ gst_play_bin3_set_stream_combiner (GstPlayBin3 * playbin, GstElement ** elem, + static void + gst_play_bin3_set_encoding (GstPlayBin3 * playbin, const gchar * encoding) + { +- GstElement *elem; +- + GST_PLAY_BIN3_LOCK (playbin); +- +- /* set subtitles on decodebin. */ +- if ((elem = playbin->decodebin)) +- g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL); +- + gst_play_sink_set_subtitle_encoding (playbin->playsink, encoding); + GST_PLAY_BIN3_UNLOCK (playbin); + } +@@ -1864,8 +1751,8 @@ gst_play_bin3_set_property (GObject * object, guint prop_id, + gst_play_bin3_set_flags (playbin, g_value_get_flags (value)); + if (playbin->curr_group) { + GST_SOURCE_GROUP_LOCK (playbin->curr_group); +- if (playbin->curr_group->urisourcebin) { +- g_object_set (playbin->curr_group->urisourcebin, "download", ++ if (playbin->curr_group->uridecodebin) { ++ g_object_set (playbin->curr_group->uridecodebin, "download", + (g_value_get_flags (value) & GST_PLAY_FLAG_DOWNLOAD) != 0, NULL); + } + GST_SOURCE_GROUP_UNLOCK (playbin->curr_group); +@@ -1939,8 +1826,8 @@ gst_play_bin3_set_property (GObject * object, guint prop_id, + playbin->ring_buffer_max_size = g_value_get_uint64 (value); + if (playbin->curr_group) { + GST_SOURCE_GROUP_LOCK (playbin->curr_group); +- if (playbin->curr_group->urisourcebin) { +- g_object_set (playbin->curr_group->urisourcebin, ++ if (playbin->curr_group->uridecodebin) { ++ g_object_set (playbin->curr_group->uridecodebin, + "ring-buffer-max-size", playbin->ring_buffer_max_size, NULL); + } + GST_SOURCE_GROUP_UNLOCK (playbin->curr_group); +@@ -1993,7 +1880,10 @@ gst_play_bin3_get_current_stream_combiner (GstPlayBin3 * playbin, + GstElement *combiner; + + GST_PLAY_BIN3_LOCK (playbin); +- if ((combiner = playbin->combiner[stream_type].combiner)) ++ /* The special concat element should never be returned */ ++ if (playbin->combiner[stream_type].is_concat) ++ combiner = NULL; ++ else if ((combiner = playbin->combiner[stream_type].combiner)) + gst_object_ref (combiner); + else if ((combiner = *elem)) + gst_object_ref (combiner); +@@ -2049,13 +1939,6 @@ gst_play_bin3_get_property (GObject * object, guint prop_id, GValue * value, + GST_PLAY_BIN3_UNLOCK (playbin); + break; + } +- case PROP_SOURCE: +- { +- GST_OBJECT_LOCK (playbin); +- g_value_set_object (value, playbin->source); +- GST_OBJECT_UNLOCK (playbin); +- break; +- } + case PROP_FLAGS: + g_value_set_flags (value, gst_play_bin3_get_flags (playbin)); + break; +@@ -2168,108 +2051,6 @@ gst_play_bin3_get_property (GObject * object, guint prop_id, GValue * value, + } + } + +-static void +-gst_play_bin3_update_cached_duration_from_query (GstPlayBin3 * playbin, +- gboolean valid, GstQuery * query) +-{ +- GstFormat fmt; +- gint64 duration; +- gint i; +- +- GST_DEBUG_OBJECT (playbin, "Updating cached duration from query"); +- gst_query_parse_duration (query, &fmt, &duration); +- +- for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) { +- if (playbin->duration[i].format == 0 || fmt == playbin->duration[i].format) { +- playbin->duration[i].valid = valid; +- playbin->duration[i].format = fmt; +- playbin->duration[i].duration = valid ? duration : -1; +- break; +- } +- } +-} +- +-static void +-gst_play_bin3_update_cached_duration (GstPlayBin3 * playbin) +-{ +- const GstFormat formats[] = +- { GST_FORMAT_TIME, GST_FORMAT_BYTES, GST_FORMAT_DEFAULT }; +- gboolean ret; +- GstQuery *query; +- gint i; +- +- GST_DEBUG_OBJECT (playbin, "Updating cached durations before group switch"); +- for (i = 0; i < G_N_ELEMENTS (formats); i++) { +- query = gst_query_new_duration (formats[i]); +- ret = +- GST_ELEMENT_CLASS (parent_class)->query (GST_ELEMENT_CAST (playbin), +- query); +- gst_play_bin3_update_cached_duration_from_query (playbin, ret, query); +- gst_query_unref (query); +- } +-} +- +-static gboolean +-gst_play_bin3_query (GstElement * element, GstQuery * query) +-{ +- GstPlayBin3 *playbin = GST_PLAY_BIN3 (element); +- gboolean ret; +- +- /* During a group switch we shouldn't allow duration queries +- * because it's not clear if the old or new group's duration +- * is returned and if the sinks are already playing new data +- * or old data. See bug #585969 +- * +- * While we're at it, also don't do any other queries during +- * a group switch or any other event that causes topology changes +- * by taking the playbin lock in any case. +- */ +- GST_PLAY_BIN3_LOCK (playbin); +- +- if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION) { +- GstSourceGroup *group = playbin->curr_group; +- gboolean pending; +- +- GST_SOURCE_GROUP_LOCK (group); +- +- pending = group->pending || group->stream_changed_pending; +- +- if (pending) { +- GstFormat fmt; +- gint i; +- +- ret = FALSE; +- gst_query_parse_duration (query, &fmt, NULL); +- for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) { +- if (fmt == playbin->duration[i].format) { +- ret = playbin->duration[i].valid; +- gst_query_set_duration (query, fmt, +- (ret ? playbin->duration[i].duration : -1)); +- break; +- } +- } +- /* if nothing cached yet, we might as well request duration, +- * such as during initial startup */ +- if (ret) { +- GST_DEBUG_OBJECT (playbin, +- "Taking cached duration because of pending group switch: %d", ret); +- GST_SOURCE_GROUP_UNLOCK (group); +- GST_PLAY_BIN3_UNLOCK (playbin); +- return ret; +- } +- } +- GST_SOURCE_GROUP_UNLOCK (group); +- } +- +- ret = GST_ELEMENT_CLASS (parent_class)->query (element, query); +- +- if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION) +- gst_play_bin3_update_cached_duration_from_query (playbin, ret, query); +- GST_PLAY_BIN3_UNLOCK (playbin); +- +- return ret; +-} +- + static gint + get_combiner_stream_id (GstPlayBin3 * playbin, GstSourceCombine * combine, + GList * full_list) +@@ -2277,7 +2058,7 @@ get_combiner_stream_id (GstPlayBin3 * playbin, GstSourceCombine * combine, + gint i; + GList *tmp; + +- for (i = 0; combine->streams->len; i++) { ++ for (i = 0; i < combine->streams->len; i++) { + GstStream *stream = (GstStream *) g_ptr_array_index (combine->streams, i); + const gchar *sid = gst_stream_get_stream_id (stream); + for (tmp = full_list; tmp; tmp = tmp->next) { +@@ -2293,17 +2074,16 @@ get_combiner_stream_id (GstPlayBin3 * playbin, GstSourceCombine * combine, + + static GList * + extend_list_of_streams (GstPlayBin3 * playbin, GstStreamType stype, +- GList * list) ++ GList * list, GstStreamCollection * collection) + { + GList *tmp, *res; + gint i, nb; + + res = list; + +- nb = gst_stream_collection_get_size (playbin->collection); ++ nb = gst_stream_collection_get_size (collection); + for (i = 0; i < nb; i++) { +- GstStream *stream = +- gst_stream_collection_get_stream (playbin->collection, i); ++ GstStream *stream = gst_stream_collection_get_stream (collection, i); + GstStreamType curtype = gst_stream_get_stream_type (stream); + if (stype == curtype) { + gboolean already_there = FALSE; +@@ -2326,7 +2106,8 @@ extend_list_of_streams (GstPlayBin3 * playbin, GstStreamType stype, + } + + static GstEvent * +-update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event) ++update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event, ++ GstSourceGroup * group) + { + GList *streams = NULL; + GList *to_use; +@@ -2340,13 +2121,21 @@ update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event) + return event; + } + ++ if (!group->collection) { ++ GST_DEBUG_OBJECT (playbin, ++ "No stream collection for group, no need to modify SELECT_STREAMS event"); ++ return event; ++ } ++ + gst_event_parse_select_streams (event, &streams); + to_use = g_list_copy_deep (streams, (GCopyFunc) g_strdup, NULL); + + /* For each combiner, we want to add all streams of that type to the + * selection */ + if (playbin->audio_stream_combiner) { +- to_use = extend_list_of_streams (playbin, GST_STREAM_TYPE_AUDIO, to_use); ++ to_use = ++ extend_list_of_streams (playbin, GST_STREAM_TYPE_AUDIO, to_use, ++ group->collection); + combine_id = + get_combiner_stream_id (playbin, + &playbin->combiner[PLAYBIN_STREAM_AUDIO], streams); +@@ -2354,7 +2143,9 @@ update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event) + gst_play_bin3_set_current_audio_stream (playbin, combine_id); + } + if (playbin->video_stream_combiner) { +- to_use = extend_list_of_streams (playbin, GST_STREAM_TYPE_VIDEO, to_use); ++ to_use = ++ extend_list_of_streams (playbin, GST_STREAM_TYPE_VIDEO, to_use, ++ group->collection); + combine_id = + get_combiner_stream_id (playbin, + &playbin->combiner[PLAYBIN_STREAM_VIDEO], streams); +@@ -2362,7 +2153,9 @@ update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event) + gst_play_bin3_set_current_video_stream (playbin, combine_id); + } + if (playbin->text_stream_combiner) { +- to_use = extend_list_of_streams (playbin, GST_STREAM_TYPE_TEXT, to_use); ++ to_use = ++ extend_list_of_streams (playbin, GST_STREAM_TYPE_TEXT, to_use, ++ group->collection); + combine_id = + get_combiner_stream_id (playbin, + &playbin->combiner[PLAYBIN_STREAM_TEXT], streams); +@@ -2371,7 +2164,90 @@ update_select_streams_event (GstPlayBin3 * playbin, GstEvent * event) + } + + gst_event_unref (event); +- return gst_event_new_select_streams (to_use); ++ event = gst_event_new_select_streams (to_use); ++ ++ if (streams) ++ g_list_free_full (streams, g_free); ++ if (to_use) ++ g_list_free_full (to_use, g_free); ++ ++ return event; ++} ++ ++/* Returns TRUE if the given list of streams belongs to the stream collection */ ++static gboolean ++gst_streams_belong_to_collection (GList * streams, ++ GstStreamCollection * collection) ++{ ++ GList *tmp; ++ guint i, nb; ++ ++ if (streams == NULL || collection == NULL) ++ return FALSE; ++ nb = gst_stream_collection_get_size (collection); ++ if (nb == 0) ++ return FALSE; ++ ++ for (tmp = streams; tmp; tmp = tmp->next) { ++ const gchar *cand = (const gchar *) tmp->data; ++ gboolean found = FALSE; ++ ++ for (i = 0; i < nb; i++) { ++ GstStream *stream = gst_stream_collection_get_stream (collection, i); ++ if (!g_strcmp0 (cand, gst_stream_get_stream_id (stream))) { ++ found = TRUE; ++ break; ++ } ++ } ++ if (!found) ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++static GstSourceGroup * ++get_source_group_for_streams (GstPlayBin3 * playbin, GstEvent * event) ++{ ++ GList *streams; ++ GstSourceGroup *res = NULL; ++ ++ gst_event_parse_select_streams (event, &streams); ++ if (playbin->curr_group->collection && ++ gst_streams_belong_to_collection (streams, ++ playbin->curr_group->collection)) ++ res = playbin->curr_group; ++ else if (playbin->next_group->collection && ++ gst_streams_belong_to_collection (streams, ++ playbin->next_group->collection)) ++ res = playbin->next_group; ++ g_list_free_full (streams, g_free); ++ ++ return res; ++} ++ ++static GstStreamType ++get_stream_type_for_event (GstStreamCollection * collection, GstEvent * event) ++{ ++ GList *stream_list = NULL; ++ GList *tmp; ++ GstStreamType res = 0; ++ guint i, len; ++ ++ gst_event_parse_select_streams (event, &stream_list); ++ len = gst_stream_collection_get_size (collection); ++ for (tmp = stream_list; tmp; tmp = tmp->next) { ++ gchar *stid = (gchar *) tmp->data; ++ ++ for (i = 0; i < len; i++) { ++ GstStream *stream = gst_stream_collection_get_stream (collection, i); ++ if (!g_strcmp0 (stid, gst_stream_get_stream_id (stream))) { ++ res |= gst_stream_get_stream_type (stream); ++ } ++ } ++ } ++ g_list_free_full (stream_list, g_free); ++ ++ return res; + } + + static gboolean +@@ -2381,6 +2257,7 @@ gst_play_bin3_send_event (GstElement * element, GstEvent * event) + + if (GST_EVENT_TYPE (event) == GST_EVENT_SELECT_STREAMS) { + gboolean res; ++ GstSourceGroup *group; + + GST_PLAY_BIN3_LOCK (playbin); + GST_LOG_OBJECT (playbin, +@@ -2388,14 +2265,32 @@ gst_play_bin3_send_event (GstElement * element, GstEvent * event) + /* This is probably already false, but it doesn't hurt to be sure */ + playbin->do_stream_selections = FALSE; + ++ group = get_source_group_for_streams (playbin, event); ++ if (group == NULL) { ++ GST_WARNING_OBJECT (playbin, ++ "Can't figure out to which uridecodebin the select-streams event should be sent to"); ++ GST_PLAY_BIN3_UNLOCK (playbin); ++ return FALSE; ++ } ++ + /* If we have custom combiners, we need to extend the selection with + * the list of all streams for that given type since we will be handling + * the selection with that combiner */ +- event = update_select_streams_event (playbin, event); ++ event = update_select_streams_event (playbin, event, group); ++ ++ if (group->collection) { ++ group->selected_stream_types = ++ get_stream_type_for_event (group->collection, event); ++ playbin->selected_stream_types = ++ playbin->groups[0].selected_stream_types | playbin-> ++ groups[1].selected_stream_types; ++ if (playbin->active_stream_types != playbin->selected_stream_types) ++ reconfigure_output (playbin); ++ } + +- /* Send this event directly to decodebin, so it works even +- * if decodebin didn't add any pads yet */ +- res = gst_element_send_event (playbin->decodebin, event); ++ /* Send this event directly to uridecodebin, so it works even ++ * if uridecodebin didn't add any pads yet */ ++ res = gst_element_send_event (group->uridecodebin, event); + GST_PLAY_BIN3_UNLOCK (playbin); + + return res; +@@ -2419,7 +2314,7 @@ gst_play_bin3_send_event (GstElement * element, GstEvent * event) + + /* Called with playbin lock held */ + static void +-do_stream_selection (GstPlayBin3 * playbin) ++do_stream_selection (GstPlayBin3 * playbin, GstSourceGroup * group) + { + GstStreamCollection *collection; + guint i, nb_streams; +@@ -2427,7 +2322,10 @@ do_stream_selection (GstPlayBin3 * playbin) + gint nb_video = 0, nb_audio = 0, nb_text = 0; + GstStreamType chosen_stream_types = 0; + +- collection = playbin->collection; ++ if (group == NULL) ++ return; ++ ++ collection = group->collection; + if (collection == NULL) { + GST_LOG_OBJECT (playbin, "No stream collection. Not doing stream-select"); + return; +@@ -2438,6 +2336,9 @@ do_stream_selection (GstPlayBin3 * playbin) + GST_INFO_OBJECT (playbin, "Empty collection received! Ignoring"); + } + ++ GST_DEBUG_OBJECT (playbin, "Doing selection on collection with %d streams", ++ nb_streams); ++ + /* Iterate the collection and choose the streams that match + * either the current-* setting, or all streams of a type if there's + * a combiner for that type */ +@@ -2448,6 +2349,8 @@ do_stream_selection (GstPlayBin3 * playbin) + gint pb_stream_type = -1; + gboolean select_this = FALSE; + ++ GST_LOG_OBJECT (playbin, "Looking at stream #%d : %s", i, stream_id); ++ + if (stream_type & GST_STREAM_TYPE_AUDIO) { + pb_stream_type = PLAYBIN_STREAM_AUDIO; + /* Select the stream if it's the current one or if there's a custom selector */ +@@ -2457,7 +2360,7 @@ do_stream_selection (GstPlayBin3 * playbin) + playbin->audio_stream_combiner != NULL); + nb_audio++; + } else if (stream_type & GST_STREAM_TYPE_VIDEO) { +- pb_stream_type = PLAYBIN_STREAM_AUDIO; ++ pb_stream_type = PLAYBIN_STREAM_VIDEO; + select_this = + (nb_video == playbin->current_video || + (playbin->current_video == -1 && nb_video == 0) || +@@ -2488,70 +2391,106 @@ do_stream_selection (GstPlayBin3 * playbin) + } + + if (streams) { +- GstEvent *ev = gst_event_new_select_streams (streams); +- gst_element_send_event (playbin->decodebin, ev); ++ if (group->uridecodebin) { ++ GstEvent *ev = gst_event_new_select_streams (streams); ++ gst_element_send_event (group->uridecodebin, ev); ++ } + g_list_free (streams); + } +- playbin->selected_stream_types = chosen_stream_types; ++ ++ group->selected_stream_types = chosen_stream_types; ++ /* Update global selected_stream_types */ ++ playbin->selected_stream_types = ++ playbin->groups[0].selected_stream_types | playbin-> ++ groups[1].selected_stream_types; ++ if (playbin->active_stream_types != playbin->selected_stream_types) ++ reconfigure_output (playbin); + } + +-/* mime types we are not handling on purpose right now, don't post a +- * missing-plugin message for these */ +-static const gchar *blacklisted_mimes[] = { +- NULL +-}; ++/* Return the GstSourceGroup to which this element belongs ++ * Can be NULL (if it belongs to playsink for example) */ ++static GstSourceGroup * ++find_source_group_owner (GstPlayBin3 * playbin, GstObject * element) ++{ ++ if (playbin->curr_group->uridecodebin ++ && gst_object_has_as_ancestor (element, ++ GST_OBJECT_CAST (playbin->curr_group->uridecodebin))) ++ return playbin->curr_group; ++ if (playbin->next_group->uridecodebin ++ && gst_object_has_as_ancestor (element, ++ GST_OBJECT_CAST (playbin->next_group->uridecodebin))) ++ return playbin->next_group; ++ return NULL; ++} + + static void + gst_play_bin3_handle_message (GstBin * bin, GstMessage * msg) + { + GstPlayBin3 *playbin = GST_PLAY_BIN3 (bin); + +- if (gst_is_missing_plugin_message (msg)) { +- gchar *detail; +- guint i; +- +- detail = gst_missing_plugin_message_get_installer_detail (msg); +- for (i = 0; detail != NULL && blacklisted_mimes[i] != NULL; ++i) { +- if (strstr (detail, "|decoder-") && strstr (detail, blacklisted_mimes[i])) { +- GST_LOG_OBJECT (bin, "suppressing message %" GST_PTR_FORMAT, msg); +- gst_message_unref (msg); +- g_free (detail); +- return; +- } +- } +- g_free (detail); +- } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_START) { +- GstSourceGroup *new_group = playbin->curr_group; +- GstMessage *buffering_msg = NULL; ++ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_START) { ++ GstSourceGroup *group = NULL, *other_group = NULL; ++ gboolean changed = FALSE; ++ guint group_id; ++ GstMessage *buffering_msg; + +- GST_SOURCE_GROUP_LOCK (new_group); +- new_group->stream_changed_pending = FALSE; +- if (new_group->pending_buffering_msg) { +- buffering_msg = new_group->pending_buffering_msg; +- new_group->pending_buffering_msg = NULL; ++ if (!gst_message_parse_group_id (msg, &group_id)) { ++ GST_ERROR_OBJECT (bin, ++ "Could not get group_id from STREAM_START message !"); ++ goto beach; + } +- GST_SOURCE_GROUP_UNLOCK (new_group); ++ GST_DEBUG_OBJECT (bin, "STREAM_START group_id:%u", group_id); + +- GST_DEBUG_OBJECT (playbin, "Stream start from new group %p", new_group); +- +- if (buffering_msg) { +- GST_DEBUG_OBJECT (playbin, "Posting pending buffering message: %" +- GST_PTR_FORMAT, buffering_msg); +- GST_BIN_CLASS (parent_class)->handle_message (bin, buffering_msg); ++ /* Figure out to which group this group_id corresponds */ ++ GST_PLAY_BIN3_LOCK (playbin); ++ if (playbin->groups[0].group_id == group_id) { ++ group = &playbin->groups[0]; ++ other_group = &playbin->groups[1]; ++ } else if (playbin->groups[1].group_id == group_id) { ++ group = &playbin->groups[1]; ++ other_group = &playbin->groups[0]; ++ } ++ if (group == NULL) { ++ GST_ERROR_OBJECT (bin, "group_id %u is not provided by any group !", ++ group_id); ++ GST_PLAY_BIN3_UNLOCK (playbin); ++ goto beach; + } + +- } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_BUFFERING) { +- GstSourceGroup *group = playbin->curr_group; +- gboolean pending; ++ debug_groups (playbin); + +- /* drop buffering messages from child queues while we are switching +- * groups (because the application set a new uri in about-to-finish) +- * if the playsink queue still has buffers to play */ ++ /* Do the switch now ! */ ++ playbin->curr_group = group; ++ playbin->next_group = other_group; + + GST_SOURCE_GROUP_LOCK (group); +- pending = group->stream_changed_pending; ++ if (group->playing == FALSE) ++ changed = TRUE; ++ group->playing = TRUE; ++ buffering_msg = group->pending_buffering_msg; ++ group->pending_buffering_msg = NULL; ++ GST_SOURCE_GROUP_UNLOCK (group); ++ ++ GST_SOURCE_GROUP_LOCK (other_group); ++ other_group->playing = FALSE; ++ GST_SOURCE_GROUP_UNLOCK (other_group); + +- if (pending) { ++ debug_groups (playbin); ++ GST_PLAY_BIN3_UNLOCK (playbin); ++ if (changed) ++ gst_play_bin3_check_group_status (playbin); ++ else ++ GST_DEBUG_OBJECT (bin, "Groups didn't changed"); ++ /* If there was a pending buffering message to send, do it now */ ++ if (buffering_msg) ++ GST_BIN_CLASS (parent_class)->handle_message (bin, buffering_msg); ++ } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_BUFFERING) { ++ GstSourceGroup *group; ++ ++ /* Only post buffering messages for group which is currently playing */ ++ group = find_source_group_owner (playbin, msg->src); ++ GST_SOURCE_GROUP_LOCK (group); ++ if (!group->playing) { + GST_DEBUG_OBJECT (playbin, "Storing buffering message from pending group " + "%p %" GST_PTR_FORMAT, group, msg); + gst_message_replace (&group->pending_buffering_msg, msg); +@@ -2561,52 +2500,27 @@ gst_play_bin3_handle_message (GstBin * bin, GstMessage * msg) + GST_SOURCE_GROUP_UNLOCK (group); + } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_COLLECTION) { + GstStreamCollection *collection = NULL; +- GstObject *src = GST_MESSAGE_SRC (msg); +- gboolean pstate = playbin->do_stream_selections; + + gst_message_parse_stream_collection (msg, &collection); + + if (collection) { +- GST_PLAY_BIN3_LOCK (playbin); +- GST_DEBUG_OBJECT (playbin, +- "STREAM_COLLECTION: Got a collection from %" GST_PTR_FORMAT, src); +- gst_object_replace ((GstObject **) & playbin->collection, +- (GstObject *) collection); +- update_combiner_info (playbin); +- if (pstate) +- playbin->do_stream_selections = FALSE; +- do_stream_selection (playbin); +- if (pstate) +- playbin->do_stream_selections = TRUE; +- GST_PLAY_BIN3_UNLOCK (playbin); +- +- gst_object_unref (collection); +- } +- } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAMS_SELECTED) { +- GstStreamCollection *collection = NULL; +- GstObject *src = GST_MESSAGE_SRC (msg); +- gboolean pstate = playbin->do_stream_selections; ++ gboolean pstate = playbin->do_stream_selections; ++ GstSourceGroup *target_group = NULL; + +- gst_message_parse_streams_selected (msg, &collection); +- if (collection) { +- guint i, len; + GST_PLAY_BIN3_LOCK (playbin); + GST_DEBUG_OBJECT (playbin, +- "STREAMS_SELECTED: Got a collection from %" GST_PTR_FORMAT, src); +- gst_object_replace ((GstObject **) & playbin->collection, +- (GstObject *) collection); +- update_combiner_info (playbin); +- len = gst_message_streams_selected_get_size (msg); +- for (i = 0; i < len; i++) { +- GstStream *stream; +- +- stream = gst_message_streams_selected_get_stream (msg, i); +- set_selected_stream (playbin, stream); +- gst_object_unref (stream); +- } ++ "STREAM_COLLECTION: Got a collection from %" GST_PTR_FORMAT, ++ msg->src); ++ target_group = find_source_group_owner (playbin, msg->src); ++ if (target_group) ++ gst_object_replace ((GstObject **) & target_group->collection, ++ (GstObject *) collection); ++ /* FIXME: Only do the following if it's the current group? */ ++ if (target_group == playbin->curr_group) ++ update_combiner_info (playbin, target_group->collection); + if (pstate) + playbin->do_stream_selections = FALSE; +- do_stream_selection (playbin); ++ do_stream_selection (playbin, target_group); + if (pstate) + playbin->do_stream_selections = TRUE; + GST_PLAY_BIN3_UNLOCK (playbin); +@@ -2615,6 +2529,7 @@ gst_play_bin3_handle_message (GstBin * bin, GstMessage * msg) + } + } + ++beach: + if (msg) + GST_BIN_CLASS (parent_class)->handle_message (bin, msg); + } +@@ -2632,11 +2547,47 @@ gst_play_bin3_deep_element_added (GstBin * playbin, GstBin * sub_bin, + GST_BIN_CLASS (parent_class)->deep_element_added (playbin, sub_bin, child); + } + ++/* Returns current stream number, or -1 if none has been selected yet */ ++static int ++get_current_stream_number (GstPlayBin3 * playbin, GstSourceCombine * combine, ++ GPtrArray * channels) ++{ ++ /* Internal API cleanup would make this easier... */ ++ int i; ++ GstPad *pad, *current; ++ GstObject *combiner = NULL; ++ int ret = -1; ++ ++ if (!combine->has_active_pad) { ++ GST_WARNING_OBJECT (playbin, ++ "combiner doesn't have the \"active-pad\" property"); ++ return ret; ++ } ++ ++ for (i = 0; i < channels->len; i++) { ++ pad = g_ptr_array_index (channels, i); ++ if ((combiner = gst_pad_get_parent (pad))) { ++ g_object_get (combiner, "active-pad", ¤t, NULL); ++ gst_object_unref (combiner); ++ ++ if (pad == current) { ++ gst_object_unref (current); ++ ret = i; ++ break; ++ } ++ ++ if (current) ++ gst_object_unref (current); ++ } ++ } ++ ++ return ret; ++} ++ + static void + combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec, + GstPlayBin3 * playbin) + { +- const gchar *property; + GstSourceCombine *combine = NULL; + GPtrArray *channels = NULL; + int i; +@@ -2659,7 +2610,6 @@ combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec, + switch (combine->type) { + case GST_PLAY_SINK_TYPE_VIDEO: + case GST_PLAY_SINK_TYPE_VIDEO_RAW: +- property = "current-video"; + playbin->current_video = get_current_stream_number (playbin, + combine, channels); + +@@ -2668,12 +2618,10 @@ combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec, + GST_PLAY_BIN3_UNLOCK (playbin); + gst_play_bin3_send_custom_event (GST_OBJECT (combiner), + "playsink-custom-video-flush-finish"); +- goto notify; + } + break; + case GST_PLAY_SINK_TYPE_AUDIO: + case GST_PLAY_SINK_TYPE_AUDIO_RAW: +- property = "current-audio"; + playbin->current_audio = get_current_stream_number (playbin, + combine, channels); + +@@ -2682,11 +2630,9 @@ combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec, + GST_PLAY_BIN3_UNLOCK (playbin); + gst_play_bin3_send_custom_event (GST_OBJECT (combiner), + "playsink-custom-audio-flush-finish"); +- goto notify; + } + break; + case GST_PLAY_SINK_TYPE_TEXT: +- property = "current-text"; + playbin->current_text = get_current_stream_number (playbin, + combine, channels); + +@@ -2695,17 +2641,12 @@ combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec, + GST_PLAY_BIN3_UNLOCK (playbin); + gst_play_bin3_send_custom_event (GST_OBJECT (combiner), + "playsink-custom-subtitle-flush-finish"); +- goto notify; + } + break; + default: +- property = NULL; ++ break; + } + GST_PLAY_BIN3_UNLOCK (playbin); +- +-notify: +- if (property) +- g_object_notify (G_OBJECT (playbin), property); + } + + static GstCaps * +@@ -2713,7 +2654,7 @@ update_video_multiview_caps (GstPlayBin3 * playbin, GstCaps * caps) + { + GstVideoMultiviewMode mv_mode; + GstVideoMultiviewMode cur_mv_mode; +- GstVideoMultiviewFlags mv_flags, cur_mv_flags; ++ guint mv_flags, cur_mv_flags; + GstStructure *s; + const gchar *mview_mode_str; + GstCaps *out_caps; +@@ -2754,11 +2695,41 @@ update_video_multiview_caps (GstPlayBin3 * playbin, GstCaps * caps) + return out_caps; + } + ++static void ++emit_about_to_finish (GstPlayBin3 * playbin) ++{ ++ GST_DEBUG_OBJECT (playbin, "Emitting about-to-finish"); ++ ++ /* after this call, we should have a next group to activate or we EOS */ ++ g_signal_emit (G_OBJECT (playbin), ++ gst_play_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL); ++ ++ debug_groups (playbin); ++ ++ /* now activate the next group. If the app did not set a uri, this will ++ * fail and we can do EOS */ ++ setup_next_source (playbin); ++} ++ ++static SourcePad * ++find_source_pad (GstSourceGroup * group, GstPad * target) ++{ ++ GList *tmp; ++ ++ for (tmp = group->source_pads; tmp; tmp = tmp->next) { ++ SourcePad *res = (SourcePad *) tmp->data; ++ if (res->pad == target) ++ return res; ++ } ++ return NULL; ++} ++ + static GstPadProbeReturn + _decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata) + { + GstPadProbeReturn ret = GST_PAD_PROBE_OK; +- GstPlayBin3 *playbin = (GstPlayBin3 *) udata; ++ GstSourceGroup *group = (GstSourceGroup *) udata; ++ GstPlayBin3 *playbin = group->playbin; + GstEvent *event = GST_PAD_PROBE_INFO_DATA (info); + + switch (GST_EVENT_TYPE (event)) { +@@ -2782,6 +2753,21 @@ _decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata) + } + break; + } ++ case GST_EVENT_STREAM_START: ++ { ++ guint group_id; ++ if (gst_event_parse_group_id (event, &group_id)) { ++ GST_LOG_OBJECT (pad, "STREAM_START group_id:%u", group_id); ++ if (group->group_id == GST_GROUP_ID_INVALID) ++ group->group_id = group_id; ++ else if (group->group_id != group_id) { ++ GST_DEBUG_OBJECT (pad, "group_id changing from %u to %u", ++ group->group_id, group_id); ++ group->group_id = group_id; ++ } ++ } ++ break; ++ } + default: + break; + } +@@ -2789,20 +2775,236 @@ _decodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata) + return ret; + } + ++static void ++control_source_pad (GstSourceGroup * group, GstPad * pad, ++ GstStreamType stream_type) ++{ ++ SourcePad *sourcepad = g_slice_new0 (SourcePad); ++ ++ sourcepad->pad = pad; ++ sourcepad->event_probe_id = ++ gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, ++ _decodebin_event_probe, group, NULL); ++ sourcepad->stream_type = stream_type; ++ group->source_pads = g_list_append (group->source_pads, sourcepad); ++} ++ ++static void ++remove_combiner (GstPlayBin3 * playbin, GstSourceCombine * combine) ++{ ++ gint n; ++ ++ if (combine->combiner == NULL) { ++ GST_DEBUG_OBJECT (playbin, "No combiner element to remove"); ++ return; ++ } ++ ++ /* Go over all sink pads and release them ! */ ++ for (n = 0; n < combine->channels->len; n++) { ++ GstPad *sinkpad = g_ptr_array_index (combine->channels, n); ++ ++ gst_element_release_request_pad (combine->combiner, sinkpad); ++ gst_object_unref (sinkpad); ++ } ++ g_ptr_array_set_size (combine->channels, 0); ++ ++ gst_element_set_state (combine->combiner, GST_STATE_NULL); ++ gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner); ++ combine->combiner = NULL; ++ ++} ++ ++/* Create the combiner element if needed for the given combine */ ++static void ++create_combiner (GstPlayBin3 * playbin, GstSourceCombine * combine) ++{ ++ GstElement *custom_combiner = NULL; ++ ++ if (combine->combiner) { ++ GST_WARNING_OBJECT (playbin, "Combiner element already exists!"); ++ return; ++ } ++ ++ if (combine->stream_type == GST_STREAM_TYPE_VIDEO) ++ custom_combiner = playbin->video_stream_combiner; ++ else if (combine->stream_type == GST_STREAM_TYPE_AUDIO) ++ custom_combiner = playbin->audio_stream_combiner; ++ else if (combine->stream_type == GST_STREAM_TYPE_TEXT) ++ custom_combiner = playbin->text_stream_combiner; ++ ++ combine->combiner = custom_combiner; ++ ++ if (!combine->combiner) { ++ gchar *concat_name; ++ GST_DEBUG_OBJECT (playbin, ++ "No custom combiner requested, using 'concat' element"); ++ concat_name = g_strdup_printf ("%s-concat", combine->media_type); ++ combine->combiner = gst_element_factory_make ("concat", concat_name); ++ g_object_set (combine->combiner, "adjust-base", FALSE, NULL); ++ g_free (concat_name); ++ combine->is_concat = TRUE; ++ } ++ ++ combine->srcpad = gst_element_get_static_pad (combine->combiner, "src"); ++ ++ /* We only want to use 'active-pad' if it's a regular combiner that ++ * will consume all streams, and not concat (which is just used for ++ * gapless) */ ++ if (!combine->is_concat) { ++ combine->has_active_pad = ++ g_object_class_find_property (G_OBJECT_GET_CLASS (combine->combiner), ++ "active-pad") != NULL; ++ ++ if (combine->has_active_pad) ++ g_signal_connect (combine->combiner, "notify::active-pad", ++ G_CALLBACK (combiner_active_pad_changed), playbin); ++ } ++ ++ GST_DEBUG_OBJECT (playbin, "adding new stream combiner %" GST_PTR_FORMAT, ++ combine->combiner); ++ gst_bin_add (GST_BIN_CAST (playbin), combine->combiner); ++ gst_element_sync_state_with_parent (combine->combiner); ++} ++ ++static gboolean ++combiner_control_pad (GstPlayBin3 * playbin, GstSourceCombine * combine, ++ GstPad * srcpad) ++{ ++ GstPadLinkReturn res; ++ ++ GST_DEBUG_OBJECT (playbin, "srcpad %" GST_PTR_FORMAT, srcpad); ++ ++ if (combine->combiner) { ++ GstPad *sinkpad = ++ gst_element_get_request_pad (combine->combiner, "sink_%u"); ++ ++ if (sinkpad == NULL) ++ goto request_pad_failed; ++ ++ GST_DEBUG_OBJECT (playbin, "Got new combiner pad %" GST_PTR_FORMAT, ++ sinkpad); ++ ++ /* store the pad in the array */ ++ GST_DEBUG_OBJECT (playbin, "pad %" GST_PTR_FORMAT " added to array", ++ sinkpad); ++ g_ptr_array_add (combine->channels, sinkpad); ++ ++ res = gst_pad_link (srcpad, sinkpad); ++ if (GST_PAD_LINK_FAILED (res)) ++ goto failed_combiner_link; ++ ++ GST_DEBUG_OBJECT (playbin, ++ "linked pad %" GST_PTR_FORMAT " to combiner %" GST_PTR_FORMAT, srcpad, ++ combine->combiner); ++ ++ } else { ++ GST_LOG_OBJECT (playbin, "combine->sinkpad:%" GST_PTR_FORMAT, ++ combine->sinkpad); ++ g_assert (combine->sinkpad != NULL); ++ /* Connect directly to playsink */ ++ if (gst_pad_is_linked (combine->sinkpad)) ++ goto sinkpad_already_linked; ++ ++ GST_DEBUG_OBJECT (playbin, "Linking new pad straight to playsink"); ++ res = gst_pad_link (srcpad, combine->sinkpad); ++ ++ if (res != GST_PAD_LINK_OK) ++ goto failed_sinkpad_link; ++ } ++ ++ return TRUE; ++ ++ /* Failure cases */ ++request_pad_failed: ++ GST_ELEMENT_ERROR (playbin, CORE, PAD, ++ ("Internal playbin error."), ++ ("Failed to get request pad from combiner %p.", combine->combiner)); ++ return FALSE; ++ ++ ++sinkpad_already_linked: ++ GST_ELEMENT_ERROR (playbin, CORE, PAD, ++ ("Internal playbin error."), ("playsink pad already used !")); ++ return FALSE; ++ ++failed_sinkpad_link: ++ GST_ELEMENT_ERROR (playbin, CORE, PAD, ++ ("Internal playbin error."), ++ ("Failed to link pad to sink. Error %d", res)); ++ return FALSE; ++ ++failed_combiner_link: ++ GST_ELEMENT_ERROR (playbin, CORE, PAD, ++ ("Internal playbin error."), ++ ("Failed to link pad to combiner. Error %d", res)); ++ return FALSE; ++} ++ ++static void ++combiner_release_pad (GstPlayBin3 * playbin, GstSourceCombine * combine, ++ GstPad * pad) ++{ ++ if (combine->combiner) { ++ GstPad *peer = gst_pad_get_peer (pad); ++ ++ if (peer) { ++ GST_DEBUG_OBJECT (playbin, "Removing combiner pad %" GST_PTR_FORMAT, ++ peer); ++ g_ptr_array_remove (combine->channels, peer); ++ ++ gst_element_release_request_pad (combine->combiner, peer); ++ gst_object_unref (peer); ++ } ++ } else { ++ /* Release direct link if present */ ++ if (combine->sinkpad) { ++ GST_DEBUG_OBJECT (playbin, "Unlinking pad from playsink sinkpad"); ++ gst_pad_unlink (pad, combine->sinkpad); ++ } ++ } ++} ++ ++/* Call after pad was unlinked from (potential) combiner */ ++static void ++release_source_pad (GstPlayBin3 * playbin, GstSourceGroup * group, GstPad * pad) ++{ ++ SourcePad *sourcepad; ++ GList *tmp; ++ GstStreamType alltype = 0; ++ ++ sourcepad = find_source_pad (group, pad); ++ if (!sourcepad) { ++ GST_DEBUG_OBJECT (playbin, "Not a pad controlled by us ?"); ++ return; ++ } ++ ++ if (sourcepad->event_probe_id) { ++ gst_pad_remove_probe (pad, sourcepad->event_probe_id); ++ sourcepad->event_probe_id = 0; ++ } ++ ++ /* Remove from list of controlled pads and check again for EOS status */ ++ group->source_pads = g_list_remove (group->source_pads, sourcepad); ++ g_slice_free (SourcePad, sourcepad); ++ ++ /* Update present stream types */ ++ for (tmp = group->source_pads; tmp; tmp = tmp->next) { ++ SourcePad *cand = (SourcePad *) tmp->data; ++ alltype |= cand->stream_type; ++ } ++ group->present_stream_types = alltype; ++} ++ + /* this function is called when a new pad is added to decodebin. We check the + * type of the pad and add it to the combiner element + */ + static void +-pad_added_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin) ++pad_added_cb (GstElement * uridecodebin, GstPad * pad, GstSourceGroup * group) + { +- GstPad *sinkpad; +- GstPadLinkReturn res; + GstSourceCombine *combine = NULL; +- GstStreamType stream_type; + gint pb_stream_type = -1; +- GstElement *custom_combiner = NULL; +- gulong event_probe_handler; + gchar *pad_name; ++ GstPlayBin3 *playbin = group->playbin; + + GST_PLAY_BIN3_SHUTDOWN_LOCK (playbin, shutdown); + +@@ -2814,17 +3016,11 @@ pad_added_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin) + /* major type of the pad, this determines the combiner to use, + try exact match first */ + if (g_str_has_prefix (pad_name, "video")) { +- stream_type = GST_STREAM_TYPE_VIDEO; + pb_stream_type = PLAYBIN_STREAM_VIDEO; +- custom_combiner = playbin->video_stream_combiner; + } else if (g_str_has_prefix (pad_name, "audio")) { +- stream_type = GST_STREAM_TYPE_AUDIO; + pb_stream_type = PLAYBIN_STREAM_AUDIO; +- custom_combiner = playbin->audio_stream_combiner; + } else if (g_str_has_prefix (pad_name, "text")) { +- stream_type = GST_STREAM_TYPE_TEXT; + pb_stream_type = PLAYBIN_STREAM_TEXT; +- custom_combiner = playbin->text_stream_combiner; + } + + g_free (pad_name); +@@ -2838,111 +3034,21 @@ pad_added_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin) + + combine = &playbin->combiner[pb_stream_type]; + +- if (custom_combiner && combine->combiner == NULL) { +- combine->combiner = custom_combiner; +- /* find out which properties the stream combiner supports */ +- combine->has_active_pad = +- g_object_class_find_property (G_OBJECT_GET_CLASS (combine->combiner), +- "active-pad") != NULL; ++ combiner_control_pad (playbin, combine, pad); + +- if (!custom_combiner) { +- /* sync-mode=1, use clock */ +- if (combine->type == GST_PLAY_SINK_TYPE_TEXT) +- g_object_set (combine->combiner, "sync-streams", TRUE, +- "sync-mode", 1, "cache-buffers", TRUE, NULL); +- else +- g_object_set (combine->combiner, "sync-streams", TRUE, NULL); +- } ++ control_source_pad (group, pad, combine->stream_type); + +- if (combine->has_active_pad) +- g_signal_connect (combine->combiner, "notify::active-pad", +- G_CALLBACK (combiner_active_pad_changed), playbin); ++ /* Update present stream_types and check whether we should post a pending about-to-finish */ ++ group->present_stream_types |= combine->stream_type; + +- GST_DEBUG_OBJECT (playbin, "adding new stream combiner %p", +- combine->combiner); +- gst_element_set_state (combine->combiner, GST_STATE_PAUSED); +- gst_bin_add (GST_BIN_CAST (playbin), combine->combiner); ++ if (group->playing && group->pending_about_to_finish ++ && group->present_stream_types == group->selected_stream_types) { ++ group->pending_about_to_finish = FALSE; ++ emit_about_to_finish (playbin); + } + + GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin); + +- if (combine->srcpad == NULL) { +- if (combine->combiner) { +- /* save source pad of the combiner */ +- combine->srcpad = gst_element_get_static_pad (combine->combiner, "src"); +- } else { +- /* no combiner, use the pad as the source pad then */ +- combine->srcpad = gst_object_ref (pad); +- } +- +- /* block the combiner srcpad. It's possible that multiple source elements +- * pushing data into the combiners before we have a chance to collect all +- * streams and connect the sinks, resulting in not-linked errors. After we +- * configure the sinks we will unblock them all. */ +- GST_DEBUG_OBJECT (playbin, "blocking %" GST_PTR_FORMAT, combine->srcpad); +- combine->block_id = +- gst_pad_add_probe (combine->srcpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, +- block_serialized_data_cb, NULL, NULL); +- } +- +- /* get sinkpad for the new stream */ +- if (combine->combiner) { +- if ((sinkpad = gst_element_get_request_pad (combine->combiner, "sink_%u"))) { +- GST_DEBUG_OBJECT (playbin, "got pad %s:%s from combiner", +- GST_DEBUG_PAD_NAME (sinkpad)); +- +- /* find out which properties the sink pad supports */ +- combine->has_always_ok = +- g_object_class_find_property (G_OBJECT_GET_CLASS (sinkpad), +- "always-ok") != NULL; +- +- /* store the combiner for the pad */ +- g_object_set_data (G_OBJECT (sinkpad), "playbin.combine", combine); +- +- /* store the pad in the array */ +- GST_DEBUG_OBJECT (playbin, "pad %p added to array", sinkpad); +- g_ptr_array_add (combine->channels, sinkpad); +- +- res = gst_pad_link (pad, sinkpad); +- if (GST_PAD_LINK_FAILED (res)) +- goto link_failed; +- +- /* store combiner pad so we can release it */ +- g_object_set_data (G_OBJECT (pad), "playbin.sinkpad", sinkpad); +- +- GST_DEBUG_OBJECT (playbin, "linked pad %s:%s to combiner %p", +- GST_DEBUG_PAD_NAME (pad), combine->combiner); +- } else { +- goto request_pad_failed; +- } +- } else { +- /* no combiner, don't configure anything, we'll link the new pad directly to +- * the sink. */ +- sinkpad = NULL; +- +- /* store the combiner for the pad */ +- g_object_set_data (G_OBJECT (pad), "playbin.combine", combine); +- } +- +- event_probe_handler = +- gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, +- _decodebin_event_probe, playbin, NULL); +- g_object_set_data (G_OBJECT (pad), "playbin.event_probe_id", +- ULONG_TO_POINTER (event_probe_handler)); +- +- playbin->active_stream_types |= stream_type; +- +- /* If we're expecting either audio or video, +- * wait for them to appear before configuring playsink */ +- if ((playbin->selected_stream_types & ~playbin->active_stream_types & +- (GST_STREAM_TYPE_VIDEO | GST_STREAM_TYPE_AUDIO)) +- == 0) { +- no_more_pads_cb (decodebin, playbin); +- } else { +- GST_LOG_OBJECT (playbin, "Active stream types 0x%x, want 0x%x. Waiting", +- playbin->active_stream_types, playbin->selected_stream_types); +- } +- + return; + + /* ERRORS */ +@@ -2950,18 +3056,6 @@ unknown_type: + GST_DEBUG_OBJECT (playbin, "Ignoring pad with unknown type"); + return; + +-link_failed: +- { +- GST_ERROR_OBJECT (playbin, +- "failed to link pad %s:%s to combiner, reason %s (%d)", +- GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res); +- return; +- } +-request_pad_failed: +- GST_ELEMENT_ERROR (playbin, CORE, PAD, +- ("Internal playbin error."), +- ("Failed to get request pad from combiner %p.", combine->combiner)); +- return; + shutdown: + { + GST_DEBUG ("ignoring, we are shutting down. Pad will be left unlinked"); +@@ -2971,86 +3065,42 @@ shutdown: + } + + /* called when a pad is removed from the decodebin. We unlink the pad from +- * the combiner. This will make the combiner select a new pad. */ ++ * the combiner. */ + static void +-pad_removed_cb (GstElement * decodebin, GstPad * pad, GstPlayBin3 * playbin) ++pad_removed_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group) + { +- GstPad *peer; +- GstElement *combiner; + GstSourceCombine *combine; +- gulong event_probe_handler; ++ GstPlayBin3 *playbin = group->playbin; + + GST_DEBUG_OBJECT (playbin, + "decoded pad %s:%s removed", GST_DEBUG_PAD_NAME (pad)); + + GST_PLAY_BIN3_LOCK (playbin); + +- if ((event_probe_handler = +- POINTER_TO_ULONG (g_object_get_data (G_OBJECT (pad), +- "playbin.event_probe_id")))) { +- gst_pad_remove_probe (pad, event_probe_handler); +- g_object_set_data (G_OBJECT (pad), "playbin.event_probe_id", NULL); +- } +- +- if ((combine = g_object_get_data (G_OBJECT (pad), "playbin.combine"))) { +- g_assert (combine->combiner == NULL); +- g_assert (combine->srcpad == pad); +- source_combine_remove_pads (playbin, combine); +- goto exit; +- } +- +- /* get the combiner sinkpad */ +- if (!(peer = g_object_get_data (G_OBJECT (pad), "playbin.sinkpad"))) +- goto not_linked; +- +- /* unlink the pad now (can fail, the pad is unlinked before it's removed) */ +- gst_pad_unlink (pad, peer); +- +- /* get combiner */ +- combiner = GST_ELEMENT_CAST (gst_pad_get_parent (peer)); +- g_assert (combiner != NULL); +- +- if ((combine = g_object_get_data (G_OBJECT (peer), "playbin.combine"))) { +- /* remove the pad from the array */ +- g_ptr_array_remove (combine->channels, peer); +- GST_DEBUG_OBJECT (playbin, "pad %p removed from array", peer); +- +- if (!combine->channels->len && combine->combiner) { +- GST_DEBUG_OBJECT (playbin, "all combiner sinkpads removed"); +- GST_DEBUG_OBJECT (playbin, "removing combiner %p", combine->combiner); +- source_combine_remove_pads (playbin, combine); +- gst_element_set_state (combine->combiner, GST_STATE_NULL); +- gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner); +- combine->combiner = NULL; +- } +- } ++ /* Get combiner for pad */ ++ if (g_str_has_prefix (GST_PAD_NAME (pad), "video")) ++ combine = &playbin->combiner[PLAYBIN_STREAM_VIDEO]; ++ else if (g_str_has_prefix (GST_PAD_NAME (pad), "audio")) ++ combine = &playbin->combiner[PLAYBIN_STREAM_AUDIO]; ++ else if (g_str_has_prefix (GST_PAD_NAME (pad), "text")) ++ combine = &playbin->combiner[PLAYBIN_STREAM_TEXT]; ++ else ++ return; + +- /* release the pad to the combiner, this will make the combiner choose a new +- * pad. */ +- gst_element_release_request_pad (combiner, peer); +- gst_object_unref (peer); ++ combiner_release_pad (playbin, combine, pad); ++ release_source_pad (playbin, group, pad); + +- gst_object_unref (combiner); +-exit: + GST_PLAY_BIN3_UNLOCK (playbin); +- +- return; +- +- /* ERRORS */ +-not_linked: +- { +- GST_DEBUG_OBJECT (playbin, "pad not linked"); +- goto exit; +- } + } + + + static gint + select_stream_cb (GstElement * decodebin, GstStreamCollection * collection, +- GstStream * stream, GstPlayBin3 * playbin) ++ GstStream * stream, GstSourceGroup * group) + { + GstStreamType stype = gst_stream_get_stream_type (stream); + GstElement *combiner = NULL; ++ GstPlayBin3 *playbin = group->playbin; + + if (stype & GST_STREAM_TYPE_AUDIO) + combiner = playbin->audio_stream_combiner; +@@ -3068,190 +3118,134 @@ select_stream_cb (GstElement * decodebin, GstStreamCollection * collection, + return -1; + } + +-/* we get called when all pads are available and we must connect the sinks to +- * them. +- * The main purpose of the code is to see if we have video/audio and subtitles +- * and pick the right pipelines to display them. +- * +- * The combiners installed on the group tell us about the presence of +- * audio/video and subtitle streams. This allows us to see if we need +- * visualisation, video or/and audio. ++/* We get called when the selected stream types change and ++ * reconfiguration of output (i.e. playsink and potential combiners) ++ * are required. + */ + static void +-no_more_pads_cb (GstElement * decodebin, GstPlayBin3 * playbin) ++reconfigure_output (GstPlayBin3 * playbin) + { +- GstSourceGroup *group; + GstPadLinkReturn res; + gint i; +- gboolean configure; + +- GST_DEBUG_OBJECT (playbin, "no more pads"); ++ g_assert (playbin->selected_stream_types != playbin->active_stream_types); + +- GST_PLAY_BIN3_SHUTDOWN_LOCK (playbin, shutdown); ++ GST_DEBUG_OBJECT (playbin, "selected_stream_types : %" STREAM_TYPES_FORMAT, ++ STREAM_TYPES_ARGS (playbin->selected_stream_types)); ++ GST_DEBUG_OBJECT (playbin, "active_stream_types : %" STREAM_TYPES_FORMAT, ++ STREAM_TYPES_ARGS (playbin->active_stream_types)); + + GST_PLAY_BIN3_LOCK (playbin); +- group = playbin->curr_group; + ++ /* Make sure combiners/playsink are in sync with selected stream types */ + for (i = 0; i < PLAYBIN_STREAM_LAST; i++) { + GstSourceCombine *combine = &playbin->combiner[i]; +- +- /* check if the specific media type was detected and thus has a combiner +- * created for it. If there is the media type, get a sinkpad from the sink +- * and link it. We only do this if we have not yet requested the sinkpad +- * before. */ +- if (combine->srcpad && combine->sinkpad == NULL) { +- GST_DEBUG_OBJECT (playbin, "requesting new sink pad %d", combine->type); +- combine->sinkpad = +- gst_play_sink_request_pad (playbin->playsink, combine->type); +- gst_object_ref (combine->sinkpad); +- } else if (combine->srcpad && combine->sinkpad) { +- GST_DEBUG_OBJECT (playbin, "re-using sink pad %d", combine->type); +- } else if (combine->sinkpad && combine->srcpad == NULL) { +- GST_DEBUG_OBJECT (playbin, "releasing sink pad %d", combine->type); +- gst_play_sink_release_pad (playbin->playsink, combine->sinkpad); +- gst_object_unref (combine->sinkpad); +- combine->sinkpad = NULL; +- } +- if (combine->sinkpad && combine->srcpad && +- !gst_pad_is_linked (combine->srcpad)) { +- res = gst_pad_link (combine->srcpad, combine->sinkpad); +- GST_DEBUG_OBJECT (playbin, "linked type %s, result: %d", +- combine->media_type, res); +- if (res != GST_PAD_LINK_OK) { +- GST_ELEMENT_ERROR (playbin, CORE, PAD, +- ("Internal playbin error."), +- ("Failed to link combiner to sink. Error %d", res)); +- } +- } +- } +- GST_PLAY_BIN3_UNLOCK (playbin); +- +- GST_SOURCE_GROUP_LOCK (group); +- GST_DEBUG_OBJECT (playbin, "pending %d > %d", group->pending, +- group->pending - 1); +- +- if (group->pending > 0) +- group->pending--; +- +- if (group->pending == 0) { +- /* we are the last group to complete, we will configure the output and then +- * signal the other waiters. */ +- GST_LOG_OBJECT (playbin, "last group complete"); +- configure = TRUE; +- } else { +- GST_LOG_OBJECT (playbin, "have more pending groups"); +- configure = FALSE; +- } +- GST_SOURCE_GROUP_UNLOCK (group); +- +- if (configure) { +- /* if we have custom sinks, configure them now */ +- GST_SOURCE_GROUP_LOCK (group); +- +- if (group->audio_sink) { +- GST_INFO_OBJECT (playbin, "setting custom audio sink %" GST_PTR_FORMAT, +- group->audio_sink); +- gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO, +- group->audio_sink); +- } +- +- if (group->video_sink) { +- GST_INFO_OBJECT (playbin, "setting custom video sink %" GST_PTR_FORMAT, +- group->video_sink); +- gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO, +- group->video_sink); +- } +- +- if (group->text_sink) { +- GST_INFO_OBJECT (playbin, "setting custom text sink %" GST_PTR_FORMAT, +- group->text_sink); +- gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT, +- group->text_sink); +- } +- +- GST_SOURCE_GROUP_UNLOCK (group); +- +- /* signal the other combiners that they can continue now. */ +- GST_PLAY_BIN3_LOCK (playbin); +- /* unblock all combiners */ +- for (i = 0; i < PLAYBIN_STREAM_LAST; i++) { +- GstSourceCombine *combine = &playbin->combiner[i]; +- ++ gboolean is_selected = ++ (combine->stream_type & playbin->selected_stream_types) == ++ combine->stream_type; ++ gboolean is_active = ++ (combine->stream_type & playbin->active_stream_types) == ++ combine->stream_type; ++ ++ GST_DEBUG_OBJECT (playbin, "Stream type status: '%s' %s %s", ++ combine->media_type, is_selected ? "selected" : "NOT selected", ++ is_active ? "active" : "NOT active"); ++ /* FIXME : Remove asserts below once enough testing has been done */ ++ ++ if (is_selected && is_active) { ++ GST_DEBUG_OBJECT (playbin, "Stream type '%s' already active", ++ combine->media_type); ++ } else if (is_active && !is_selected) { ++ GST_DEBUG_OBJECT (playbin, "Stream type '%s' is no longer requested", ++ combine->media_type); ++ ++ /* Unlink combiner from sink */ + if (combine->srcpad) { +- GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT, +- combine->srcpad); +- if (combine->block_id) { +- gst_pad_remove_probe (combine->srcpad, combine->block_id); +- combine->block_id = 0; +- } ++ GST_LOG_OBJECT (playbin, "Unlinking from sink"); ++ if (combine->sinkpad) ++ gst_pad_unlink (combine->srcpad, combine->sinkpad); ++ gst_object_unref (combine->srcpad); ++ combine->srcpad = NULL; + } +- } +- GST_PLAY_BIN3_UNLOCK (playbin); +- gst_play_sink_reconfigure (playbin->playsink); +- } + +- GST_PLAY_BIN3_SHUTDOWN_UNLOCK (playbin); ++ if (combine->sinkpad) { ++ /* Release playsink sink pad */ ++ GST_LOG_OBJECT (playbin, "Releasing playsink pad"); ++ gst_play_sink_release_pad (playbin->playsink, combine->sinkpad); ++ gst_object_unref (combine->sinkpad); ++ combine->sinkpad = NULL; ++ } + +- if (configure) { +- do_async_done (playbin); +- } ++ /* Release combiner */ ++ GST_FIXME_OBJECT (playbin, "Release combiner"); ++ remove_combiner (playbin, combine); ++ } else if (!is_active && is_selected) { ++ GST_DEBUG_OBJECT (playbin, "Stream type '%s' is now requested", ++ combine->media_type); + +- return; ++ /* If we are shutting down, do *not* add more combiners */ ++ if (g_atomic_int_get (&playbin->shutdown)) ++ continue; + +-shutdown: +- { +- GST_DEBUG ("ignoring, we are shutting down"); +- /* Request a flushing pad from playsink that we then link to the combiner. +- * Then we unblock the combiners so that they stop with a WRONG_STATE +- * instead of a NOT_LINKED error. +- */ +- GST_PLAY_BIN3_LOCK (playbin); +- for (i = 0; i < PLAYBIN_STREAM_LAST; i++) { +- GstSourceCombine *combine = &playbin->combiner[i]; ++ g_assert (combine->sinkpad == NULL); + +- if (combine->srcpad) { +- if (combine->sinkpad == NULL) { +- GST_DEBUG_OBJECT (playbin, "requesting new flushing sink pad"); +- combine->sinkpad = +- gst_play_sink_request_pad (playbin->playsink, +- GST_PLAY_SINK_TYPE_FLUSHING); +- gst_object_ref (combine->sinkpad); +- res = gst_pad_link (combine->srcpad, combine->sinkpad); +- GST_DEBUG_OBJECT (playbin, "linked flushing, result: %d", res); +- } +- GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT, +- combine->srcpad); +- if (combine->block_id) { +- gst_pad_remove_probe (combine->srcpad, combine->block_id); +- combine->block_id = 0; ++ /* Request playsink sink pad */ ++ combine->sinkpad = ++ gst_play_sink_request_pad (playbin->playsink, combine->type); ++ gst_object_ref (combine->sinkpad); ++ /* Create combiner if needed and link it */ ++ create_combiner (playbin, combine); ++ if (combine->combiner) { ++ res = gst_pad_link (combine->srcpad, combine->sinkpad); ++ GST_DEBUG_OBJECT (playbin, "linked type %s, result: %d", ++ combine->media_type, res); ++ if (res != GST_PAD_LINK_OK) { ++ GST_ELEMENT_ERROR (playbin, CORE, PAD, ++ ("Internal playbin error."), ++ ("Failed to link combiner to sink. Error %d", res)); + } ++ + } + } +- GST_PLAY_BIN3_UNLOCK (playbin); +- return; + } ++ ++ playbin->active_stream_types = playbin->selected_stream_types; ++ ++ GST_PLAY_BIN3_UNLOCK (playbin); ++ ++ gst_play_sink_reconfigure (playbin->playsink); ++ ++ do_async_done (playbin); ++ ++ GST_DEBUG_OBJECT (playbin, "selected_stream_types : %" STREAM_TYPES_FORMAT, ++ STREAM_TYPES_ARGS (playbin->selected_stream_types)); ++ GST_DEBUG_OBJECT (playbin, "active_stream_types : %" STREAM_TYPES_FORMAT, ++ STREAM_TYPES_ARGS (playbin->active_stream_types)); ++ ++ return; + } + +-#if 0 + static void +-drained_cb (GstElement * decodebin, GstSourceGroup * group) ++about_to_finish_cb (GstElement * uridecodebin, GstSourceGroup * group) + { +- GstPlayBin3 *playbin; +- +- playbin = group->playbin; +- ++ GstPlayBin3 *playbin = group->playbin; + GST_DEBUG_OBJECT (playbin, "about to finish in group %p", group); + +- /* after this call, we should have a next group to activate or we EOS */ +- g_signal_emit (G_OBJECT (playbin), +- gst_play_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL); ++ GST_LOG_OBJECT (playbin, "selected_stream_types:%" STREAM_TYPES_FORMAT, ++ STREAM_TYPES_ARGS (group->selected_stream_types)); ++ GST_LOG_OBJECT (playbin, "present_stream_types:%" STREAM_TYPES_FORMAT, ++ STREAM_TYPES_ARGS (group->present_stream_types)); + +- /* now activate the next group. If the app did not set a uri, this will +- * fail and we can do EOS */ +- setup_next_source (playbin, GST_STATE_PAUSED); ++ if (group->selected_stream_types == 0 ++ || (group->selected_stream_types != group->present_stream_types)) { ++ GST_LOG_OBJECT (playbin, ++ "Delaying emission of signal until this group is ready"); ++ group->pending_about_to_finish = TRUE; ++ } else ++ emit_about_to_finish (playbin); + } +-#endif + ++#if 0 /* AUTOPLUG DISABLED */ + /* Like gst_element_factory_can_sink_any_caps() but doesn't + * allow ANY caps on the sinkpad template */ + static gboolean +@@ -3657,64 +3651,7 @@ autoplug_factories_cb (GstElement * decodebin, GstPad * pad, + + return result; + } +- +-static void +-gst_play_bin3_set_context (GstElement * element, GstContext * context) +-{ +- GstPlayBin3 *playbin = GST_PLAY_BIN3 (element); +- +- /* Proxy contexts to the sinks, they might not be in playsink yet */ +- GST_PLAY_BIN3_LOCK (playbin); +- if (playbin->audio_sink) +- gst_element_set_context (playbin->audio_sink, context); +- if (playbin->video_sink) +- gst_element_set_context (playbin->video_sink, context); +- if (playbin->text_sink) +- gst_element_set_context (playbin->text_sink, context); +- +- GST_SOURCE_GROUP_LOCK (playbin->curr_group); +- +- if (playbin->curr_group->audio_sink) +- gst_element_set_context (playbin->curr_group->audio_sink, context); +- if (playbin->curr_group->video_sink) +- gst_element_set_context (playbin->curr_group->video_sink, context); +- if (playbin->curr_group->text_sink) +- gst_element_set_context (playbin->curr_group->text_sink, context); +- +- GST_SOURCE_GROUP_UNLOCK (playbin->curr_group); +- GST_PLAY_BIN3_UNLOCK (playbin); +- +- GST_ELEMENT_CLASS (parent_class)->set_context (element, context); +-} +- +-/* Pass sink messages to the application, e.g. NEED_CONTEXT messages */ +-static void +-gst_play_bin3_update_context (GstPlayBin3 * playbin, GstContext * context) +-{ +- GList *l; +- const gchar *context_type; +- +- GST_OBJECT_LOCK (playbin); +- context_type = gst_context_get_context_type (context); +- for (l = playbin->contexts; l; l = l->next) { +- GstContext *tmp = l->data; +- const gchar *tmp_type = gst_context_get_context_type (tmp); +- +- /* Always store newest context but never replace +- * a persistent one by a non-persistent one */ +- if (strcmp (context_type, tmp_type) == 0 && +- (gst_context_is_persistent (context) || +- !gst_context_is_persistent (tmp))) { +- gst_context_replace ((GstContext **) & l->data, context); +- break; +- } +- } +- /* Not found? Add */ +- if (l == NULL) +- playbin->contexts = +- g_list_prepend (playbin->contexts, gst_context_ref (context)); +- GST_OBJECT_UNLOCK (playbin); +-} ++#endif + + static GstBusSyncReply + activate_sink_bus_handler (GstBus * bus, GstMessage * msg, +@@ -3737,37 +3674,6 @@ activate_sink_bus_handler (GstBus * bus, GstMessage * msg, + gst_element_post_message (GST_ELEMENT_CAST (playbin), msg); + else + gst_message_unref (msg); +- } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_NEED_CONTEXT) { +- const gchar *context_type; +- GList *l; +- +- gst_message_parse_context_type (msg, &context_type); +- GST_OBJECT_LOCK (playbin); +- for (l = playbin->contexts; l; l = l->next) { +- GstContext *tmp = l->data; +- const gchar *tmp_type = gst_context_get_context_type (tmp); +- +- if (strcmp (context_type, tmp_type) == 0) { +- gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (msg)), l->data); +- break; +- } +- } +- GST_OBJECT_UNLOCK (playbin); +- +- /* Forward if we couldn't answer the message */ +- if (l == NULL) { +- gst_element_post_message (GST_ELEMENT_CAST (playbin), msg); +- } else { +- gst_message_unref (msg); +- } +- } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_HAVE_CONTEXT) { +- GstContext *context; +- +- gst_message_parse_have_context (msg, &context); +- gst_play_bin3_update_context (playbin, context); +- gst_context_unref (context); +- +- gst_element_post_message (GST_ELEMENT_CAST (playbin), msg); + } else { + gst_element_post_message (GST_ELEMENT_CAST (playbin), msg); + } +@@ -3819,6 +3725,7 @@ done: + return ret; + } + ++#if 0 /* AUTOPLUG DISABLED */ + /* autoplug-continue decides, if a pad has raw caps that can be exposed + * directly or if further decoding is necessary. We use this to expose + * supported subtitles directly */ +@@ -4001,7 +3908,7 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad, + ave_list = g_list_prepend (ave_list, NULL); + } + +- /* if it is a decoder and we don't have a fixed sink, then find out ++ /* if it is a decoder and we don't have a fixed sink, then find out + * the matching audio/video sink from GstAVElements list */ + for (l = ave_list; l; l = l->next) { + gboolean created_sink = FALSE; +@@ -4189,7 +4096,7 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad, + } + + /* remember the sink in the group now, the element is floating, we take +- * ownership now ++ * ownership now + * + * store the sink in the group, we will configure it later when we + * reconfigure the sink */ +@@ -4442,29 +4349,7 @@ autoplug_query_cb (GstElement * uridecodebin, GstPad * pad, + return FALSE; + } + } +- +-static void +-notify_source_cb (GstElement * urisourcebin, GParamSpec * pspec, +- GstSourceGroup * group) +-{ +- GstPlayBin3 *playbin; +- GstElement *source; +- +- playbin = group->playbin; +- +- g_object_get (urisourcebin, "source", &source, NULL); +- +- GST_OBJECT_LOCK (playbin); +- if (playbin->source) +- gst_object_unref (playbin->source); +- playbin->source = source; +- GST_OBJECT_UNLOCK (playbin); +- +- g_object_notify (G_OBJECT (playbin), "source"); +- +- g_signal_emit (playbin, gst_play_bin3_signals[SIGNAL_SOURCE_SETUP], +- 0, playbin->source); +-} ++#endif + + /* must be called with the group lock */ + static gboolean +@@ -4473,10 +4358,8 @@ group_set_locked_state_unlocked (GstPlayBin3 * playbin, GstSourceGroup * group, + { + GST_DEBUG_OBJECT (playbin, "locked_state %d on group %p", locked, group); + +- if (group->urisourcebin) +- gst_element_set_locked_state (group->urisourcebin, locked); +- if (group->suburisourcebin) +- gst_element_set_locked_state (group->suburisourcebin, locked); ++ if (group->uridecodebin) ++ gst_element_set_locked_state (group->uridecodebin, locked); + + return TRUE; + } +@@ -4506,160 +4389,20 @@ make_or_reuse_element (GstPlayBin3 * playbin, const gchar * name, + return TRUE; + } + +-static void +-urisrc_pad_added (GstElement * urisrc, GstPad * pad, GstSourceGroup * group) +-{ +- GstPadLinkReturn res; +- GstPad *sinkpad = NULL; +- GstPlayBin3 *playbin; +- +- GST_SOURCE_GROUP_LOCK (group); +- playbin = group->playbin; +- if (urisrc == group->urisourcebin) { +- /* Primary stream, link to the main pad of decodebin3 */ +- sinkpad = gst_element_get_static_pad (playbin->decodebin, "sink"); +- if (gst_pad_is_linked (sinkpad)) { +- gst_object_unref (GST_OBJECT (sinkpad)); +- sinkpad = NULL; +- } +- } +- GST_SOURCE_GROUP_UNLOCK (group); +- +- if (sinkpad == NULL) { +- /* Auxiliary stream, request a new pad from decodebin */ +- if ((sinkpad = gst_element_get_request_pad (playbin->decodebin, "sink_%u"))) { +- g_object_set_data (G_OBJECT (pad), "playbin.sinkpad", sinkpad); +- } +- } +- if (sinkpad) { +- GST_DEBUG_OBJECT (playbin, "New pad %" GST_PTR_FORMAT +- " from urisourcebin %" GST_PTR_FORMAT " linking to %" +- GST_PTR_FORMAT, pad, urisrc, sinkpad); +- +- res = gst_pad_link (pad, sinkpad); +- gst_object_unref (sinkpad); +- +- if (GST_PAD_LINK_FAILED (res)) +- goto link_failed; +- } +- return; +- +-link_failed: +- { +- GST_ERROR_OBJECT (playbin, +- "failed to link pad %s:%s to decodebin, reason %s (%d)", +- GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res); +- return; +- } +-} + + static void +-urisrc_pad_removed_cb (GstElement * urisrc, GstPad * pad, ++source_setup_cb (GstElement * element, GstElement * source, + GstSourceGroup * group) + { ++ g_signal_emit (group->playbin, gst_play_bin3_signals[SIGNAL_SOURCE_SETUP], 0, ++ source); + } + + /* must be called with PLAY_BIN_LOCK */ + static GstStateChangeReturn +-activate_decodebin (GstPlayBin3 * playbin, GstState target) +-{ +- GstStateChangeReturn state_ret; +- GstElement *decodebin = NULL; +- +- if (playbin->decodebin_active) +- return GST_STATE_CHANGE_SUCCESS; +- +- GST_LOG_OBJECT (playbin, "Adding and activating decodebin"); +- +- if (!make_or_reuse_element (playbin, "decodebin3", &playbin->decodebin)) +- goto no_decodebin; +- decodebin = playbin->decodebin; +- +- /* connect pads and other things */ +- playbin->db_pad_added_id = g_signal_connect (decodebin, "pad-added", +- G_CALLBACK (pad_added_cb), playbin); +- playbin->db_pad_removed_id = g_signal_connect (decodebin, "pad-removed", +- G_CALLBACK (pad_removed_cb), playbin); +- playbin->db_no_more_pads_id = g_signal_connect (decodebin, "no-more-pads", +- G_CALLBACK (no_more_pads_cb), playbin); +- playbin->db_select_stream_id = g_signal_connect (decodebin, "select-stream", +- G_CALLBACK (select_stream_cb), playbin); +- /* is called when the decodebin is out of data and we can switch to the +- * next uri */ +-#if 0 +- /* FIXME: Re-enable if/when decodebin3 supports 'drained' */ +- playbin->db_drained_id = +- g_signal_connect (decodebin, "drained", G_CALLBACK (drained_cb), playbin); +-#endif +- +- gst_element_set_locked_state (decodebin, TRUE); +- if ((state_ret = +- gst_element_set_state (decodebin, +- target)) == GST_STATE_CHANGE_FAILURE) +- goto decodebin_failure; +- gst_element_set_locked_state (decodebin, FALSE); +- +- playbin->decodebin_active = TRUE; +- +- return state_ret; +- +- +-no_decodebin: +- { +- GstMessage *msg; +- +- msg = +- gst_missing_element_message_new (GST_ELEMENT_CAST (playbin), +- "decodebin3"); +- gst_element_post_message (GST_ELEMENT_CAST (playbin), msg); +- +- GST_ELEMENT_ERROR (playbin, CORE, MISSING_PLUGIN, +- (_("Could not create \"decodebin3\" element.")), (NULL)); +- +- goto error_cleanup; +- } +-decodebin_failure: +- { +- GST_DEBUG_OBJECT (playbin, "failed state change of decodebin"); +- goto error_cleanup; +- } +-error_cleanup:{ +- if (decodebin) { +- REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_added_id); +- REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_removed_id); +- REMOVE_SIGNAL (playbin->decodebin, playbin->db_no_more_pads_id); +- REMOVE_SIGNAL (playbin->decodebin, playbin->db_drained_id); +- REMOVE_SIGNAL (playbin->decodebin, playbin->db_select_stream_id); +- gst_element_set_state (decodebin, GST_STATE_NULL); +- gst_bin_remove (GST_BIN_CAST (playbin), decodebin); +- } +- return GST_STATE_CHANGE_FAILURE; +- } +-} +- +-/* must be called with PLAY_BIN_LOCK */ +-static void +-deactivate_decodebin (GstPlayBin3 * playbin) +-{ +- if (playbin->decodebin) { +- GST_LOG_OBJECT (playbin, "Deactivating and removing decodebin"); +- REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_added_id); +- REMOVE_SIGNAL (playbin->decodebin, playbin->db_pad_removed_id); +- REMOVE_SIGNAL (playbin->decodebin, playbin->db_no_more_pads_id); +- REMOVE_SIGNAL (playbin->decodebin, playbin->db_drained_id); +- REMOVE_SIGNAL (playbin->decodebin, playbin->db_select_stream_id); +- gst_bin_remove (GST_BIN_CAST (playbin), playbin->decodebin); +- playbin->decodebin_active = FALSE; +- playbin->active_stream_types = 0; +- } +-} +- +-/* must be called with PLAY_BIN_LOCK */ +-static GstStateChangeReturn +-activate_group (GstPlayBin3 * playbin, GstSourceGroup * group, GstState target) ++activate_group (GstPlayBin3 * playbin, GstSourceGroup * group) + { +- GstElement *urisrcbin = NULL; +- GstElement *suburisrcbin = NULL; ++ GstElement *uridecodebin = NULL; + GstPlayFlags flags; + gboolean audio_sink_activated = FALSE; + gboolean video_sink_activated = FALSE; +@@ -4726,13 +4469,13 @@ activate_group (GstPlayBin3 * playbin, GstSourceGroup * group, GstState target) + } + + +- if (!make_or_reuse_element (playbin, "urisourcebin", &group->urisourcebin)) +- goto no_urisrcbin; +- urisrcbin = group->urisourcebin; ++ if (!make_or_reuse_element (playbin, "uridecodebin3", &group->uridecodebin)) ++ goto no_uridecodebin; ++ uridecodebin = group->uridecodebin; + + flags = gst_play_sink_get_flags (playbin->playsink); + +- g_object_set (urisrcbin, ++ g_object_set (uridecodebin, + /* configure connection speed */ + "connection-speed", playbin->connection_speed / 1000, + /* configure uri */ +@@ -4746,99 +4489,30 @@ activate_group (GstPlayBin3 * playbin, GstSourceGroup * group, GstState target) + "buffer-size", playbin->buffer_size, + "ring-buffer-max-size", playbin->ring_buffer_max_size, NULL); + +- /* we have 1 pending no-more-pads */ +- group->pending = 1; +- +- group->notify_source_id = g_signal_connect (urisrcbin, "notify::source", +- G_CALLBACK (notify_source_cb), group); +- +- /* will be called when a new media type is found. We return a list of decoders +- * including sinks for decodebin to try */ +- group->autoplug_factories_id = +- g_signal_connect (urisrcbin, "autoplug-factories", +- G_CALLBACK (autoplug_factories_cb), group); +- group->autoplug_select_id = +- g_signal_connect (urisrcbin, "autoplug-select", +- G_CALLBACK (autoplug_select_cb), group); +- group->autoplug_continue_id = +- g_signal_connect (urisrcbin, "autoplug-continue", +- G_CALLBACK (autoplug_continue_cb), group); +- group->autoplug_query_id = +- g_signal_connect (urisrcbin, "autoplug-query", +- G_CALLBACK (autoplug_query_cb), group); +- +- group->urisrc_pad_added_id = g_signal_connect (urisrcbin, "pad-added", +- G_CALLBACK (urisrc_pad_added), group); +- group->urisrc_pad_removed_id = g_signal_connect (urisrcbin, +- "pad-removed", G_CALLBACK (urisrc_pad_removed_cb), group); +- +- if (group->suburi) { +- /* subtitles */ +- if (!make_or_reuse_element (playbin, "urisourcebin", +- &group->suburisourcebin)) +- goto no_urisrcbin; +- suburisrcbin = group->suburisourcebin; +- +- g_object_set (suburisrcbin, +- /* configure connection speed */ +- "connection-speed", playbin->connection_speed, +- /* configure uri */ +- "uri", group->suburi, NULL); +- +- /* connect pads and other things */ +- group->sub_pad_added_id = g_signal_connect (suburisrcbin, "pad-added", +- G_CALLBACK (urisrc_pad_added), group); +- group->sub_pad_removed_id = g_signal_connect (suburisrcbin, +- "pad-removed", G_CALLBACK (urisrc_pad_removed_cb), group); +- +- group->sub_autoplug_continue_id = +- g_signal_connect (suburisrcbin, "autoplug-continue", +- G_CALLBACK (autoplug_continue_cb), group); +- +- group->sub_autoplug_query_id = +- g_signal_connect (suburisrcbin, "autoplug-query", +- G_CALLBACK (autoplug_query_cb), group); +- +- /* we have 2 pending no-more-pads */ +- group->pending = 2; +- group->sub_pending = TRUE; +- } else { +- group->sub_pending = FALSE; +- } ++ group->pad_added_id = g_signal_connect (uridecodebin, "pad-added", ++ G_CALLBACK (pad_added_cb), group); ++ group->pad_removed_id = g_signal_connect (uridecodebin, ++ "pad-removed", G_CALLBACK (pad_removed_cb), group); ++ group->select_stream_id = g_signal_connect (uridecodebin, "select-stream", ++ G_CALLBACK (select_stream_cb), group); ++ group->source_setup_id = g_signal_connect (uridecodebin, "source-setup", ++ G_CALLBACK (source_setup_cb), group); ++ group->about_to_finish_id = ++ g_signal_connect (uridecodebin, "about-to-finish", ++ G_CALLBACK (about_to_finish_cb), group); ++ ++ if (group->suburi) ++ g_object_set (group->uridecodebin, "suburi", group->suburi, NULL); + + /* release the group lock before setting the state of the source bins, they + * might fire signals in this thread that we need to handle with the + * group_lock taken. */ + GST_SOURCE_GROUP_UNLOCK (group); + +- if (suburisrcbin) { +- if (gst_element_set_state (suburisrcbin, +- target) == GST_STATE_CHANGE_FAILURE) { +- GST_DEBUG_OBJECT (playbin, +- "failed state change of subtitle urisourcebin"); +- GST_SOURCE_GROUP_LOCK (group); +- +- REMOVE_SIGNAL (suburisrcbin, group->sub_pad_added_id); +- REMOVE_SIGNAL (suburisrcbin, group->sub_pad_removed_id); +- REMOVE_SIGNAL (suburisrcbin, group->sub_autoplug_continue_id); +- REMOVE_SIGNAL (suburisrcbin, group->sub_autoplug_query_id); +- /* Might already be removed because of an error message */ +- if (GST_OBJECT_PARENT (suburisrcbin) == GST_OBJECT_CAST (playbin)) +- gst_bin_remove (GST_BIN_CAST (playbin), suburisrcbin); +- if (group->sub_pending) { +- group->pending--; +- group->sub_pending = FALSE; +- } +- gst_element_set_state (suburisrcbin, GST_STATE_READY); +- g_free (group->suburi); +- group->suburi = NULL; +- GST_SOURCE_GROUP_UNLOCK (group); +- } +- } + if ((state_ret = +- gst_element_set_state (urisrcbin, +- target)) == GST_STATE_CHANGE_FAILURE) +- goto urisrcbin_failure; ++ gst_element_set_state (uridecodebin, ++ GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) ++ goto uridecodebin_failure; + + GST_SOURCE_GROUP_LOCK (group); + /* allow state changes of the playbin affect the group elements now */ +@@ -4849,26 +4523,26 @@ activate_group (GstPlayBin3 * playbin, GstSourceGroup * group, GstState target) + return state_ret; + + /* ERRORS */ +-no_urisrcbin: ++no_uridecodebin: + { + GstMessage *msg; + + GST_SOURCE_GROUP_UNLOCK (group); + msg = + gst_missing_element_message_new (GST_ELEMENT_CAST (playbin), +- "urisourcebin"); ++ "uridecodebin3"); + gst_element_post_message (GST_ELEMENT_CAST (playbin), msg); + + GST_ELEMENT_ERROR (playbin, CORE, MISSING_PLUGIN, +- (_("Could not create \"urisourcebin\" element.")), (NULL)); ++ (_("Could not create \"uridecodebin3\" element.")), (NULL)); + + GST_SOURCE_GROUP_LOCK (group); + + goto error_cleanup; + } +-urisrcbin_failure: ++uridecodebin_failure: + { +- GST_DEBUG_OBJECT (playbin, "failed state change of urisrcbin"); ++ GST_DEBUG_OBJECT (playbin, "failed state change of uridecodebin"); + GST_SOURCE_GROUP_LOCK (group); + goto error_cleanup; + } +@@ -4880,6 +4554,8 @@ sink_failure: + + error_cleanup: + { ++ group->selected_stream_types = 0; ++ + /* delete any custom sinks we might have */ + if (group->audio_sink) { + /* If this is a automatically created sink set it to NULL */ +@@ -4905,17 +4581,21 @@ error_cleanup: + } + group->text_sink = NULL; + +- if (urisrcbin) { +- REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_added_id); +- REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_removed_id); +- REMOVE_SIGNAL (group->urisourcebin, group->notify_source_id); ++ if (uridecodebin) { ++ REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id); ++ REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id); ++ REMOVE_SIGNAL (group->uridecodebin, group->select_stream_id); ++ REMOVE_SIGNAL (group->uridecodebin, group->source_setup_id); ++ REMOVE_SIGNAL (group->uridecodebin, group->about_to_finish_id); ++#if 0 + REMOVE_SIGNAL (group->urisourcebin, group->autoplug_factories_id); + REMOVE_SIGNAL (group->urisourcebin, group->autoplug_select_id); + REMOVE_SIGNAL (group->urisourcebin, group->autoplug_continue_id); + REMOVE_SIGNAL (group->urisourcebin, group->autoplug_query_id); ++#endif + +- gst_element_set_state (urisrcbin, GST_STATE_NULL); +- gst_bin_remove (GST_BIN_CAST (playbin), urisrcbin); ++ gst_element_set_state (uridecodebin, GST_STATE_NULL); ++ gst_bin_remove (GST_BIN_CAST (playbin), uridecodebin); + } + + GST_SOURCE_GROUP_UNLOCK (group); +@@ -4924,13 +4604,10 @@ error_cleanup: + } + } + +-/* unlink a group of urisrcbin from the decodebin. +- * must be called with PLAY_BIN_LOCK */ ++/* must be called with PLAY_BIN_LOCK */ + static gboolean + deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group) + { +- gint i; +- + g_return_val_if_fail (group->active, FALSE); + g_return_val_if_fail (group->valid, FALSE); + +@@ -4938,32 +4615,17 @@ deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group) + + GST_SOURCE_GROUP_LOCK (group); + group->active = FALSE; +- for (i = 0; i < PLAYBIN_STREAM_LAST; i++) { +- GstSourceCombine *combine = &playbin->combiner[i]; +- +- GST_DEBUG_OBJECT (playbin, "unlinking combiner %s", combine->media_type); ++ group->playing = FALSE; ++ group->group_id = GST_GROUP_ID_INVALID; + +- if (combine->srcpad) { +- source_combine_remove_pads (playbin, combine); +- } +- +- if (combine->combiner) { +- gint n; +- +- /* release and unref requests pad from the combiner */ +- for (n = 0; n < combine->channels->len; n++) { +- GstPad *sinkpad = g_ptr_array_index (combine->channels, n); +- +- gst_element_release_request_pad (combine->combiner, sinkpad); +- gst_object_unref (sinkpad); +- } +- g_ptr_array_set_size (combine->channels, 0); ++ group->selected_stream_types = 0; ++ /* Update global selected_stream_types */ ++ playbin->selected_stream_types = ++ playbin->groups[0].selected_stream_types | playbin-> ++ groups[1].selected_stream_types; ++ if (playbin->active_stream_types != playbin->selected_stream_types) ++ reconfigure_output (playbin); + +- gst_element_set_state (combine->combiner, GST_STATE_NULL); +- gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner); +- combine->combiner = NULL; +- } +- } + #if 0 + /* delete any custom sinks we might have. + * conditionally set them to null if they aren't inside playsink yet */ +@@ -4993,30 +4655,28 @@ deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group) + group->text_sink = NULL; + #endif + +- if (group->urisourcebin) { +- REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_added_id); +- REMOVE_SIGNAL (group->urisourcebin, group->urisrc_pad_removed_id); +- REMOVE_SIGNAL (group->urisourcebin, group->notify_source_id); ++ if (group->uridecodebin) { ++ REMOVE_SIGNAL (group->uridecodebin, group->select_stream_id); ++ REMOVE_SIGNAL (group->uridecodebin, group->source_setup_id); ++ REMOVE_SIGNAL (group->uridecodebin, group->about_to_finish_id); ++ ++ gst_element_set_state (group->uridecodebin, GST_STATE_NULL); ++ gst_bin_remove (GST_BIN_CAST (playbin), group->uridecodebin); ++ ++ REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id); ++ REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id); ++#if 0 + REMOVE_SIGNAL (group->urisourcebin, group->autoplug_factories_id); + REMOVE_SIGNAL (group->urisourcebin, group->autoplug_select_id); + REMOVE_SIGNAL (group->urisourcebin, group->autoplug_continue_id); + REMOVE_SIGNAL (group->urisourcebin, group->autoplug_query_id); +- gst_bin_remove (GST_BIN_CAST (playbin), group->urisourcebin); +- } +- +- if (group->suburisourcebin) { +- REMOVE_SIGNAL (group->suburisourcebin, group->sub_pad_added_id); +- REMOVE_SIGNAL (group->suburisourcebin, group->sub_pad_removed_id); +- REMOVE_SIGNAL (group->suburisourcebin, group->sub_autoplug_continue_id); +- REMOVE_SIGNAL (group->suburisourcebin, group->sub_autoplug_query_id); +- +- /* Might already be removed because of errors */ +- if (GST_OBJECT_PARENT (group->suburisourcebin) == GST_OBJECT_CAST (playbin)) +- gst_bin_remove (GST_BIN_CAST (playbin), group->suburisourcebin); ++#endif + } + + GST_SOURCE_GROUP_UNLOCK (group); + ++ GST_DEBUG_OBJECT (playbin, "Done"); ++ + return TRUE; + } + +@@ -5024,55 +4684,36 @@ deactivate_group (GstPlayBin3 * playbin, GstSourceGroup * group) + * configured. It swaps out the current_group and activates the valid + * next_group. */ + static GstStateChangeReturn +-setup_next_source (GstPlayBin3 * playbin, GstState target) ++setup_next_source (GstPlayBin3 * playbin) + { +- GstSourceGroup *new_group, *old_group; ++ GstSourceGroup *new_group; + GstStateChangeReturn state_ret; + +- GST_DEBUG_OBJECT (playbin, "setup sources"); ++ GST_DEBUG_OBJECT (playbin, "setup next source"); ++ ++ debug_groups (playbin); + + /* see if there is a next group */ + GST_PLAY_BIN3_LOCK (playbin); + new_group = playbin->next_group; +- if (!new_group || !new_group->valid) ++ if (!new_group || !new_group->valid || new_group->active) + goto no_next_group; + +- /* first unlink the current source, if any */ +- old_group = playbin->curr_group; +- if (old_group && old_group->valid && old_group->active) { +- new_group->stream_changed_pending = TRUE; +- +- gst_play_bin3_update_cached_duration (playbin); +- /* unlink our pads with the sink */ +- deactivate_group (playbin, old_group); +- old_group->valid = FALSE; +- } +- +- /* swap old and new */ +- playbin->curr_group = new_group; +- playbin->next_group = old_group; +- +- /* Get decodebin ready now */ +- if ((state_ret = +- activate_decodebin (playbin, target)) == GST_STATE_CHANGE_FAILURE) +- goto activate_failed; +- + /* activate the new group */ +- if ((state_ret = +- activate_group (playbin, new_group, +- target)) == GST_STATE_CHANGE_FAILURE) ++ state_ret = activate_group (playbin, new_group); ++ if (state_ret == GST_STATE_CHANGE_FAILURE) + goto activate_failed; + + GST_PLAY_BIN3_UNLOCK (playbin); + ++ debug_groups (playbin); ++ + return state_ret; + + /* ERRORS */ + no_next_group: + { + GST_DEBUG_OBJECT (playbin, "no next group"); +- if (target == GST_STATE_READY && new_group && new_group->uri == NULL) +- GST_ELEMENT_ERROR (playbin, RESOURCE, NOT_FOUND, ("No URI set"), (NULL)); + GST_PLAY_BIN3_UNLOCK (playbin); + return GST_STATE_CHANGE_FAILURE; + } +@@ -5131,6 +4772,117 @@ groups_set_locked_state (GstPlayBin3 * playbin, gboolean locked) + return TRUE; + } + ++static void ++gst_play_bin3_check_group_status (GstPlayBin3 * playbin) ++{ ++ if (playbin->activation_task) ++ gst_task_start (playbin->activation_task); ++} ++ ++static void ++gst_play_bin3_activation_thread (GstPlayBin3 * playbin) ++{ ++ GST_DEBUG_OBJECT (playbin, "starting"); ++ ++ debug_groups (playbin); ++ ++ /* Check if next_group needs to be deactivated */ ++ GST_PLAY_BIN3_LOCK (playbin); ++ if (playbin->next_group->active) { ++ deactivate_group (playbin, playbin->next_group); ++ playbin->next_group->valid = FALSE; ++ } ++ ++ /* Is there a pending about-to-finish to be emitted ? */ ++ GST_SOURCE_GROUP_LOCK (playbin->curr_group); ++ if (playbin->curr_group->pending_about_to_finish) { ++ GST_LOG_OBJECT (playbin, "Propagating about-to-finish"); ++ playbin->curr_group->pending_about_to_finish = FALSE; ++ GST_SOURCE_GROUP_UNLOCK (playbin->curr_group); ++ /* This will activate the next source afterwards */ ++ emit_about_to_finish (playbin); ++ } else ++ GST_SOURCE_GROUP_UNLOCK (playbin->curr_group); ++ ++ GST_LOG_OBJECT (playbin, "Pausing task"); ++ if (playbin->activation_task) ++ gst_task_pause (playbin->activation_task); ++ GST_PLAY_BIN3_UNLOCK (playbin); ++ ++ GST_DEBUG_OBJECT (playbin, "done"); ++ return; ++} ++ ++static gboolean ++gst_play_bin3_start (GstPlayBin3 * playbin) ++{ ++ GST_DEBUG_OBJECT (playbin, "starting"); ++ ++ GST_PLAY_BIN3_LOCK (playbin); ++ ++ if (playbin->activation_task == NULL) { ++ playbin->activation_task = ++ gst_task_new ((GstTaskFunction) gst_play_bin3_activation_thread, ++ playbin, NULL); ++ if (playbin->activation_task == NULL) ++ goto task_error; ++ gst_task_set_lock (playbin->activation_task, &playbin->activation_lock); ++ } ++ GST_LOG_OBJECT (playbin, "clearing shutdown flag"); ++ g_atomic_int_set (&playbin->shutdown, 0); ++ do_async_start (playbin); ++ ++ GST_PLAY_BIN3_UNLOCK (playbin); ++ ++ return TRUE; ++ ++task_error: ++ { ++ GST_PLAY_BIN3_UNLOCK (playbin); ++ GST_ERROR_OBJECT (playbin, "Failed to create task"); ++ return FALSE; ++ } ++} ++ ++static void ++gst_play_bin3_stop (GstPlayBin3 * playbin) ++{ ++ GstTask *task; ++ ++ GST_DEBUG_OBJECT (playbin, "stopping"); ++ ++ /* FIXME unlock our waiting groups */ ++ GST_LOG_OBJECT (playbin, "setting shutdown flag"); ++ g_atomic_int_set (&playbin->shutdown, 1); ++ ++ /* wait for all callbacks to end by taking the lock. ++ * No dynamic (critical) new callbacks will ++ * be able to happen as we set the shutdown flag. */ ++ GST_PLAY_BIN3_DYN_LOCK (playbin); ++ GST_LOG_OBJECT (playbin, "dynamic lock taken, we can continue shutdown"); ++ GST_PLAY_BIN3_DYN_UNLOCK (playbin); ++ ++ /* Stop the activation task */ ++ GST_PLAY_BIN3_LOCK (playbin); ++ if ((task = playbin->activation_task)) { ++ playbin->activation_task = NULL; ++ GST_PLAY_BIN3_UNLOCK (playbin); ++ ++ gst_task_stop (task); ++ ++ /* Make sure task is not running */ ++ g_rec_mutex_lock (&playbin->activation_lock); ++ g_rec_mutex_unlock (&playbin->activation_lock); ++ ++ /* Wait for task to finish and unref it */ ++ gst_task_join (task); ++ gst_object_unref (task); ++ ++ GST_PLAY_BIN3_LOCK (playbin); ++ } ++ GST_PLAY_BIN3_UNLOCK (playbin); ++} ++ + static GstStateChangeReturn + gst_play_bin3_change_state (GstElement * element, GstStateChange transition) + { +@@ -5141,39 +4893,24 @@ gst_play_bin3_change_state (GstElement * element, GstStateChange transition) + playbin = GST_PLAY_BIN3 (element); + + switch (transition) { +- case GST_STATE_CHANGE_NULL_TO_READY: +- memset (&playbin->duration, 0, sizeof (playbin->duration)); +- break; + case GST_STATE_CHANGE_READY_TO_PAUSED: +- GST_LOG_OBJECT (playbin, "clearing shutdown flag"); +- memset (&playbin->duration, 0, sizeof (playbin->duration)); +- g_atomic_int_set (&playbin->shutdown, 0); +- do_async_start (playbin); ++ if (!gst_play_bin3_start (playbin)) ++ return GST_STATE_CHANGE_FAILURE; + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + async_down: +- /* FIXME unlock our waiting groups */ +- GST_LOG_OBJECT (playbin, "setting shutdown flag"); +- g_atomic_int_set (&playbin->shutdown, 1); +- memset (&playbin->duration, 0, sizeof (playbin->duration)); +- +- /* wait for all callbacks to end by taking the lock. +- * No dynamic (critical) new callbacks will +- * be able to happen as we set the shutdown flag. */ +- GST_PLAY_BIN3_DYN_LOCK (playbin); +- GST_LOG_OBJECT (playbin, "dynamic lock taken, we can continue shutdown"); +- GST_PLAY_BIN3_DYN_UNLOCK (playbin); ++ gst_play_bin3_stop (playbin); + if (!do_save) + break; + case GST_STATE_CHANGE_READY_TO_NULL: + /* we go async to PAUSED, so if that fails, we never make it to PAUSED +- * and no state change PAUSED to READY passes here, +- * though it is a nice-to-have ... */ ++ * and we will never be called with the GST_STATE_CHANGE_PAUSED_TO_READY. ++ * Make sure we do go through the same steps (see above) for ++ * proper cleanup */ + if (!g_atomic_int_get (&playbin->shutdown)) { + do_save = TRUE; + goto async_down; + } +- memset (&playbin->duration, 0, sizeof (playbin->duration)); + + /* unlock so that all groups go to NULL */ + groups_set_locked_state (playbin, FALSE); +@@ -5188,9 +4925,7 @@ gst_play_bin3_change_state (GstElement * element, GstStateChange transition) + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: +- if ((ret = +- setup_next_source (playbin, +- GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) ++ if ((ret = setup_next_source (playbin)) == GST_STATE_CHANGE_FAILURE) + goto failure; + if (ret == GST_STATE_CHANGE_SUCCESS) + ret = GST_STATE_CHANGE_ASYNC; +@@ -5206,40 +4941,24 @@ gst_play_bin3_change_state (GstElement * element, GstStateChange transition) + case GST_STATE_CHANGE_READY_TO_NULL: + { + guint i; +- GList *l; + + /* also do missed state change down to READY */ + if (do_save) + save_current_group (playbin); +- /* Deactive the groups, set the urisrcbins to NULL +- * and unref them. +- */ ++ /* Deactive the groups, set uridecodebin to NULL and unref it */ + for (i = 0; i < 2; i++) { + if (playbin->groups[i].active && playbin->groups[i].valid) { + deactivate_group (playbin, &playbin->groups[i]); + playbin->groups[i].valid = FALSE; + } + +- if (playbin->groups[i].urisourcebin) { +- gst_element_set_state (playbin->groups[i].urisourcebin, ++ if (playbin->groups[i].uridecodebin) { ++ gst_element_set_state (playbin->groups[i].uridecodebin, + GST_STATE_NULL); +- gst_object_unref (playbin->groups[i].urisourcebin); +- playbin->groups[i].urisourcebin = NULL; ++ gst_object_unref (playbin->groups[i].uridecodebin); ++ playbin->groups[i].uridecodebin = NULL; + } + +- if (playbin->groups[i].suburisourcebin) { +- gst_element_set_state (playbin->groups[i].suburisourcebin, +- GST_STATE_NULL); +- gst_object_unref (playbin->groups[i].suburisourcebin); +- playbin->groups[i].suburisourcebin = NULL; +- } +- } +- +- deactivate_decodebin (playbin); +- if (playbin->decodebin) { +- gst_object_unref (playbin->decodebin); +- playbin->decodebin = NULL; +- playbin->decodebin_active = FALSE; + } + + /* Set our sinks back to NULL, they might not be child of playbin */ +@@ -5260,31 +4979,6 @@ gst_play_bin3_change_state (GstElement * element, GstStateChange transition) + /* make sure the groups don't perform a state change anymore until we + * enable them again */ + groups_set_locked_state (playbin, TRUE); +- +- /* Remove all non-persistent contexts */ +- GST_OBJECT_LOCK (playbin); +- for (l = playbin->contexts; l;) { +- GstContext *context = l->data; +- +- if (!gst_context_is_persistent (context)) { +- GList *next; +- +- gst_context_unref (context); +- +- next = l->next; +- playbin->contexts = g_list_delete_link (playbin->contexts, l); +- l = next; +- } else { +- l = l->next; +- } +- } +- +- if (playbin->source) { +- gst_object_unref (playbin->source); +- playbin->source = NULL; +- } +- +- GST_OBJECT_UNLOCK (playbin); + break; + } + default: +diff --git a/gst/playback/gsturidecodebin3.c b/gst/playback/gsturidecodebin3.c +new file mode 100644 +index 000000000..8b1840d14 +--- /dev/null ++++ b/gst/playback/gsturidecodebin3.c +@@ -0,0 +1,1087 @@ ++/* GStreamer ++ * Copyright (C) <2007> Wim Taymans ++ * ++ * 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. ++ */ ++ ++/* TODO/FIXME: ++ * ++ * * BUFFERING MESSAGES ++ * ** How/Where do we deal with buffering messages from a new/prerolling ++ * source ? Ideally we want to re-use the same sourcebin ? ++ * ** Remember last buffering messages per source handler, if the SourceEntry ++ * group_id is the one being currently outputted on the source ghostpads, ++ * post the (last) buffering messages. ++ * If no group_id is being outputted (still prerolling), then output ++ * the messages directly ++ * ++ * * ASYNC HANDLING ++ * ** URIDECODEBIN3 is not async-aware. ++ * ++ * * GAPLESS HANDLING ++ * ** Correlate group_id and URI to know when/which stream is being outputted/started ++ */ ++ ++/** ++ * SECTION:element-uridecodebin3 ++ * @title: uridecodebin3 ++ * ++ * Decodes data from a URI into raw media. It selects a source element that can ++ * handle the given #GstURIDecodeBin3:uri scheme and connects it to a decodebin. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include ++ ++#include ++#include ++#include ++ ++#include "gstplay-enum.h" ++#include "gstrawcaps.h" ++#include "gstplayback.h" ++#include "gstplaybackutils.h" ++ ++#define GST_TYPE_URI_DECODE_BIN3 \ ++ (gst_uri_decode_bin3_get_type()) ++#define GST_URI_DECODE_BIN3(obj) \ ++ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3)) ++#define GST_URI_DECODE_BIN3_CLASS(klass) \ ++ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_URI_DECODE_BIN3,GstURIDecodeBin3Class)) ++#define GST_IS_URI_DECODE_BIN3(obj) \ ++ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_URI_DECODE_BIN3)) ++#define GST_IS_URI_DECODE_BIN3_CLASS(klass) \ ++ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_URI_DECODE_BIN3)) ++#define GST_URI_DECODE_BIN3_CAST(obj) ((GstURIDecodeBin3 *) (obj)) ++ ++typedef struct _GstSourceGroup GstSourceGroup; ++typedef struct _GstURIDecodeBin3 GstURIDecodeBin3; ++typedef struct _GstURIDecodeBin3Class GstURIDecodeBin3Class; ++ ++#define GST_URI_DECODE_BIN3_LOCK(dec) (g_mutex_lock(&((GstURIDecodeBin3*)(dec))->lock)) ++#define GST_URI_DECODE_BIN3_UNLOCK(dec) (g_mutex_unlock(&((GstURIDecodeBin3*)(dec))->lock)) ++ ++typedef struct _GstPlayItem GstPlayItem; ++typedef struct _GstSourceItem GstSourceItem; ++typedef struct _GstSourceHandler GstSourceHandler; ++typedef struct _OutputPad OutputPad; ++ ++/* A structure describing a play item, which travels through the elements ++ * over time. */ ++struct _GstPlayItem ++{ ++ GstURIDecodeBin3 *uridecodebin; ++ ++ /* Main URI */ ++ GstSourceItem *main_item; ++ ++ /* Auxiliary URI */ ++ /* FIXME : Replace by a list later */ ++ GstSourceItem *sub_item; ++ ++ /* The group_id used to identify this play item via STREAM_START events ++ * This is the group_id which will be used externally (i.e. rewritten ++ * to outgoing STREAM_START events and in emitted signals). ++ * The urisourcebin-specific group_id is located in GstSourceItem */ ++ guint group_id; ++ ++ /* Is this play item the one being currently outputted by decodebin3 ++ * and on our source ghostpads */ ++ gboolean currently_outputted; ++}; ++ ++struct _GstSourceItem ++{ ++ /* The GstPlayItem to which this GstSourceItem belongs to */ ++ GstPlayItem *play_item; ++ ++ gchar *uri; ++ ++ /* The urisourcebin controlling this uri ++ * Can be NULL */ ++ GstSourceHandler *handler; ++ ++ /* Last buffering information */ ++ gint last_perc; ++ GstMessage *last_buffering_message; ++ ++ /* The groupid created by urisourcebin for this uri */ ++ guint internal_groupid; ++ ++ /* FIXME : Add tag lists and other uri-specific items here ? */ ++}; ++ ++/* Structure wrapping everything related to a urisourcebin */ ++struct _GstSourceHandler ++{ ++ GstURIDecodeBin3 *uridecodebin; ++ ++ GstElement *urisourcebin; ++ ++ /* Signal handlers */ ++ gulong pad_added_id; ++ gulong pad_removed_id; ++ gulong source_setup_id; ++ gulong about_to_finish_id; ++ ++ /* TRUE if the controlled urisourcebin was added to uridecodebin */ ++ gboolean active; ++ ++ /* whether urisourcebin is drained or not. ++ * Reset if/when setting a new URI */ ++ gboolean drained; ++ ++ /* Whether urisourcebin posted EOS on all pads and ++ * there is no pending entry */ ++ gboolean is_eos; ++ ++ /* TRUE if the urisourcebin handles main item */ ++ gboolean is_main_source; ++ ++ /* buffering message stored for after switching */ ++ GstMessage *pending_buffering_msg; ++}; ++ ++/* Controls an output source pad */ ++struct _OutputPad ++{ ++ GstURIDecodeBin3 *uridecodebin; ++ ++ GstPad *target_pad; ++ GstPad *ghost_pad; ++ ++ /* Downstream event probe id */ ++ gulong probe_id; ++ ++ /* TRUE if the pad saw EOS. Resetted to FALSE on STREAM_START */ ++ gboolean is_eos; ++ ++ /* The last seen (i.e. current) group_id ++ * Can be (guint)-1 if no group_id was seen yet */ ++ guint current_group_id; ++}; ++ ++/** ++ * GstURIDecodeBin3 ++ * ++ * uridecodebin3 element struct ++ */ ++struct _GstURIDecodeBin3 ++{ ++ GstBin parent_instance; ++ ++ GMutex lock; /* lock for constructing */ ++ ++ /* Properties */ ++ GstElement *source; ++ guint64 connection_speed; /* In bits/sec (0 = unknown) */ ++ GstCaps *caps; ++ guint64 buffer_duration; /* When buffering, buffer duration (ns) */ ++ guint buffer_size; /* When buffering, buffer size (bytes) */ ++ gboolean download; ++ gboolean use_buffering; ++ guint64 ring_buffer_max_size; ++ ++ GList *play_items; /* List of GstPlayItem ordered by time of ++ * creation. Head of list is therefore the ++ * current (or pending if initial) one being ++ * outputted */ ++ GstPlayItem *current; /* Currently active GstPlayItem. Can be NULL ++ * if no entry is active yet (i.e. no source ++ * pads) */ ++ ++ /* sources. ++ * FIXME : Replace by a more modular system later on */ ++ GstSourceHandler *main_handler; ++ GstSourceHandler *sub_handler; ++ ++ /* URI handling ++ * FIXME : Switch to a playlist-based API */ ++ gchar *uri; ++ gboolean uri_changed; /* TRUE if uri changed */ ++ gchar *suburi; ++ gboolean suburi_changed; /* TRUE if suburi changed */ ++ ++ /* A global decodebin3 that's used to actually do decoding */ ++ GstElement *decodebin; ++ ++ /* db3 signals */ ++ gulong db_pad_added_id; ++ gulong db_pad_removed_id; ++ gulong db_select_stream_id; ++ gulong db_about_to_finish_id; ++ ++ GList *output_pads; /* List of OutputPad */ ++ ++ GList *source_handlers; /* List of SourceHandler */ ++ ++ /* Whether we already signalled about-to-finish or not ++ * FIXME: Track this by group-id ! */ ++ gboolean posted_about_to_finish; ++}; ++ ++struct _GstURIDecodeBin3Class ++{ ++ GstBinClass parent_class; ++ ++ gint (*select_stream) (GstURIDecodeBin3 * dbin, ++ GstStreamCollection * collection, GstStream * stream); ++}; ++ ++GST_DEBUG_CATEGORY_STATIC (gst_uri_decode_bin3_debug); ++#define GST_CAT_DEFAULT gst_uri_decode_bin3_debug ++ ++/* signals */ ++enum ++{ ++ SIGNAL_SELECT_STREAM, ++ SIGNAL_SOURCE_SETUP, ++ SIGNAL_ABOUT_TO_FINISH, ++ LAST_SIGNAL ++}; ++ ++#if 0 ++static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw(ANY)"); ++static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw(ANY)"); ++#endif ++ ++/* properties */ ++#define DEFAULT_PROP_URI NULL ++#define DEFAULT_PROP_SUBURI NULL ++#define DEFAULT_PROP_SOURCE NULL ++#define DEFAULT_CONNECTION_SPEED 0 ++#define DEFAULT_CAPS (gst_static_caps_get (&default_raw_caps)) ++#define DEFAULT_BUFFER_DURATION -1 ++#define DEFAULT_BUFFER_SIZE -1 ++#define DEFAULT_DOWNLOAD FALSE ++#define DEFAULT_USE_BUFFERING FALSE ++#define DEFAULT_RING_BUFFER_MAX_SIZE 0 ++ ++enum ++{ ++ PROP_0, ++ PROP_URI, ++ PROP_CURRENT_URI, ++ PROP_SUBURI, ++ PROP_CURRENT_SUBURI, ++ PROP_SOURCE, ++ PROP_CONNECTION_SPEED, ++ PROP_BUFFER_SIZE, ++ PROP_BUFFER_DURATION, ++ PROP_DOWNLOAD, ++ PROP_USE_BUFFERING, ++ PROP_RING_BUFFER_MAX_SIZE, ++ PROP_CAPS ++}; ++ ++static guint gst_uri_decode_bin3_signals[LAST_SIGNAL] = { 0 }; ++ ++static GstStaticCaps default_raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS); ++ ++static GstStaticPadTemplate video_src_template = ++GST_STATIC_PAD_TEMPLATE ("video_%u", ++ GST_PAD_SRC, ++ GST_PAD_SOMETIMES, ++ GST_STATIC_CAPS_ANY); ++ ++static GstStaticPadTemplate audio_src_template = ++GST_STATIC_PAD_TEMPLATE ("audio_%u", ++ GST_PAD_SRC, ++ GST_PAD_SOMETIMES, ++ GST_STATIC_CAPS_ANY); ++ ++static GstStaticPadTemplate text_src_template = ++GST_STATIC_PAD_TEMPLATE ("text_%u", ++ GST_PAD_SRC, ++ GST_PAD_SOMETIMES, ++ GST_STATIC_CAPS_ANY); ++ ++static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u", ++ GST_PAD_SRC, ++ GST_PAD_SOMETIMES, ++ GST_STATIC_CAPS_ANY); ++ ++GType gst_uri_decode_bin3_get_type (void); ++#define gst_uri_decode_bin3_parent_class parent_class ++G_DEFINE_TYPE (GstURIDecodeBin3, gst_uri_decode_bin3, GST_TYPE_BIN); ++ ++#define REMOVE_SIGNAL(obj,id) \ ++if (id) { \ ++ g_signal_handler_disconnect (obj, id); \ ++ id = 0; \ ++} ++ ++static void gst_uri_decode_bin3_set_property (GObject * object, guint prop_id, ++ const GValue * value, GParamSpec * pspec); ++static void gst_uri_decode_bin3_get_property (GObject * object, guint prop_id, ++ GValue * value, GParamSpec * pspec); ++static void gst_uri_decode_bin3_finalize (GObject * obj); ++ ++static GstStateChangeReturn gst_uri_decode_bin3_change_state (GstElement * ++ element, GstStateChange transition); ++ ++static gboolean ++_gst_int_accumulator (GSignalInvocationHint * ihint, ++ GValue * return_accu, const GValue * handler_return, gpointer dummy) ++{ ++ gint res = g_value_get_int (handler_return); ++ ++ if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) ++ g_value_set_int (return_accu, res); ++ ++ if (res == -1) ++ return TRUE; ++ ++ return FALSE; ++} ++ ++ ++static void ++gst_uri_decode_bin3_class_init (GstURIDecodeBin3Class * klass) ++{ ++ GObjectClass *gobject_class; ++ GstElementClass *gstelement_class; ++ ++ gobject_class = G_OBJECT_CLASS (klass); ++ gstelement_class = GST_ELEMENT_CLASS (klass); ++ ++ gobject_class->set_property = gst_uri_decode_bin3_set_property; ++ gobject_class->get_property = gst_uri_decode_bin3_get_property; ++ gobject_class->finalize = gst_uri_decode_bin3_finalize; ++ ++ g_object_class_install_property (gobject_class, PROP_URI, ++ g_param_spec_string ("uri", "URI", "URI to decode", ++ DEFAULT_PROP_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property (gobject_class, PROP_CURRENT_URI, ++ g_param_spec_string ("current-uri", "Current URI", ++ "The currently playing URI", NULL, ++ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property (gobject_class, PROP_SUBURI, ++ g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle", ++ NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property (gobject_class, PROP_CURRENT_SUBURI, ++ g_param_spec_string ("current-suburi", "Current .sub-URI", ++ "The currently playing URI of a subtitle", ++ NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property (gobject_class, PROP_SOURCE, ++ g_param_spec_object ("source", "Source", "Source object used", ++ GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property (gobject_class, PROP_CONNECTION_SPEED, ++ g_param_spec_uint64 ("connection-speed", "Connection Speed", ++ "Network connection speed in kbps (0 = unknown)", ++ 0, G_MAXUINT64 / 1000, DEFAULT_CONNECTION_SPEED, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property (gobject_class, PROP_BUFFER_SIZE, ++ g_param_spec_int ("buffer-size", "Buffer size (bytes)", ++ "Buffer size when buffering streams (-1 default value)", ++ -1, G_MAXINT, DEFAULT_BUFFER_SIZE, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property (gobject_class, PROP_BUFFER_DURATION, ++ g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)", ++ "Buffer duration when buffering streams (-1 default value)", ++ -1, G_MAXINT64, DEFAULT_BUFFER_DURATION, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ ++ /** ++ * GstURIDecodeBin3::download: ++ * ++ * For certain media type, enable download buffering. ++ */ ++ g_object_class_install_property (gobject_class, PROP_DOWNLOAD, ++ g_param_spec_boolean ("download", "Download", ++ "Attempt download buffering when buffering network streams", ++ DEFAULT_DOWNLOAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ /** ++ * GstURIDecodeBin3::use-buffering: ++ * ++ * Emit BUFFERING messages based on low-/high-percent thresholds of the ++ * demuxed or parsed data. ++ * When download buffering is activated and used for the current media ++ * type, this property does nothing. Otherwise perform buffering on the ++ * demuxed or parsed media. ++ */ ++ g_object_class_install_property (gobject_class, PROP_USE_BUFFERING, ++ g_param_spec_boolean ("use-buffering", "Use Buffering", ++ "Perform buffering on demuxed/parsed media", ++ DEFAULT_USE_BUFFERING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ ++ /** ++ * GstURIDecodeBin3::ring-buffer-max-size ++ * ++ * The maximum size of the ring buffer in kilobytes. If set to 0, the ring ++ * buffer is disabled. Default is 0. ++ */ ++ g_object_class_install_property (gobject_class, PROP_RING_BUFFER_MAX_SIZE, ++ g_param_spec_uint64 ("ring-buffer-max-size", ++ "Max. ring buffer size (bytes)", ++ "Max. amount of data in the ring buffer (bytes, 0 = ring buffer disabled)", ++ 0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE, ++ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ ++ g_object_class_install_property (gobject_class, PROP_CAPS, ++ g_param_spec_boxed ("caps", "Caps", ++ "The caps on which to stop decoding. (NULL = default)", ++ GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); ++ ++ /** ++ * GstURIDecodebin3::select-stream ++ * @decodebin: a #GstURIDecodebin3 ++ * @collection: a #GstStreamCollection ++ * @stream: a #GstStream ++ * ++ * This signal is emitted whenever @decodebin needs to decide whether ++ * to expose a @stream of a given @collection. ++ * ++ * Returns: 1 if the stream should be selected, 0 if it shouldn't be selected. ++ * A value of -1 (default) lets @decodebin decide what to do with the stream. ++ * */ ++ gst_uri_decode_bin3_signals[SIGNAL_SELECT_STREAM] = ++ g_signal_new ("select-stream", G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURIDecodeBin3Class, select_stream), ++ _gst_int_accumulator, NULL, g_cclosure_marshal_generic, ++ G_TYPE_INT, 2, GST_TYPE_STREAM_COLLECTION, GST_TYPE_STREAM); ++ ++ /** ++ * GstURIDecodeBin3::source-setup: ++ * @bin: the uridecodebin. ++ * @source: source element ++ * ++ * This signal is emitted after a source element has been created, so ++ * it can be configured by setting additional properties (e.g. set a ++ * proxy server for an http source, or set the device and read speed for ++ * an audio cd source). ++ */ ++ gst_uri_decode_bin3_signals[SIGNAL_SOURCE_SETUP] = ++ g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, 0, NULL, NULL, ++ g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_ELEMENT); ++ /** ++ * GstURIDecodeBin3::about-to-finish: ++ * ++ * This signal is emitted when the data for the selected URI is ++ * entirely buffered and it is safe to specify anothe URI. ++ */ ++ gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH] = ++ g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, ++ 0, G_TYPE_NONE); ++ ++ ++ gst_element_class_add_static_pad_template (gstelement_class, ++ &video_src_template); ++ gst_element_class_add_static_pad_template (gstelement_class, ++ &audio_src_template); ++ gst_element_class_add_static_pad_template (gstelement_class, ++ &text_src_template); ++ gst_element_class_add_static_pad_template (gstelement_class, &src_template); ++ gst_element_class_set_static_metadata (gstelement_class, ++ "URI Decoder", "Generic/Bin/Decoder", ++ "Autoplug and decode an URI to raw media", ++ "Edward Hervey , Jan Schmidt "); ++ ++ gstelement_class->change_state = gst_uri_decode_bin3_change_state; ++ ++} ++ ++static GstPadProbeReturn ++db_src_probe (GstPad * pad, GstPadProbeInfo * info, OutputPad * output) ++{ ++ /* FIXME : IMPLEMENT */ ++ ++ /* EOS : Mark pad as EOS */ ++ ++ /* STREAM_START : Store group_id and check if currently active ++ * PlayEntry changed */ ++ ++ return GST_PAD_PROBE_OK; ++} ++ ++static OutputPad * ++add_output_pad (GstURIDecodeBin3 * dec, GstPad * target_pad) ++{ ++ OutputPad *output; ++ gchar *pad_name; ++ ++ output = g_slice_new0 (OutputPad); ++ ++ GST_LOG_OBJECT (dec, "Created output %p", output); ++ ++ output->uridecodebin = dec; ++ output->target_pad = target_pad; ++ output->current_group_id = (guint) - 1; ++ pad_name = gst_pad_get_name (target_pad); ++ output->ghost_pad = gst_ghost_pad_new (pad_name, target_pad); ++ g_free (pad_name); ++ ++ gst_pad_set_active (output->ghost_pad, TRUE); ++ gst_element_add_pad (GST_ELEMENT (dec), output->ghost_pad); ++ ++ output->probe_id = ++ gst_pad_add_probe (output->target_pad, ++ GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, (GstPadProbeCallback) db_src_probe, ++ output, NULL); ++ ++ /* FIXME: LOCK TO PROTECT PAD LIST */ ++ dec->output_pads = g_list_append (dec->output_pads, output); ++ ++ return output; ++} ++ ++static void ++db_pad_added_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec) ++{ ++ GST_DEBUG_OBJECT (dec, "Wrapping new pad %s:%s", GST_DEBUG_PAD_NAME (pad)); ++ ++ if (GST_PAD_IS_SRC (pad)) ++ add_output_pad (dec, pad); ++} ++ ++static void ++db_pad_removed_cb (GstElement * element, GstPad * pad, GstURIDecodeBin3 * dec) ++{ ++ GList *tmp; ++ OutputPad *output = NULL; ++ ++ if (!GST_PAD_IS_SRC (pad)) ++ return; ++ ++ GST_DEBUG_OBJECT (dec, "pad %s:%s", GST_DEBUG_PAD_NAME (pad)); ++ /* FIXME: LOCK for list access */ ++ ++ for (tmp = dec->output_pads; tmp; tmp = tmp->next) { ++ OutputPad *cand = (OutputPad *) tmp->data; ++ ++ if (cand->target_pad == pad) { ++ output = cand; ++ dec->output_pads = g_list_delete_link (dec->output_pads, tmp); ++ break; ++ } ++ } ++ ++ if (output) { ++ GST_LOG_OBJECT (element, "Removing output %p", output); ++ /* Remove source ghost pad */ ++ gst_ghost_pad_set_target ((GstGhostPad *) output->ghost_pad, NULL); ++ gst_element_remove_pad ((GstElement *) dec, output->ghost_pad); ++ ++ /* FIXME : Update global/current PlayEntry group_id (did we switch ?) */ ++ ++ /* Remove event probe */ ++ gst_pad_remove_probe (output->target_pad, output->probe_id); ++ ++ g_slice_free (OutputPad, output); ++ } ++} ++ ++static gint ++db_select_stream_cb (GstElement * decodebin, ++ GstStreamCollection * collection, GstStream * stream, ++ GstURIDecodeBin3 * uridecodebin) ++{ ++ gint response = -1; ++ ++ g_signal_emit (uridecodebin, ++ gst_uri_decode_bin3_signals[SIGNAL_SELECT_STREAM], 0, collection, stream, ++ &response); ++ return response; ++} ++ ++static void ++db_about_to_finish_cb (GstElement * decodebin, GstURIDecodeBin3 * uridecodebin) ++{ ++ if (!uridecodebin->posted_about_to_finish) { ++ uridecodebin->posted_about_to_finish = TRUE; ++ g_signal_emit (uridecodebin, ++ gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL); ++ } ++} ++ ++static void ++gst_uri_decode_bin3_init (GstURIDecodeBin3 * dec) ++{ ++ g_mutex_init (&dec->lock); ++ ++ dec->caps = gst_static_caps_get (&default_raw_caps); ++ ++ dec->decodebin = gst_element_factory_make ("decodebin3", NULL); ++ gst_bin_add (GST_BIN_CAST (dec), dec->decodebin); ++ dec->db_pad_added_id = ++ g_signal_connect (dec->decodebin, "pad-added", ++ G_CALLBACK (db_pad_added_cb), dec); ++ dec->db_pad_removed_id = ++ g_signal_connect (dec->decodebin, "pad-removed", ++ G_CALLBACK (db_pad_removed_cb), dec); ++ dec->db_select_stream_id = ++ g_signal_connect (dec->decodebin, "select-stream", ++ G_CALLBACK (db_select_stream_cb), dec); ++ dec->db_about_to_finish_id = ++ g_signal_connect (dec->decodebin, "about-to-finish", ++ G_CALLBACK (db_about_to_finish_cb), dec); ++ ++ GST_OBJECT_FLAG_SET (dec, GST_ELEMENT_FLAG_SOURCE); ++ gst_bin_set_suppressed_flags (GST_BIN (dec), ++ GST_ELEMENT_FLAG_SOURCE | GST_ELEMENT_FLAG_SINK); ++} ++ ++static void ++gst_uri_decode_bin3_finalize (GObject * obj) ++{ ++ GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (obj); ++ ++ g_mutex_clear (&dec->lock); ++ g_free (dec->uri); ++ g_free (dec->suburi); ++ ++ G_OBJECT_CLASS (parent_class)->finalize (obj); ++} ++ ++static GstStateChangeReturn ++activate_source_item (GstSourceItem * item) ++{ ++ GstSourceHandler *handler = item->handler; ++ ++ if (handler == NULL) { ++ GST_WARNING ("Can't activate item without a handler"); ++ return GST_STATE_CHANGE_FAILURE; ++ } ++ ++ g_object_set (handler->urisourcebin, "uri", item->uri, NULL); ++ if (!handler->active) { ++ gst_bin_add ((GstBin *) handler->uridecodebin, handler->urisourcebin); ++ /* if (!gst_element_sync_state_with_parent (handler->urisourcebin)) */ ++ /* return GST_STATE_CHANGE_FAILURE; */ ++ handler->active = TRUE; ++ } ++ ++ return GST_STATE_CHANGE_SUCCESS; ++} ++ ++static void ++src_pad_added_cb (GstElement * element, GstPad * pad, ++ GstSourceHandler * handler) ++{ ++ GstURIDecodeBin3 *uridecodebin; ++ GstPad *sinkpad = NULL; ++ GstPadLinkReturn res; ++ ++ uridecodebin = handler->uridecodebin; ++ ++ GST_DEBUG_OBJECT (uridecodebin, ++ "New pad %" GST_PTR_FORMAT " from source %" GST_PTR_FORMAT, pad, element); ++ ++ /* FIXME: Add probe to unify group_id and detect EOS */ ++ ++ /* Try to link to main sink pad only if it's from a main handler */ ++ if (handler->is_main_source) { ++ sinkpad = gst_element_get_static_pad (uridecodebin->decodebin, "sink"); ++ if (gst_pad_is_linked (sinkpad)) { ++ gst_object_unref (sinkpad); ++ sinkpad = NULL; ++ } ++ } ++ ++ if (sinkpad == NULL) ++ sinkpad = gst_element_get_request_pad (uridecodebin->decodebin, "sink_%u"); ++ ++ if (sinkpad) { ++ GST_DEBUG_OBJECT (uridecodebin, ++ "Linking %" GST_PTR_FORMAT " to %" GST_PTR_FORMAT, pad, sinkpad); ++ res = gst_pad_link (pad, sinkpad); ++ gst_object_unref (sinkpad); ++ if (GST_PAD_LINK_FAILED (res)) ++ goto link_failed; ++ } ++ return; ++ ++link_failed: ++ { ++ GST_ERROR_OBJECT (uridecodebin, ++ "failed to link pad %s:%s to decodebin, reason %s (%d)", ++ GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res); ++ return; ++ } ++} ++ ++static void ++src_pad_removed_cb (GstElement * element, GstPad * pad, ++ GstSourceHandler * handler) ++{ ++ /* FIXME : IMPLEMENT */ ++} ++ ++static void ++src_source_setup_cb (GstElement * element, GstElement * source, ++ GstSourceHandler * handler) ++{ ++ g_signal_emit (handler->uridecodebin, ++ gst_uri_decode_bin3_signals[SIGNAL_SOURCE_SETUP], 0, source, NULL); ++} ++ ++static void ++src_about_to_finish_cb (GstElement * element, GstSourceHandler * handler) ++{ ++ /* FIXME : check if all sources are done */ ++ if (!handler->uridecodebin->posted_about_to_finish) { ++ handler->uridecodebin->posted_about_to_finish = TRUE; ++ g_signal_emit (handler->uridecodebin, ++ gst_uri_decode_bin3_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL); ++ } ++} ++ ++static GstSourceHandler * ++new_source_handler (GstURIDecodeBin3 * uridecodebin, gboolean is_main) ++{ ++ GstSourceHandler *handler; ++ ++ handler = g_slice_new0 (GstSourceHandler); ++ ++ handler->uridecodebin = uridecodebin; ++ handler->is_main_source = is_main; ++ handler->urisourcebin = gst_element_factory_make ("urisourcebin", NULL); ++ /* Set pending properties */ ++ g_object_set (handler->urisourcebin, ++ "connection-speed", uridecodebin->connection_speed / 1000, ++ "download", uridecodebin->download, ++ "use-buffering", uridecodebin->use_buffering, ++ "buffer-duration", uridecodebin->buffer_duration, ++ "buffer-size", uridecodebin->buffer_size, ++ "ring-buffer-max-size", uridecodebin->ring_buffer_max_size, NULL); ++ ++ handler->pad_added_id = ++ g_signal_connect (handler->urisourcebin, "pad-added", ++ (GCallback) src_pad_added_cb, handler); ++ handler->pad_removed_id = ++ g_signal_connect (handler->urisourcebin, "pad-removed", ++ (GCallback) src_pad_removed_cb, handler); ++ handler->source_setup_id = ++ g_signal_connect (handler->urisourcebin, "source-setup", ++ (GCallback) src_source_setup_cb, handler); ++ handler->about_to_finish_id = ++ g_signal_connect (handler->urisourcebin, "about-to-finish", ++ (GCallback) src_about_to_finish_cb, handler); ++ ++ uridecodebin->source_handlers = ++ g_list_append (uridecodebin->source_handlers, handler); ++ ++ return handler; ++} ++ ++static void ++gst_uri_decode_bin3_set_property (GObject * object, guint prop_id, ++ const GValue * value, GParamSpec * pspec) ++{ ++ GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (object); ++ ++ switch (prop_id) { ++ case PROP_URI: ++ if (dec->uri) ++ g_free (dec->uri); ++ dec->uri = g_value_dup_string (value); ++ break; ++ case PROP_SUBURI: ++ if (dec->suburi) ++ g_free (dec->suburi); ++ dec->suburi = g_value_dup_string (value); ++ break; ++ case PROP_CONNECTION_SPEED: ++ GST_URI_DECODE_BIN3_LOCK (dec); ++ dec->connection_speed = g_value_get_uint64 (value) * 1000; ++ GST_URI_DECODE_BIN3_UNLOCK (dec); ++ break; ++ case PROP_BUFFER_SIZE: ++ dec->buffer_size = g_value_get_int (value); ++ break; ++ case PROP_BUFFER_DURATION: ++ dec->buffer_duration = g_value_get_int64 (value); ++ break; ++ case PROP_DOWNLOAD: ++ dec->download = g_value_get_boolean (value); ++ break; ++ case PROP_USE_BUFFERING: ++ dec->use_buffering = g_value_get_boolean (value); ++ break; ++ case PROP_RING_BUFFER_MAX_SIZE: ++ dec->ring_buffer_max_size = g_value_get_uint64 (value); ++ break; ++ case PROP_CAPS: ++ GST_OBJECT_LOCK (dec); ++ if (dec->caps) ++ gst_caps_unref (dec->caps); ++ dec->caps = g_value_dup_boxed (value); ++ GST_OBJECT_UNLOCK (dec); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++gst_uri_decode_bin3_get_property (GObject * object, guint prop_id, ++ GValue * value, GParamSpec * pspec) ++{ ++ GstURIDecodeBin3 *dec = GST_URI_DECODE_BIN3 (object); ++ ++ switch (prop_id) { ++ case PROP_URI: ++ { ++ g_value_set_string (value, dec->uri); ++ break; ++ } ++ case PROP_CURRENT_URI: ++ { ++ g_value_set_string (value, dec->suburi); ++ break; ++ } ++ case PROP_SUBURI: ++ { ++ /* FIXME : Return current uri */ ++ g_value_set_string (value, dec->uri); ++ break; ++ } ++ case PROP_CURRENT_SUBURI: ++ { ++ /* FIXME : Return current suburi */ ++ g_value_set_string (value, dec->suburi); ++ break; ++ } ++ case PROP_SOURCE: ++ { ++ GST_OBJECT_LOCK (dec); ++ g_value_set_object (value, dec->source); ++ GST_OBJECT_UNLOCK (dec); ++ break; ++ } ++ case PROP_CONNECTION_SPEED: ++ GST_URI_DECODE_BIN3_LOCK (dec); ++ g_value_set_uint64 (value, dec->connection_speed / 1000); ++ GST_URI_DECODE_BIN3_UNLOCK (dec); ++ break; ++ case PROP_BUFFER_SIZE: ++ GST_OBJECT_LOCK (dec); ++ g_value_set_int (value, dec->buffer_size); ++ GST_OBJECT_UNLOCK (dec); ++ break; ++ case PROP_BUFFER_DURATION: ++ GST_OBJECT_LOCK (dec); ++ g_value_set_int64 (value, dec->buffer_duration); ++ GST_OBJECT_UNLOCK (dec); ++ break; ++ case PROP_DOWNLOAD: ++ g_value_set_boolean (value, dec->download); ++ break; ++ case PROP_USE_BUFFERING: ++ g_value_set_boolean (value, dec->use_buffering); ++ break; ++ case PROP_RING_BUFFER_MAX_SIZE: ++ g_value_set_uint64 (value, dec->ring_buffer_max_size); ++ break; ++ case PROP_CAPS: ++ GST_OBJECT_LOCK (dec); ++ g_value_set_boxed (value, dec->caps); ++ GST_OBJECT_UNLOCK (dec); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++free_source_handler (GstURIDecodeBin3 * uridecodebin, ++ GstSourceHandler * handler) ++{ ++ GST_LOG_OBJECT (uridecodebin, "source handler %p", handler); ++ if (handler->active) { ++ GST_LOG_OBJECT (uridecodebin, "Removing %" GST_PTR_FORMAT, ++ handler->urisourcebin); ++ gst_element_set_state (handler->urisourcebin, GST_STATE_NULL); ++ gst_bin_remove ((GstBin *) uridecodebin, handler->urisourcebin); ++ } ++ uridecodebin->source_handlers = ++ g_list_remove (uridecodebin->source_handlers, handler); ++ g_slice_free (GstSourceHandler, handler); ++} ++ ++static GstSourceItem * ++new_source_item (GstURIDecodeBin3 * dec, GstPlayItem * item, gchar * uri) ++{ ++ GstSourceItem *sourceitem = g_slice_new0 (GstSourceItem); ++ ++ sourceitem->play_item = item; ++ sourceitem->uri = uri; ++ ++ return sourceitem; ++} ++ ++static void ++free_source_item (GstURIDecodeBin3 * uridecodebin, GstSourceItem * item) ++{ ++ GST_LOG_OBJECT (uridecodebin, "source item %p", item); ++ if (item->handler) ++ free_source_handler (uridecodebin, item->handler); ++ g_slice_free (GstSourceItem, item); ++} ++ ++static GstPlayItem * ++new_play_item (GstURIDecodeBin3 * dec, gchar * uri, gchar * suburi) ++{ ++ GstPlayItem *item = g_slice_new0 (GstPlayItem); ++ ++ item->uridecodebin = dec; ++ item->main_item = new_source_item (dec, item, uri); ++ if (suburi) ++ item->sub_item = new_source_item (dec, item, suburi); ++ ++ return item; ++} ++ ++static void ++free_play_item (GstURIDecodeBin3 * dec, GstPlayItem * item) ++{ ++ GST_LOG_OBJECT (dec, "play item %p", item); ++ if (item->main_item) ++ free_source_item (dec, item->main_item); ++ if (item->sub_item) ++ free_source_item (dec, item->sub_item); ++ ++ g_slice_free (GstPlayItem, item); ++} ++ ++/* Sync source handlers for the given play item. Might require creating/removing some ++ * and/or configure the handlers accordingly */ ++static GstStateChangeReturn ++assign_handlers_to_item (GstURIDecodeBin3 * dec, GstPlayItem * item) ++{ ++ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; ++ ++ /* FIXME : Go over existing handlers to see if we can assign some to the ++ * given item */ ++ ++ /* Create missing handlers */ ++ if (item->main_item->handler == NULL) { ++ item->main_item->handler = new_source_handler (dec, TRUE); ++ ret = activate_source_item (item->main_item); ++ if (ret == GST_STATE_CHANGE_FAILURE) ++ return ret; ++ } ++ ++ if (item->sub_item && item->sub_item->handler) { ++ item->sub_item->handler = new_source_handler (dec, FALSE); ++ ret = activate_source_item (item->sub_item); ++ } ++ ++ return ret; ++} ++ ++/* Called to activate the next play item */ ++static GstStateChangeReturn ++activate_next_play_item (GstURIDecodeBin3 * dec) ++{ ++ GstPlayItem *item; ++ GstStateChangeReturn ret; ++ ++ /* If there is no current play entry, create one from the uri/suburi ++ * FIXME : Use a playlist API in the future */ ++ item = new_play_item (dec, dec->uri, dec->suburi); ++ ++ ret = assign_handlers_to_item (dec, item); ++ if (ret == GST_STATE_CHANGE_FAILURE) { ++ free_play_item (dec, item); ++ return ret; ++ } ++ ++ dec->play_items = g_list_append (dec->play_items, item); ++ ++ return ret; ++} ++ ++static void ++free_play_items (GstURIDecodeBin3 * dec) ++{ ++ GList *tmp; ++ ++ for (tmp = dec->play_items; tmp; tmp = tmp->next) { ++ GstPlayItem *item = (GstPlayItem *) tmp->data; ++ free_play_item (dec, item); ++ } ++ ++ g_list_free (dec->play_items); ++ dec->play_items = NULL; ++} ++ ++static GstStateChangeReturn ++gst_uri_decode_bin3_change_state (GstElement * element, ++ GstStateChange transition) ++{ ++ GstStateChangeReturn ret; ++ GstURIDecodeBin3 *uridecodebin = (GstURIDecodeBin3 *) element; ++ ++ switch (transition) { ++ case GST_STATE_CHANGE_READY_TO_PAUSED: ++ ret = activate_next_play_item (uridecodebin); ++ if (ret == GST_STATE_CHANGE_FAILURE) ++ goto failure; ++ default: ++ break; ++ } ++ ++ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); ++ if (ret == GST_STATE_CHANGE_FAILURE) ++ goto failure; ++ ++ switch (transition) { ++ case GST_STATE_CHANGE_PAUSED_TO_READY: ++ /* FIXME: Cleanup everything */ ++ free_play_items (uridecodebin); ++ /* Free play item */ ++ uridecodebin->posted_about_to_finish = FALSE; ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++ ++ /* ERRORS */ ++failure: ++ { ++ if (transition == GST_STATE_CHANGE_READY_TO_PAUSED) ++ free_play_items (uridecodebin); ++ return ret; ++ } ++} ++ ++ ++gboolean ++gst_uri_decode_bin3_plugin_init (GstPlugin * plugin) ++{ ++ GST_DEBUG_CATEGORY_INIT (gst_uri_decode_bin3_debug, "uridecodebin3", 0, ++ "URI decoder element 3"); ++ ++ return gst_element_register (plugin, "uridecodebin3", GST_RANK_NONE, ++ GST_TYPE_URI_DECODE_BIN3); ++} +diff --git a/gst/playback/gsturisourcebin.c b/gst/playback/gsturisourcebin.c +index 7720a4c7a..27138523b 100644 +--- a/gst/playback/gsturisourcebin.c ++++ b/gst/playback/gsturisourcebin.c +@@ -20,6 +20,7 @@ + + /** + * SECTION:element-urisourcebin ++ * @title: urisourcebin + * + * urisourcebin is an element for accessing URIs in a uniform manner. + * +@@ -52,16 +53,16 @@ + #include "gstplayback.h" + #include "gstplaybackutils.h" + +-#define GST_TYPE_URI_DECODE_BIN \ ++#define GST_TYPE_URI_SOURCE_BIN \ + (gst_uri_source_bin_get_type()) + #define GST_URI_SOURCE_BIN(obj) \ +- (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_URI_DECODE_BIN,GstURISourceBin)) ++ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_URI_SOURCE_BIN,GstURISourceBin)) + #define GST_URI_SOURCE_BIN_CLASS(klass) \ +- (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_URI_DECODE_BIN,GstURISourceBinClass)) ++ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_URI_SOURCE_BIN,GstURISourceBinClass)) + #define GST_IS_URI_SOURCE_BIN(obj) \ +- (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_URI_DECODE_BIN)) ++ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_URI_SOURCE_BIN)) + #define GST_IS_URI_SOURCE_BIN_CLASS(klass) \ +- (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_URI_DECODE_BIN)) ++ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_URI_SOURCE_BIN)) + #define GST_URI_SOURCE_BIN_CAST(obj) ((GstURISourceBin *) (obj)) + + typedef struct _GstURISourceBin GstURISourceBin; +@@ -72,6 +73,23 @@ typedef struct _OutputSlotInfo OutputSlotInfo; + #define GST_URI_SOURCE_BIN_LOCK(dec) (g_mutex_lock(&((GstURISourceBin*)(dec))->lock)) + #define GST_URI_SOURCE_BIN_UNLOCK(dec) (g_mutex_unlock(&((GstURISourceBin*)(dec))->lock)) + ++#define BUFFERING_LOCK(ubin) G_STMT_START { \ ++ GST_LOG_OBJECT (ubin, \ ++ "buffering locking from thread %p", \ ++ g_thread_self ()); \ ++ g_mutex_lock (&GST_URI_SOURCE_BIN_CAST(ubin)->buffering_lock); \ ++ GST_LOG_OBJECT (ubin, \ ++ "buffering lock from thread %p", \ ++ g_thread_self ()); \ ++} G_STMT_END ++ ++#define BUFFERING_UNLOCK(ubin) G_STMT_START { \ ++ GST_LOG_OBJECT (ubin, \ ++ "buffering unlocking from thread %p", \ ++ g_thread_self ()); \ ++ g_mutex_unlock (&GST_URI_SOURCE_BIN_CAST(ubin)->buffering_lock); \ ++} G_STMT_END ++ + /* Track a source pad from a child that + * is linked or needs linking to an output + * slot */ +@@ -122,8 +140,7 @@ struct _GstURISourceBin + gboolean use_buffering; + + GstElement *source; +- GstElement *typefind; +- guint have_type_id; /* have-type signal id from typefind */ ++ GList *typefinds; /* list of typefind element */ + + GstElement *demuxer; /* Adaptive demuxer if any */ + GSList *out_slots; +@@ -134,8 +151,6 @@ struct _GstURISourceBin + /* for dynamic sources */ + guint src_np_sig_id; /* new-pad signal id */ + +- gboolean async_pending; /* async-start has been emitted */ +- + guint64 ring_buffer_max_size; /* 0 means disabled */ + + GList *pending_pads; /* Pads we have blocked pending assignment +@@ -144,34 +159,20 @@ struct _GstURISourceBin + + GList *buffering_status; /* element currently buffering messages */ + gint last_buffering_pct; /* Avoid sending buffering over and over */ ++ GMutex buffering_lock; ++ GMutex buffering_post_lock; + }; + + struct _GstURISourceBinClass + { + GstBinClass parent_class; + +- /* signal fired when we found a pad that we cannot decode */ +- void (*unknown_type) (GstElement * element, GstPad * pad, GstCaps * caps); +- +- /* signal fired to know if we continue trying to decode the given caps */ +- gboolean (*autoplug_continue) (GstElement * element, GstPad * pad, +- GstCaps * caps); +- /* signal fired to get a list of factories to try to autoplug */ +- GValueArray *(*autoplug_factories) (GstElement * element, GstPad * pad, +- GstCaps * caps); +- /* signal fired to sort the factories */ +- GValueArray *(*autoplug_sort) (GstElement * element, GstPad * pad, +- GstCaps * caps, GValueArray * factories); +- /* signal fired to select from the proposed list of factories */ +- GstAutoplugSelectResult (*autoplug_select) (GstElement * element, +- GstPad * pad, GstCaps * caps, GstElementFactory * factory); +- /* signal fired when a autoplugged element that is not linked downstream +- * or exposed wants to query something */ +- gboolean (*autoplug_query) (GstElement * element, GstPad * pad, +- GstQuery * query); +- +- /* emitted when all data is decoded */ ++ /* emitted when all data has been drained out ++ * FIXME : What do we need this for ?? */ + void (*drained) (GstElement * element); ++ /* emitted when all data has been fed into buffering slots (i.e the ++ * actual sources are done) */ ++ void (*about_to_finish) (GstElement * element); + }; + + static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src_%u", +@@ -187,13 +188,8 @@ GST_DEBUG_CATEGORY_STATIC (gst_uri_source_bin_debug); + /* signals */ + enum + { +- SIGNAL_UNKNOWN_TYPE, +- SIGNAL_AUTOPLUG_CONTINUE, +- SIGNAL_AUTOPLUG_FACTORIES, +- SIGNAL_AUTOPLUG_SELECT, +- SIGNAL_AUTOPLUG_SORT, +- SIGNAL_AUTOPLUG_QUERY, + SIGNAL_DRAINED, ++ SIGNAL_ABOUT_TO_FINISH, + SIGNAL_SOURCE_SETUP, + LAST_SIGNAL + }; +@@ -205,7 +201,7 @@ enum + #define DEFAULT_BUFFER_DURATION -1 + #define DEFAULT_BUFFER_SIZE -1 + #define DEFAULT_DOWNLOAD FALSE +-#define DEFAULT_USE_BUFFERING FALSE ++#define DEFAULT_USE_BUFFERING TRUE + #define DEFAULT_RING_BUFFER_MAX_SIZE 0 + + #define DEFAULT_CAPS (gst_static_caps_get (&default_raw_caps)) +@@ -222,6 +218,21 @@ enum + PROP_RING_BUFFER_MAX_SIZE + }; + ++#define CUSTOM_EOS_QUARK _custom_eos_quark_get () ++#define CUSTOM_EOS_QUARK_DATA "custom-eos" ++static GQuark ++_custom_eos_quark_get (void) ++{ ++ static gsize g_quark; ++ ++ if (g_once_init_enter (&g_quark)) { ++ gsize quark = ++ (gsize) g_quark_from_static_string ("urisourcebin-custom-eos"); ++ g_once_init_leave (&g_quark, quark); ++ } ++ return g_quark; ++} ++ + static void post_missing_plugin_error (GstElement * dec, + const gchar * element_name); + +@@ -249,175 +260,11 @@ static void expose_output_pad (GstURISourceBin * urisrc, GstPad * pad); + static OutputSlotInfo *get_output_slot (GstURISourceBin * urisrc, + gboolean do_download, gboolean is_adaptive, GstCaps * caps); + static void free_output_slot (OutputSlotInfo * slot, GstURISourceBin * urisrc); ++static void free_output_slot_async (GstURISourceBin * urisrc, ++ OutputSlotInfo * slot); + static GstPad *create_output_pad (GstURISourceBin * urisrc, GstPad * pad); + static void remove_buffering_msgs (GstURISourceBin * bin, GstObject * src); + +-static gboolean +-_gst_boolean_accumulator (GSignalInvocationHint * ihint, +- GValue * return_accu, const GValue * handler_return, gpointer dummy) +-{ +- gboolean myboolean; +- +- myboolean = g_value_get_boolean (handler_return); +- if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) +- g_value_set_boolean (return_accu, myboolean); +- +- /* stop emission if FALSE */ +- return myboolean; +-} +- +-static gboolean +-_gst_boolean_or_accumulator (GSignalInvocationHint * ihint, +- GValue * return_accu, const GValue * handler_return, gpointer dummy) +-{ +- gboolean myboolean; +- gboolean retboolean; +- +- myboolean = g_value_get_boolean (handler_return); +- retboolean = g_value_get_boolean (return_accu); +- +- if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) +- g_value_set_boolean (return_accu, myboolean || retboolean); +- +- return TRUE; +-} +- +-static gboolean +-_gst_array_accumulator (GSignalInvocationHint * ihint, +- GValue * return_accu, const GValue * handler_return, gpointer dummy) +-{ +- gpointer array; +- +- array = g_value_get_boxed (handler_return); +- if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) +- g_value_set_boxed (return_accu, array); +- +- return FALSE; +-} +- +-static gboolean +-_gst_select_accumulator (GSignalInvocationHint * ihint, +- GValue * return_accu, const GValue * handler_return, gpointer dummy) +-{ +- GstAutoplugSelectResult res; +- +- res = g_value_get_enum (handler_return); +- if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) +- g_value_set_enum (return_accu, res); +- +- /* Call the next handler in the chain (if any) when the current callback +- * returns TRY. This makes it possible to register separate autoplug-select +- * handlers that implement different TRY/EXPOSE/SKIP strategies. +- */ +- if (res == GST_AUTOPLUG_SELECT_TRY) +- return TRUE; +- +- return FALSE; +-} +- +-static gboolean +-_gst_array_hasvalue_accumulator (GSignalInvocationHint * ihint, +- GValue * return_accu, const GValue * handler_return, gpointer dummy) +-{ +- gpointer array; +- +- array = g_value_get_boxed (handler_return); +- if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP)) +- g_value_set_boxed (return_accu, array); +- +- if (array != NULL) +- return FALSE; +- +- return TRUE; +-} +- +-static gboolean +-gst_uri_source_bin_autoplug_continue (GstElement * element, GstPad * pad, +- GstCaps * caps) +-{ +- /* by default we always continue */ +- return TRUE; +-} +- +-/* Must be called with factories lock! */ +-static void +-gst_uri_source_bin_update_factories_list (GstURISourceBin * dec) +-{ +- guint32 cookie; +- +- cookie = gst_registry_get_feature_list_cookie (gst_registry_get ()); +- if (!dec->factories || dec->factories_cookie != cookie) { +- if (dec->factories) +- gst_plugin_feature_list_free (dec->factories); +- dec->factories = +- gst_element_factory_list_get_elements +- (GST_ELEMENT_FACTORY_TYPE_DECODABLE, GST_RANK_MARGINAL); +- dec->factories = +- g_list_sort (dec->factories, gst_playback_utils_compare_factories_func); +- dec->factories_cookie = cookie; +- } +-} +- +-static GValueArray * +-gst_uri_source_bin_autoplug_factories (GstElement * element, GstPad * pad, +- GstCaps * caps) +-{ +- GList *list, *tmp; +- GValueArray *result; +- GstURISourceBin *dec = GST_URI_SOURCE_BIN_CAST (element); +- +- GST_DEBUG_OBJECT (element, "finding factories"); +- +- /* return all compatible factories for caps */ +- g_mutex_lock (&dec->factories_lock); +- gst_uri_source_bin_update_factories_list (dec); +- list = +- gst_element_factory_list_filter (dec->factories, caps, GST_PAD_SINK, +- gst_caps_is_fixed (caps)); +- g_mutex_unlock (&dec->factories_lock); +- +- result = g_value_array_new (g_list_length (list)); +- for (tmp = list; tmp; tmp = tmp->next) { +- GstElementFactory *factory = GST_ELEMENT_FACTORY_CAST (tmp->data); +- GValue val = { 0, }; +- +- g_value_init (&val, G_TYPE_OBJECT); +- g_value_set_object (&val, factory); +- g_value_array_append (result, &val); +- g_value_unset (&val); +- } +- gst_plugin_feature_list_free (list); +- +- GST_DEBUG_OBJECT (element, "autoplug-factories returns %p", result); +- +- return result; +-} +- +-static GValueArray * +-gst_uri_source_bin_autoplug_sort (GstElement * element, GstPad * pad, +- GstCaps * caps, GValueArray * factories) +-{ +- return NULL; +-} +- +-static GstAutoplugSelectResult +-gst_uri_source_bin_autoplug_select (GstElement * element, GstPad * pad, +- GstCaps * caps, GstElementFactory * factory) +-{ +- GST_DEBUG_OBJECT (element, "default autoplug-select returns TRY"); +- +- /* Try factory. */ +- return GST_AUTOPLUG_SELECT_TRY; +-} +- +-static gboolean +-gst_uri_source_bin_autoplug_query (GstElement * element, GstPad * pad, +- GstQuery * query) +-{ +- /* No query handled here */ +- return FALSE; +-} +- + static void + gst_uri_source_bin_class_init (GstURISourceBinClass * klass) + { +@@ -468,7 +315,7 @@ gst_uri_source_bin_class_init (GstURISourceBinClass * klass) + "Attempt download buffering when buffering network streams", + DEFAULT_DOWNLOAD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + +- /** ++ /** + * GstURISourceBin::use-buffering: + * + * Perform buffering using a queue2 element, and emit BUFFERING +@@ -498,179 +345,6 @@ gst_uri_source_bin_class_init (GstURISourceBinClass * klass) + 0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + +- /** +- * GstURISourceBin::unknown-type: +- * @bin: The urisourcebin. +- * @pad: the new pad containing caps that cannot be resolved to a 'final'. +- * stream type. +- * @caps: the #GstCaps of the pad that cannot be resolved. +- * +- * This signal is emitted when a pad for which there is no further possible +- * decoding is added to the urisourcebin. +- */ +- gst_uri_source_bin_signals[SIGNAL_UNKNOWN_TYPE] = +- g_signal_new ("unknown-type", G_TYPE_FROM_CLASS (klass), +- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass, unknown_type), +- NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, +- GST_TYPE_PAD, GST_TYPE_CAPS); +- +- /** +- * GstURISourceBin::autoplug-continue: +- * @bin: The urisourcebin. +- * @pad: The #GstPad. +- * @caps: The #GstCaps found. +- * +- * This signal is emitted whenever urisourcebin finds a new stream. It is +- * emitted before looking for any elements that can handle that stream. +- * +- * +- * Invocation of signal handlers stops after the first signal handler +- * returns #FALSE. Signal handlers are invoked in the order they were +- * connected in. +- * +- * +- * Returns: #TRUE if you wish urisourcebin to look for elements that can +- * handle the given @caps. If #FALSE, those caps will be considered as +- * final and the pad will be exposed as such (see 'pad-added' signal of +- * #GstElement). +- */ +- gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_CONTINUE] = +- g_signal_new ("autoplug-continue", G_TYPE_FROM_CLASS (klass), +- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass, +- autoplug_continue), _gst_boolean_accumulator, NULL, +- g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 2, GST_TYPE_PAD, +- GST_TYPE_CAPS); +- +- /** +- * GstURISourceBin::autoplug-factories: +- * @bin: The urisourcebin. +- * @pad: The #GstPad. +- * @caps: The #GstCaps found. +- * +- * This function is emitted when an array of possible factories for @caps on +- * @pad is needed. urisourcebin will by default return an array with all +- * compatible factories, sorted by rank. +- * +- * If this function returns NULL, @pad will be exposed as a final caps. +- * +- * If this function returns an empty array, the pad will be considered as +- * having an unhandled type media type. +- * +- * +- * Only the signal handler that is connected first will ever by invoked. +- * Don't connect signal handlers with the #G_CONNECT_AFTER flag to this +- * signal, they will never be invoked! +- * +- * +- * Returns: a #GValueArray* with a list of factories to try. The factories are +- * by default tried in the returned order or based on the index returned by +- * "autoplug-select". +- */ +- gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_FACTORIES] = +- g_signal_new ("autoplug-factories", G_TYPE_FROM_CLASS (klass), +- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass, +- autoplug_factories), _gst_array_accumulator, NULL, +- g_cclosure_marshal_generic, G_TYPE_VALUE_ARRAY, 2, +- GST_TYPE_PAD, GST_TYPE_CAPS); +- +- /** +- * GstURISourceBin::autoplug-sort: +- * @bin: The urisourcebin. +- * @pad: The #GstPad. +- * @caps: The #GstCaps. +- * @factories: A #GValueArray of possible #GstElementFactory to use. +- * +- * Once decodebin has found the possible #GstElementFactory objects to try +- * for @caps on @pad, this signal is emited. The purpose of the signal is for +- * the application to perform additional sorting or filtering on the element +- * factory array. +- * +- * The callee should copy and modify @factories or return #NULL if the +- * order should not change. +- * +- * +- * Invocation of signal handlers stops after one signal handler has +- * returned something else than #NULL. Signal handlers are invoked in +- * the order they were connected in. +- * Don't connect signal handlers with the #G_CONNECT_AFTER flag to this +- * signal, they will never be invoked! +- * +- * +- * Returns: A new sorted array of #GstElementFactory objects. +- * +- * Since: 0.10.33 +- */ +- gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_SORT] = +- g_signal_new ("autoplug-sort", G_TYPE_FROM_CLASS (klass), +- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass, autoplug_sort), +- _gst_array_hasvalue_accumulator, NULL, +- g_cclosure_marshal_generic, G_TYPE_VALUE_ARRAY, 3, GST_TYPE_PAD, +- GST_TYPE_CAPS, G_TYPE_VALUE_ARRAY | G_SIGNAL_TYPE_STATIC_SCOPE); +- +- /** +- * GstURISourceBin::autoplug-select: +- * @bin: The urisourcebin. +- * @pad: The #GstPad. +- * @caps: The #GstCaps. +- * @factory: A #GstElementFactory to use. +- * +- * This signal is emitted once urisourcebin has found all the possible +- * #GstElementFactory that can be used to handle the given @caps. For each of +- * those factories, this signal is emitted. +- * +- * The signal handler should return a #GST_TYPE_AUTOPLUG_SELECT_RESULT enum +- * value indicating what decodebin should do next. +- * +- * A value of #GST_AUTOPLUG_SELECT_TRY will try to autoplug an element from +- * @factory. +- * +- * A value of #GST_AUTOPLUG_SELECT_EXPOSE will expose @pad without plugging +- * any element to it. +- * +- * A value of #GST_AUTOPLUG_SELECT_SKIP will skip @factory and move to the +- * next factory. +- * +- * +- * The signal handler will not be invoked if any of the previously +- * registered signal handlers (if any) return a value other than +- * GST_AUTOPLUG_SELECT_TRY. Which also means that if you return +- * GST_AUTOPLUG_SELECT_TRY from one signal handler, handlers that get +- * registered next (again, if any) can override that decision. +- * +- * +- * Returns: a #GST_TYPE_AUTOPLUG_SELECT_RESULT that indicates the required +- * operation. The default handler will always return +- * #GST_AUTOPLUG_SELECT_TRY. +- */ +- gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_SELECT] = +- g_signal_new ("autoplug-select", G_TYPE_FROM_CLASS (klass), +- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass, +- autoplug_select), _gst_select_accumulator, NULL, +- g_cclosure_marshal_generic, +- GST_TYPE_AUTOPLUG_SELECT_RESULT, 3, GST_TYPE_PAD, GST_TYPE_CAPS, +- GST_TYPE_ELEMENT_FACTORY); +- +- /** +- * GstDecodeBin::autoplug-query: +- * @bin: The decodebin. +- * @child: The child element doing the query +- * @pad: The #GstPad. +- * @query: The #GstQuery. +- * +- * This signal is emitted whenever an autoplugged element that is +- * not linked downstream yet and not exposed does a query. It can +- * be used to tell the element about the downstream supported caps +- * for example. +- * +- * Returns: #TRUE if the query was handled, #FALSE otherwise. +- */ +- gst_uri_source_bin_signals[SIGNAL_AUTOPLUG_QUERY] = +- g_signal_new ("autoplug-query", G_TYPE_FROM_CLASS (klass), +- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstURISourceBinClass, autoplug_query), +- _gst_boolean_or_accumulator, NULL, g_cclosure_marshal_generic, +- G_TYPE_BOOLEAN, 3, GST_TYPE_PAD, GST_TYPE_ELEMENT, +- GST_TYPE_QUERY | G_SIGNAL_TYPE_STATIC_SCOPE); +- + /** + * GstURISourceBin::drained: + * +@@ -682,6 +356,17 @@ gst_uri_source_bin_class_init (GstURISourceBinClass * klass) + G_STRUCT_OFFSET (GstURISourceBinClass, drained), NULL, NULL, + g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE); + ++ /** ++ * GstURISourceBin::about-to-finish: ++ * ++ * This signal is emitted when the data for the current uri is played. ++ */ ++ gst_uri_source_bin_signals[SIGNAL_ABOUT_TO_FINISH] = ++ g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GstURISourceBinClass, about_to_finish), NULL, NULL, ++ g_cclosure_marshal_generic, G_TYPE_NONE, 0, G_TYPE_NONE); ++ + /** + * GstURISourceBin::source-setup: + * @bin: the urisourcebin. +@@ -712,15 +397,6 @@ gst_uri_source_bin_class_init (GstURISourceBinClass * klass) + GST_DEBUG_FUNCPTR (gst_uri_source_bin_change_state); + + gstbin_class->handle_message = GST_DEBUG_FUNCPTR (handle_message); +- +- klass->autoplug_continue = +- GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_continue); +- klass->autoplug_factories = +- GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_factories); +- klass->autoplug_sort = GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_sort); +- klass->autoplug_select = +- GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_select); +- klass->autoplug_query = GST_DEBUG_FUNCPTR (gst_uri_source_bin_autoplug_query); + } + + static void +@@ -731,12 +407,16 @@ gst_uri_source_bin_init (GstURISourceBin * urisrc) + + g_mutex_init (&urisrc->lock); + ++ g_mutex_init (&urisrc->buffering_lock); ++ g_mutex_init (&urisrc->buffering_post_lock); ++ + urisrc->uri = g_strdup (DEFAULT_PROP_URI); + urisrc->connection_speed = DEFAULT_CONNECTION_SPEED; + + urisrc->buffer_duration = DEFAULT_BUFFER_DURATION; + urisrc->buffer_size = DEFAULT_BUFFER_SIZE; + urisrc->download = DEFAULT_DOWNLOAD; ++ urisrc->use_buffering = DEFAULT_USE_BUFFERING; + urisrc->ring_buffer_max_size = DEFAULT_RING_BUFFER_MAX_SIZE; + urisrc->last_buffering_pct = -1; + +@@ -753,6 +433,8 @@ gst_uri_source_bin_finalize (GObject * obj) + remove_demuxer (urisrc); + g_mutex_clear (&urisrc->lock); + g_mutex_clear (&urisrc->factories_lock); ++ g_mutex_clear (&urisrc->buffering_lock); ++ g_mutex_clear (&urisrc->buffering_post_lock); + g_free (urisrc->uri); + if (urisrc->factories) + gst_plugin_feature_list_free (urisrc->factories); +@@ -846,32 +528,6 @@ gst_uri_source_bin_get_property (GObject * object, guint prop_id, + } + } + +-static void +-do_async_start (GstURISourceBin * dbin) +-{ +- GstMessage *message; +- +- dbin->async_pending = TRUE; +- +- message = gst_message_new_async_start (GST_OBJECT_CAST (dbin)); +- GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (dbin), message); +-} +- +-static void +-do_async_done (GstURISourceBin * dbin) +-{ +- GstMessage *message; +- +- if (dbin->async_pending) { +- GST_DEBUG_OBJECT (dbin, "posting ASYNC_DONE"); +- message = +- gst_message_new_async_done (GST_OBJECT_CAST (dbin), +- GST_CLOCK_TIME_NONE); +- GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (dbin), message); +- +- dbin->async_pending = FALSE; +- } +-} + + #define DEFAULT_QUEUE_SIZE (3 * GST_SECOND) + #define DEFAULT_QUEUE_MIN_THRESHOLD ((DEFAULT_QUEUE_SIZE * 30) / 100) +@@ -932,8 +588,8 @@ new_demuxer_pad_added_cb (GstElement * element, GstPad * pad, + gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, + pending_pad_blocked, urisrc, NULL); + info->event_probe_id = +- gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, +- demux_pad_events, urisrc, NULL); ++ gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | ++ GST_PAD_PROBE_TYPE_EVENT_FLUSH, demux_pad_events, urisrc, NULL); + } + + static GstPadProbeReturn +@@ -958,8 +614,9 @@ pending_pad_blocked (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) + + /* If already linked to a slot, nothing more to do */ + if (child_info->output_slot) { +- GST_LOG_OBJECT (urisrc, "Pad %" GST_PTR_FORMAT " is linked to slot %p", +- pad, child_info->output_slot); ++ GST_LOG_OBJECT (urisrc, "Pad %" GST_PTR_FORMAT " is linked to queue %" ++ GST_PTR_FORMAT " on slot %p", pad, child_info->output_slot->queue, ++ child_info->output_slot); + GST_URI_SOURCE_BIN_UNLOCK (urisrc); + goto done; + } +@@ -968,7 +625,6 @@ pending_pad_blocked (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) + if (caps == NULL) + caps = gst_pad_query_caps (pad, NULL); + +- /* FIXME: Don't do buffering if use_buffering is FALSE */ + slot = get_output_slot (urisrc, FALSE, TRUE, caps); + + gst_caps_unref (caps); +@@ -1008,6 +664,9 @@ link_pending_pad_to_output (GstURISourceBin * urisrc, OutputSlotInfo * slot) + /* Look for a suitable pending pad */ + cur_caps = gst_pad_get_current_caps (slot->sinkpad); + ++ GST_DEBUG_OBJECT (urisrc, ++ "Looking for a pending pad with caps %" GST_PTR_FORMAT, cur_caps); ++ + for (cur = urisrc->pending_pads; cur != NULL; cur = g_list_next (cur)) { + GstPad *pending = (GstPad *) (cur->data); + ChildSrcPadInfo *cur_info = NULL; +@@ -1035,8 +694,8 @@ link_pending_pad_to_output (GstURISourceBin * urisrc, OutputSlotInfo * slot) + guint block_id = + gst_pad_add_probe (slot->sinkpad, GST_PAD_PROBE_TYPE_BLOCK_UPSTREAM, + NULL, NULL, NULL); +- GST_DEBUG_OBJECT (urisrc, "Linking pending pad to existing output slot %p", +- slot); ++ GST_DEBUG_OBJECT (urisrc, "Linking pending pad %" GST_PTR_FORMAT ++ " to existing output slot %p", out_info->demux_src_pad, slot); + + if (in_info) { + gst_pad_unlink (in_info->demux_src_pad, slot->sinkpad); +@@ -1048,7 +707,15 @@ link_pending_pad_to_output (GstURISourceBin * urisrc, OutputSlotInfo * slot) + slot->sinkpad) == GST_PAD_LINK_OK) { + out_info->output_slot = slot; + slot->linked_info = out_info; ++ ++ BUFFERING_LOCK (urisrc); ++ /* A re-linked slot is no longer EOS */ ++ slot->is_eos = FALSE; ++ BUFFERING_UNLOCK (urisrc); + res = TRUE; ++ slot->is_eos = FALSE; ++ urisrc->pending_pads = ++ g_list_remove (urisrc->pending_pads, out_info->demux_src_pad); + } else { + GST_ERROR_OBJECT (urisrc, + "Failed to link new demuxer pad to the output slot we tried"); +@@ -1059,11 +726,27 @@ link_pending_pad_to_output (GstURISourceBin * urisrc, OutputSlotInfo * slot) + return res; + } + ++/* Called with lock held */ ++static gboolean ++all_slots_are_eos (GstURISourceBin * urisrc) ++{ ++ GSList *tmp; ++ ++ for (tmp = urisrc->out_slots; tmp; tmp = tmp->next) { ++ OutputSlotInfo *slot = (OutputSlotInfo *) tmp->data; ++ if (slot->is_eos == FALSE) ++ return FALSE; ++ } ++ return TRUE; ++} ++ + static GstPadProbeReturn + demux_pad_events (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) + { + GstURISourceBin *urisrc = GST_URI_SOURCE_BIN (user_data); + ChildSrcPadInfo *child_info; ++ GstPadProbeReturn ret = GST_PAD_PROBE_OK; ++ GstEvent *ev = GST_PAD_PROBE_INFO_EVENT (info); + + if (!(child_info = + g_object_get_data (G_OBJECT (pad), "urisourcebin.srcpadinfo"))) +@@ -1076,34 +759,84 @@ demux_pad_events (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) + goto done; + } + +- if (GST_IS_EVENT (GST_PAD_PROBE_INFO_DATA (info))) { +- GstEvent *ev = GST_PAD_PROBE_INFO_EVENT (info); +- if (GST_EVENT_TYPE (ev) == GST_EVENT_EOS && urisrc->pending_pads) { ++ switch (GST_EVENT_TYPE (ev)) { ++ case GST_EVENT_EOS: ++ { ++ gboolean all_streams_eos; ++ + GST_LOG_OBJECT (urisrc, "EOS on pad %" GST_PTR_FORMAT, pad); +- if (!link_pending_pad_to_output (urisrc, child_info->output_slot)) { +- GstEvent *event; +- GstStructure *s; + +- /* Mark that we fed an EOS to this slot */ +- child_info->output_slot->is_eos = TRUE; ++ if ((urisrc->pending_pads && ++ link_pending_pad_to_output (urisrc, child_info->output_slot))) { ++ /* Found a new source pad to give this slot data - no need to send EOS */ ++ GST_URI_SOURCE_BIN_UNLOCK (urisrc); ++ ret = GST_PAD_PROBE_DROP; ++ goto done; ++ } + +- /* Actually feed a custom EOS event to avoid marking pads as EOSed */ +- s = gst_structure_new_empty ("urisourcebin-custom-eos"); +- event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s); +- gst_pad_send_event (child_info->output_slot->sinkpad, event); ++ BUFFERING_LOCK (urisrc); ++ /* Mark that we fed an EOS to this slot */ ++ child_info->output_slot->is_eos = TRUE; ++ all_streams_eos = all_slots_are_eos (urisrc); ++ BUFFERING_UNLOCK (urisrc); ++ ++ /* EOS means this element is no longer buffering */ ++ remove_buffering_msgs (urisrc, ++ GST_OBJECT_CAST (child_info->output_slot->queue)); ++ ++ /* Mark this custom EOS */ ++ gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (ev), CUSTOM_EOS_QUARK, ++ (gchar *) CUSTOM_EOS_QUARK_DATA, NULL); ++ if (all_streams_eos) { ++ GST_DEBUG_OBJECT (urisrc, "POSTING ABOUT TO FINISH"); ++ g_signal_emit (urisrc, ++ gst_uri_source_bin_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL); + } +- GST_URI_SOURCE_BIN_UNLOCK (urisrc); +- return GST_PAD_PROBE_HANDLED; +- } else if (GST_EVENT_TYPE (ev) == GST_EVENT_CAPS) { ++ } ++ break; ++ case GST_EVENT_CAPS: ++ { + GstCaps *caps; + gst_event_parse_caps (ev, &caps); + gst_caps_replace (&child_info->cur_caps, caps); + } ++ break; ++ case GST_EVENT_STREAM_START: ++ case GST_EVENT_FLUSH_STOP: ++ BUFFERING_LOCK (urisrc); ++ child_info->output_slot->is_eos = FALSE; ++ BUFFERING_UNLOCK (urisrc); ++ break; ++ default: ++ break; + } ++ + GST_URI_SOURCE_BIN_UNLOCK (urisrc); + + done: +- return GST_PAD_PROBE_OK; ++ return ret; ++} ++ ++static GstPadProbeReturn ++pre_queue_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) ++{ ++ GstURISourceBin *urisrc = GST_URI_SOURCE_BIN (user_data); ++ GstPadProbeReturn ret = GST_PAD_PROBE_OK; ++ GstEvent *ev = GST_PAD_PROBE_INFO_EVENT (info); ++ ++ switch (GST_EVENT_TYPE (ev)) { ++ case GST_EVENT_EOS: ++ { ++ GST_LOG_OBJECT (urisrc, "EOS on pad %" GST_PTR_FORMAT, pad); ++ GST_DEBUG_OBJECT (urisrc, "POSTING ABOUT TO FINISH"); ++ g_signal_emit (urisrc, ++ gst_uri_source_bin_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL); ++ } ++ break; ++ default: ++ break; ++ } ++ return ret; + } + + /* Called with lock held */ +@@ -1129,6 +862,7 @@ get_output_slot (GstURISourceBin * urisrc, gboolean do_download, + if (cur_caps == NULL || gst_caps_is_equal (caps, cur_caps)) { + GST_LOG_OBJECT (urisrc, "Found existing slot %p to link to", slot); + gst_caps_unref (cur_caps); ++ slot->is_eos = FALSE; + return slot; + } + gst_caps_unref (cur_caps); +@@ -1137,11 +871,9 @@ get_output_slot (GstURISourceBin * urisrc, gboolean do_download, + } + + /* Otherwise create the new slot */ +-#if 0 /* There's no downloadbuffer in 1.2 */ + if (do_download) + elem_name = "downloadbuffer"; + else +-#endif + elem_name = "queue2"; + + queue = gst_element_factory_make (elem_name, NULL); +@@ -1151,6 +883,9 @@ get_output_slot (GstURISourceBin * urisrc, gboolean do_download, + slot = g_new0 (OutputSlotInfo, 1); + slot->queue = queue; + ++ /* Set the slot onto the queue (needed in buffering msg handling) */ ++ g_object_set_data (G_OBJECT (queue), "urisourcebin.slotinfo", slot); ++ + if (do_download) { + gchar *temp_template, *filename; + const gchar *tmp_dir, *prgname; +@@ -1176,16 +911,21 @@ get_output_slot (GstURISourceBin * urisrc, gboolean do_download, + } else { + if (is_adaptive) { + GST_LOG_OBJECT (urisrc, "Adding queue for adaptive streaming stream"); +- g_object_set (queue, "use-buffering", TRUE, "use-tags-bitrate", TRUE, +- "use-rate-estimate", FALSE, NULL); ++ g_object_set (queue, "use-buffering", urisrc->use_buffering, ++ "use-tags-bitrate", TRUE, "use-rate-estimate", FALSE, NULL); + } else { + GST_LOG_OBJECT (urisrc, "Adding queue for buffering"); +- g_object_set (queue, "use-buffering", TRUE, NULL); ++ g_object_set (queue, "use-buffering", urisrc->use_buffering, NULL); + } + g_object_set (queue, "ring-buffer-max-size", + urisrc->ring_buffer_max_size, NULL); + /* Disable max-size-buffers - queue based on data rate to the default time limit */ + g_object_set (queue, "max-size-buffers", 0, NULL); ++ ++ /* Don't start buffering until the queue is empty (< 1%). ++ * Start playback when the queue is 60% full, leaving a bit more room ++ * for upstream to push more without getting bursty */ ++ g_object_set (queue, "low-percent", 1, "high-percent", 60, NULL); + } + + /* If buffer size or duration are set, set them on the element */ +@@ -1193,16 +933,14 @@ get_output_slot (GstURISourceBin * urisrc, gboolean do_download, + g_object_set (queue, "max-size-bytes", urisrc->buffer_size, NULL); + if (urisrc->buffer_duration != -1) + g_object_set (queue, "max-size-time", urisrc->buffer_duration, NULL); ++#if 0 ++ /* Disabled because this makes initial startup slower for radio streams */ + else { + /* Buffer 4 seconds by default - some extra headroom over the + * core default, because we trigger playback sooner */ +- g_object_set (queue, "max-size-time", 4 * GST_SECOND, NULL); ++ //g_object_set (queue, "max-size-time", 4 * GST_SECOND, NULL); + } +- +- /* Don't start buffering until the queue is empty (< 1%). +- * Start playback when the queue is 60% full, leaving a bit more room +- * for upstream to push more without getting bursty */ +- g_object_set (queue, "low-percent", 1, "high-percent", 60, NULL); ++#endif + + /* save queue pointer so we can remove it later */ + urisrc->out_slots = g_slist_prepend (urisrc->out_slots, slot); +@@ -1236,48 +974,62 @@ source_pad_event_probe (GstPad * pad, GstPadProbeInfo * info, + GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info); + GstURISourceBin *urisrc = user_data; + +- GST_LOG_OBJECT (pad, "%s, urisrc %p", GST_EVENT_TYPE_NAME (event), urisrc); ++ GST_LOG_OBJECT (pad, "%s, urisrc %p", GST_EVENT_TYPE_NAME (event), event); + +- if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM && +- gst_event_has_name (event, "urisourcebin-custom-eos")) { ++ if (GST_EVENT_TYPE (event) == GST_EVENT_EOS && ++ gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (event), ++ CUSTOM_EOS_QUARK)) { + OutputSlotInfo *slot; + GST_DEBUG_OBJECT (pad, "we received EOS"); + +- /* Check the slot is still unlinked - maybe it got +- * re-linked and we should drop this EOS */ ++ /* remove custom-eos */ ++ gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (event), CUSTOM_EOS_QUARK, ++ NULL, NULL); ++ + GST_URI_SOURCE_BIN_LOCK (urisrc); ++ + slot = g_object_get_data (G_OBJECT (pad), "urisourcebin.slotinfo"); +- if (slot && slot->linked_info) { +- GST_DEBUG_OBJECT (pad, +- "EOS pad was re-linked to pending pad, so removing EOS status"); +- slot->is_eos = FALSE; +- GST_URI_SOURCE_BIN_UNLOCK (urisrc); +- return GST_PAD_PROBE_HANDLED; +- } + +- /* Otherwise it's time to send EOS and clean up this pad */ +- gst_pad_push_event (slot->srcpad, gst_event_new_eos ()); ++ if (slot) { ++ GstEvent *eos; ++ guint32 seqnum; ++ ++ if (slot->linked_info) { ++ if (slot->is_eos) { ++ /* linked_info is old input which is stil linked without removal */ ++ GST_DEBUG_OBJECT (pad, "push actual EOS"); ++ seqnum = gst_event_get_seqnum (event); ++ eos = gst_event_new_eos (); ++ gst_event_set_seqnum (eos, seqnum); ++ gst_pad_push_event (slot->srcpad, eos); ++ } else { ++ /* Do not clear output slot yet. A new input was ++ * connected. We should just drop this EOS */ ++ } ++ GST_URI_SOURCE_BIN_UNLOCK (urisrc); ++ return GST_PAD_PROBE_DROP; ++ } + +- /* FIXME: Can't clean the pad up from the streaming thread... */ +- urisrc->out_slots = g_slist_remove (urisrc->out_slots, slot); +-#if 0 +- free_output_slot (slot, urisrc); +- slot = NULL; +-#endif ++ seqnum = gst_event_get_seqnum (event); ++ eos = gst_event_new_eos (); ++ gst_event_set_seqnum (eos, seqnum); ++ gst_pad_push_event (slot->srcpad, eos); ++ free_output_slot_async (urisrc, slot); ++ } + + /* FIXME: Only emit drained if all output pads are done and there's no + * pending pads */ + g_signal_emit (urisrc, gst_uri_source_bin_signals[SIGNAL_DRAINED], 0, NULL); + + GST_URI_SOURCE_BIN_UNLOCK (urisrc); +- return GST_PAD_PROBE_HANDLED; ++ return GST_PAD_PROBE_DROP; + } + /* never drop events */ + return GST_PAD_PROBE_OK; + } + + /* called when we found a raw pad to expose. We set up a +- * padprobe to detect EOS before exposing the pad. ++ * padprobe to detect EOS before exposing the pad. + * Called with LOCK held. */ + static GstPad * + create_output_pad (GstURISourceBin * urisrc, GstPad * pad) +@@ -1298,6 +1050,9 @@ create_output_pad (GstURISourceBin * urisrc, GstPad * pad) + gst_object_unref (pad_tmpl); + g_free (padname); + ++ GST_DEBUG_OBJECT (urisrc, "Created output pad %s:%s for pad %s:%s", ++ GST_DEBUG_PAD_NAME (newpad), GST_DEBUG_PAD_NAME (pad)); ++ + return newpad; + } + +@@ -1316,9 +1071,6 @@ expose_output_pad (GstURISourceBin * urisrc, GstPad * pad) + + gst_pad_set_active (pad, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (urisrc), pad); +- +- /* Once we expose a pad, we're no longer async */ +- do_async_done (urisrc); + } + + static void +@@ -1342,21 +1094,39 @@ pad_removed_cb (GstElement * element, GstPad * pad, GstURISourceBin * urisrc) + + /* Send EOS to the output slot if the demuxer didn't already */ + if (info->output_slot) { +- if (!info->output_slot->is_eos) { +- GstStructure *s; +- GstEvent *event; ++ GstStructure *s; ++ GstEvent *event; ++ OutputSlotInfo *slot; + +- GST_LOG_OBJECT (element, +- "Pad %" GST_PTR_FORMAT " was removed without EOS. Sending.", pad); ++ slot = info->output_slot; + +- s = gst_structure_new_empty ("urisourcebin-custom-eos"); +- event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s); +- gst_pad_send_event (info->output_slot->sinkpad, event); +- info->output_slot->is_eos = TRUE; ++ if (!slot->is_eos && urisrc->pending_pads && ++ link_pending_pad_to_output (urisrc, slot)) { ++ /* Found a new source pad to give this slot data - no need to send EOS */ ++ GST_URI_SOURCE_BIN_UNLOCK (urisrc); ++ return; + } +- /* After the pad goes away, the slot is free to reuse */ +- info->output_slot->linked_info = NULL; ++ ++ BUFFERING_LOCK (urisrc); ++ /* Unlink this pad from its output slot and send a fake EOS event ++ * to drain the queue */ ++ slot->is_eos = TRUE; ++ BUFFERING_UNLOCK (urisrc); ++ ++ remove_buffering_msgs (urisrc, GST_OBJECT_CAST (slot->queue)); ++ ++ slot->linked_info = NULL; ++ + info->output_slot = NULL; ++ ++ GST_LOG_OBJECT (element, ++ "Pad %" GST_PTR_FORMAT " was removed without EOS. Sending.", pad); ++ ++ event = gst_event_new_eos (); ++ s = gst_event_writable_structure (event); ++ gst_structure_set (s, "urisourcebin-custom-eos", G_TYPE_BOOLEAN, TRUE, ++ NULL); ++ gst_pad_send_event (slot->sinkpad, event); + } else { + GST_LOG_OBJECT (urisrc, "Removed pad has no output slot"); + } +@@ -1619,7 +1389,6 @@ post_missing_plugin_error (GstElement * dec, const gchar * element_name) + GST_ELEMENT_ERROR (dec, CORE, MISSING_PLUGIN, + (_("Missing element '%s' - check your GStreamer installation."), + element_name), (NULL)); +- do_async_done (GST_URI_SOURCE_BIN (dec)); + } + + /** +@@ -1691,7 +1460,7 @@ analyse_source (GstURISourceBin * urisrc, gboolean * is_raw, + GST_URI_SOURCE_BIN_LOCK (urisrc); + if (use_queue) { + OutputSlotInfo *slot = get_output_slot (urisrc, FALSE, FALSE, NULL); +- if (slot) ++ if (!slot) + goto no_slot; + + gst_pad_link (pad, slot->sinkpad); +@@ -1700,13 +1469,16 @@ analyse_source (GstURISourceBin * urisrc, gboolean * is_raw, + gst_object_unref (pad); + pad = slot->srcpad; + } else { +- pad = create_output_pad (urisrc, pad); ++ GstPad *tmppad = create_output_pad (urisrc, pad); ++ gst_object_unref (pad); ++ ++ pad = tmppad; + } + GST_URI_SOURCE_BIN_UNLOCK (urisrc); + expose_output_pad (urisrc, pad); ++ } else { + gst_object_unref (pad); + } +- gst_object_unref (pad); + g_value_reset (&item); + break; + } +@@ -1768,6 +1540,7 @@ make_demuxer (GstURISourceBin * urisrc, GstCaps * caps) + { + GList *factories, *eligible, *cur; + GstElement *demuxer = NULL; ++ GParamSpec *pspec; + + GST_LOG_OBJECT (urisrc, "making new adaptive demuxer"); + +@@ -1814,9 +1587,11 @@ make_demuxer (GstURISourceBin * urisrc, GstCaps * caps) + "pad-removed", G_CALLBACK (pad_removed_cb), urisrc); + + /* Propagate connection-speed property */ +- /* FIXME: Check the property exists on the demuxer */ +- g_object_set (demuxer, +- "connection-speed", urisrc->connection_speed / 1000, NULL); ++ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (demuxer), ++ "connection-speed"); ++ if (pspec != NULL) ++ g_object_set (demuxer, ++ "connection-speed", urisrc->connection_speed / 1000, NULL); + + return demuxer; + +@@ -1826,7 +1601,6 @@ no_demuxer: + /* FIXME: Fire the right error */ + GST_ELEMENT_ERROR (urisrc, CORE, MISSING_PLUGIN, (NULL), + ("No demuxer element, check your installation")); +- do_async_done (urisrc); + return NULL; + } + } +@@ -1861,16 +1635,15 @@ handle_new_pad (GstURISourceBin * urisrc, GstPad * srcpad, GstCaps * caps) + urisrc->is_adaptive = IS_ADAPTIVE_MEDIA (media_type); + + if (urisrc->is_adaptive) { +- GstElement *demux_elem; + GstPad *sinkpad; + GstPadLinkReturn link_res; + +- demux_elem = make_demuxer (urisrc, caps); +- if (!demux_elem) ++ urisrc->demuxer = make_demuxer (urisrc, caps); ++ if (!urisrc->demuxer) + goto no_demuxer; +- gst_bin_add (GST_BIN_CAST (urisrc), demux_elem); ++ gst_bin_add (GST_BIN_CAST (urisrc), urisrc->demuxer); + +- sinkpad = gst_element_get_static_pad (demux_elem, "sink"); ++ sinkpad = gst_element_get_static_pad (urisrc->demuxer, "sink"); + if (sinkpad == NULL) + goto no_demuxer_sink; + +@@ -1880,7 +1653,14 @@ handle_new_pad (GstURISourceBin * urisrc, GstPad * srcpad, GstCaps * caps) + if (link_res != GST_PAD_LINK_OK) + goto could_not_link; + +- gst_element_sync_state_with_parent (demux_elem); ++ gst_element_sync_state_with_parent (urisrc->demuxer); ++ } else if (!urisrc->is_stream) { ++ GstPad *pad; ++ /* We don't need slot here, expose immediately */ ++ GST_URI_SOURCE_BIN_LOCK (urisrc); ++ pad = create_output_pad (urisrc, srcpad); ++ expose_output_pad (urisrc, pad); ++ GST_URI_SOURCE_BIN_UNLOCK (urisrc); + } else { + OutputSlotInfo *slot; + +@@ -1892,10 +1672,10 @@ handle_new_pad (GstURISourceBin * urisrc, GstPad * srcpad, GstCaps * caps) + gst_query_parse_duration (query, NULL, &dur); + do_download = (dur != -1); + } +- gst_object_unref (query); ++ gst_query_unref (query); + } + +- GST_DEBUG_OBJECT (urisrc, "check media-type %s, %d", media_type, ++ GST_DEBUG_OBJECT (urisrc, "check media-type %s, do_download:%d", media_type, + do_download); + + GST_URI_SOURCE_BIN_LOCK (urisrc); +@@ -1904,6 +1684,9 @@ handle_new_pad (GstURISourceBin * urisrc, GstPad * srcpad, GstCaps * caps) + if (slot == NULL || gst_pad_link (srcpad, slot->sinkpad) != GST_PAD_LINK_OK) + goto could_not_link; + ++ gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, ++ pre_queue_event_probe, urisrc, NULL); ++ + expose_output_pad (urisrc, slot->srcpad); + GST_URI_SOURCE_BIN_UNLOCK (urisrc); + } +@@ -1920,7 +1703,6 @@ no_demuxer_sink: + { + GST_ELEMENT_ERROR (urisrc, CORE, NEGOTIATION, + (NULL), ("Adaptive demuxer element has no 'sink' pad")); +- do_async_done (urisrc); + return; + } + could_not_link: +@@ -1928,7 +1710,6 @@ could_not_link: + GST_URI_SOURCE_BIN_UNLOCK (urisrc); + GST_ELEMENT_ERROR (urisrc, CORE, NEGOTIATION, + (NULL), ("Can't link typefind to adaptive demuxer element")); +- do_async_done (urisrc); + return; + } + } +@@ -1948,11 +1729,11 @@ type_found (GstElement * typefind, guint probability, + gst_object_unref (GST_OBJECT (srcpad)); + } + +-/* setup a streaming source. This will first plug a typefind element to the ++/* setup typefind for any source. This will first plug a typefind element to the + * source. After we find the type, we decide to whether to plug an adaptive +- * demuxer, or just link through queue2 and expose the data. */ ++ * demuxer, or just link through queue2 (if needed) and expose the data */ + static gboolean +-setup_streaming (GstURISourceBin * urisrc) ++setup_typefind (GstURISourceBin * urisrc, GstPad * srcpad) + { + GstElement *typefind; + +@@ -1961,18 +1742,33 @@ setup_streaming (GstURISourceBin * urisrc) + if (!typefind) + goto no_typefind; + ++ /* Make sure the bin doesn't set the typefind running yet */ ++ gst_element_set_locked_state (typefind, TRUE); ++ + gst_bin_add (GST_BIN_CAST (urisrc), typefind); + +- if (!gst_element_link_pads (urisrc->source, NULL, typefind, "sink")) +- goto could_not_link; ++ if (!srcpad) { ++ if (!gst_element_link_pads (urisrc->source, NULL, typefind, "sink")) ++ goto could_not_link; ++ } else { ++ GstPad *sinkpad = gst_element_get_static_pad (typefind, "sink"); ++ GstPadLinkReturn ret; ++ ++ ret = gst_pad_link (srcpad, sinkpad); ++ gst_object_unref (sinkpad); ++ if (ret != GST_PAD_LINK_OK) ++ goto could_not_link; ++ } + +- urisrc->typefind = typefind; ++ urisrc->typefinds = g_list_append (urisrc->typefinds, typefind); + + /* connect a signal to find out when the typefind element found + * a type */ +- urisrc->have_type_id = +- g_signal_connect (urisrc->typefind, "have-type", +- G_CALLBACK (type_found), urisrc); ++ g_signal_connect (typefind, "have-type", G_CALLBACK (type_found), urisrc); ++ ++ /* Now it can start */ ++ gst_element_set_locked_state (typefind, FALSE); ++ gst_element_sync_state_with_parent (typefind); + + return TRUE; + +@@ -1982,7 +1778,6 @@ no_typefind: + post_missing_plugin_error (GST_ELEMENT_CAST (urisrc), "typefind"); + GST_ELEMENT_ERROR (urisrc, CORE, MISSING_PLUGIN, (NULL), + ("No typefind element, check your installation")); +- do_async_done (urisrc); + return FALSE; + } + could_not_link: +@@ -1990,7 +1785,6 @@ could_not_link: + GST_ELEMENT_ERROR (urisrc, CORE, NEGOTIATION, + (NULL), ("Can't link source to typefind element")); + gst_bin_remove (GST_BIN_CAST (urisrc), typefind); +- do_async_done (urisrc); + return FALSE; + } + } +@@ -2015,6 +1809,23 @@ free_output_slot (OutputSlotInfo * slot, GstURISourceBin * urisrc) + g_free (slot); + } + ++static void ++call_free_output_slot (GstURISourceBin * urisrc, OutputSlotInfo * slot) ++{ ++ GST_LOG_OBJECT (urisrc, "free output slot in thread pool"); ++ free_output_slot (slot, urisrc); ++} ++ ++/* must be called with GST_URI_SOURCE_BIN_LOCK */ ++static void ++free_output_slot_async (GstURISourceBin * urisrc, OutputSlotInfo * slot) ++{ ++ GST_LOG_OBJECT (urisrc, "pushing output slot on thread pool to free"); ++ urisrc->out_slots = g_slist_remove (urisrc->out_slots, slot); ++ gst_element_call_async (GST_ELEMENT_CAST (urisrc), ++ (GstElementCallAsyncFunc) call_free_output_slot, slot, NULL); ++} ++ + /* remove source and all related elements */ + static void + remove_source (GstURISourceBin * urisrc) +@@ -2032,11 +1843,19 @@ remove_source (GstURISourceBin * urisrc) + gst_bin_remove (GST_BIN_CAST (urisrc), source); + urisrc->source = NULL; + } +- if (urisrc->typefind) { ++ if (urisrc->typefinds) { ++ GList *iter, *next; + GST_DEBUG_OBJECT (urisrc, "removing old typefind element"); +- gst_element_set_state (urisrc->typefind, GST_STATE_NULL); +- gst_bin_remove (GST_BIN_CAST (urisrc), urisrc->typefind); +- urisrc->typefind = NULL; ++ for (iter = urisrc->typefinds; iter; iter = next) { ++ GstElement *typefind = iter->data; ++ ++ next = g_list_next (iter); ++ ++ gst_element_set_state (typefind, GST_STATE_NULL); ++ gst_bin_remove (GST_BIN_CAST (urisrc), typefind); ++ } ++ g_list_free (urisrc->typefinds); ++ urisrc->typefinds = NULL; + } + + GST_URI_SOURCE_BIN_LOCK (urisrc); +@@ -2063,9 +1882,11 @@ source_new_pad (GstElement * element, GstPad * pad, GstURISourceBin * urisrc) + GST_DEBUG_PAD_NAME (pad), GST_ELEMENT_NAME (element)); + caps = gst_pad_get_current_caps (pad); + if (caps == NULL) +- caps = gst_pad_query_caps (pad, NULL); +- handle_new_pad (urisrc, pad, caps); +- gst_caps_unref (caps); ++ setup_typefind (urisrc, pad); ++ else { ++ handle_new_pad (urisrc, pad, caps); ++ gst_caps_unref (caps); ++ } + } + + static gboolean +@@ -2131,7 +1952,6 @@ setup_source (GstURISourceBin * urisrc) + /* source provides raw data, we added the pads and we can now signal a + * no_more pads because we are done. */ + gst_element_no_more_pads (GST_ELEMENT_CAST (urisrc)); +- do_async_done (urisrc); + return TRUE; + } + if (!have_out && !is_dynamic) { +@@ -2148,7 +1968,7 @@ setup_source (GstURISourceBin * urisrc) + if (urisrc->is_stream) { + GST_DEBUG_OBJECT (urisrc, "Setting up streaming"); + /* do the stream things here */ +- if (!setup_streaming (urisrc)) ++ if (!setup_typefind (urisrc, NULL)) + goto streaming_failed; + } else { + GstIterator *pads_iter; +@@ -2172,18 +1992,15 @@ setup_source (GstURISourceBin * urisrc) + break; + case GST_ITERATOR_OK: + pad = g_value_get_object (&item); +- /* no streaming source, expose pads directly */ +- GST_URI_SOURCE_BIN_LOCK (urisrc); +- pad = create_output_pad (urisrc, pad); +- GST_URI_SOURCE_BIN_UNLOCK (urisrc); +- expose_output_pad (urisrc, pad); ++ if (!setup_typefind (urisrc, pad)) { ++ gst_iterator_free (pads_iter); ++ goto streaming_failed; ++ } + g_value_reset (&item); + break; + } + } + gst_iterator_free (pads_iter); +- gst_element_no_more_pads (GST_ELEMENT_CAST (urisrc)); +- do_async_done (urisrc); + } + } + return TRUE; +@@ -2296,7 +2113,7 @@ handle_redirect_message (GstURISourceBin * dec, GstMessage * msg) + return new_msg; + } + +-static GstMessage * ++static void + handle_buffering_message (GstURISourceBin * urisrc, GstMessage * msg) + { + gint perc, msg_perc; +@@ -2304,6 +2121,7 @@ handle_buffering_message (GstURISourceBin * urisrc, GstMessage * msg) + GstMessage *smaller = NULL; + GList *found = NULL; + GList *iter; ++ OutputSlotInfo *slot; + + /* buffering messages must be aggregated as there might be multiple + * multiqueue in the pipeline and their independent buffering messages +@@ -2323,7 +2141,22 @@ handle_buffering_message (GstURISourceBin * urisrc, GstMessage * msg) + GST_LOG_OBJECT (urisrc, "Got buffering msg from %" GST_PTR_FORMAT + " with %d%%", GST_MESSAGE_SRC (msg), msg_perc); + +- GST_OBJECT_LOCK (urisrc); ++ slot = g_object_get_data (G_OBJECT (GST_MESSAGE_SRC (msg)), ++ "urisourcebin.slotinfo"); ++ ++ BUFFERING_LOCK (urisrc); ++ if (slot && slot->is_eos) { ++ /* Ignore buffering messages from queues we marked as EOS, ++ * we already removed those from the list of buffering ++ * objects */ ++ BUFFERING_UNLOCK (urisrc); ++ gst_message_replace (&msg, NULL); ++ return; ++ } ++ ++ ++ g_mutex_lock (&urisrc->buffering_post_lock); ++ + /* + * Single loop for 2 things: + * 1) Look for a message with the same source +@@ -2332,11 +2165,10 @@ handle_buffering_message (GstURISourceBin * urisrc, GstMessage * msg) + */ + for (iter = urisrc->buffering_status; iter;) { + GstMessage *bufstats = iter->data; +- OutputSlotInfo *slot = +- g_object_get_data (G_OBJECT (GST_MESSAGE_SRC (bufstats)), +- "urisourcebin.slotinfo"); + gboolean is_eos = FALSE; + ++ slot = g_object_get_data (G_OBJECT (GST_MESSAGE_SRC (bufstats)), ++ "urisourcebin.slotinfo"); + if (slot) + is_eos = slot->is_eos; + +@@ -2395,16 +2227,17 @@ handle_buffering_message (GstURISourceBin * urisrc, GstMessage * msg) + gst_message_replace (&msg, smaller); + } + } +- GST_OBJECT_UNLOCK (urisrc); ++ BUFFERING_UNLOCK (urisrc); + + if (msg) { + GST_LOG_OBJECT (urisrc, "Sending buffering msg from %" GST_PTR_FORMAT + " with %d%%", GST_MESSAGE_SRC (msg), smaller_perc); ++ GST_BIN_CLASS (parent_class)->handle_message (GST_BIN (urisrc), msg); + } else { + GST_LOG_OBJECT (urisrc, "Dropped buffering msg as a repeat of %d%%", + smaller_perc); + } +- return msg; ++ g_mutex_unlock (&urisrc->buffering_post_lock); + } + + /* Remove any buffering message from the given source */ +@@ -2412,19 +2245,38 @@ static void + remove_buffering_msgs (GstURISourceBin * urisrc, GstObject * src) + { + GList *iter; ++ gboolean removed = FALSE, post; ++ ++ BUFFERING_LOCK (urisrc); ++ g_mutex_lock (&urisrc->buffering_post_lock); ++ ++ GST_DEBUG_OBJECT (urisrc, "Removing %" GST_PTR_FORMAT ++ " buffering messages", src); + +- GST_OBJECT_LOCK (urisrc); + for (iter = urisrc->buffering_status; iter;) { + GstMessage *bufstats = iter->data; + if (GST_MESSAGE_SRC (bufstats) == src) { + gst_message_unref (bufstats); + urisrc->buffering_status = + g_list_delete_link (urisrc->buffering_status, iter); ++ removed = TRUE; + break; + } + iter = g_list_next (iter); + } +- GST_OBJECT_UNLOCK (urisrc); ++ ++ post = (removed && urisrc->buffering_status == NULL); ++ BUFFERING_UNLOCK (urisrc); ++ ++ if (post) { ++ GST_DEBUG_OBJECT (urisrc, "Last buffering element done - posting 100%%"); ++ ++ /* removed the last buffering element, post 100% */ ++ gst_element_post_message (GST_ELEMENT_CAST (urisrc), ++ gst_message_new_buffering (GST_OBJECT_CAST (urisrc), 100)); ++ } ++ ++ g_mutex_unlock (&urisrc->buffering_post_lock); + } + + static void +@@ -2444,7 +2296,8 @@ handle_message (GstBin * bin, GstMessage * msg) + break; + } + case GST_MESSAGE_BUFFERING: +- msg = handle_buffering_message (urisrc, msg); ++ handle_buffering_message (urisrc, msg); ++ msg = NULL; + break; + default: + break; +@@ -2732,12 +2585,6 @@ done: + return res; + } + +-static void +-sync_slot_queue (OutputSlotInfo * slot) +-{ +- gst_element_sync_state_with_parent (slot->queue); +-} +- + static GstStateChangeReturn + gst_uri_source_bin_change_state (GstElement * element, + GstStateChange transition) +@@ -2747,7 +2594,9 @@ gst_uri_source_bin_change_state (GstElement * element, + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: +- do_async_start (urisrc); ++ GST_DEBUG ("ready to paused"); ++ if (!setup_source (urisrc)) ++ goto source_failed; + break; + default: + break; +@@ -2756,36 +2605,14 @@ gst_uri_source_bin_change_state (GstElement * element, + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + goto setup_failed; +- else if (ret == GST_STATE_CHANGE_NO_PREROLL) +- do_async_done (urisrc); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: +- GST_DEBUG ("ready to paused"); +- if (!setup_source (urisrc)) +- goto source_failed; +- +- ret = GST_STATE_CHANGE_ASYNC; +- +- /* And now sync the states of everything we added */ +- g_slist_foreach (urisrc->out_slots, (GFunc) sync_slot_queue, NULL); +- if (urisrc->typefind) +- ret = gst_element_set_state (urisrc->typefind, GST_STATE_PAUSED); +- if (ret == GST_STATE_CHANGE_FAILURE) +- goto setup_failed; +- if (urisrc->source) +- ret = gst_element_set_state (urisrc->source, GST_STATE_PAUSED); +- if (ret == GST_STATE_CHANGE_FAILURE) +- goto setup_failed; +- if (ret == GST_STATE_CHANGE_SUCCESS) +- ret = GST_STATE_CHANGE_ASYNC; +- + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_DEBUG ("paused to ready"); + remove_demuxer (urisrc); + remove_source (urisrc); +- do_async_done (urisrc); + g_list_free_full (urisrc->buffering_status, + (GDestroyNotify) gst_message_unref); + urisrc->buffering_status = NULL; +@@ -2804,13 +2631,11 @@ gst_uri_source_bin_change_state (GstElement * element, + /* ERRORS */ + source_failed: + { +- do_async_done (urisrc); + return GST_STATE_CHANGE_FAILURE; + } + setup_failed: + { + /* clean up leftover groups */ +- do_async_done (urisrc); + return GST_STATE_CHANGE_FAILURE; + } + } +@@ -2822,5 +2647,5 @@ gst_uri_source_bin_plugin_init (GstPlugin * plugin) + "URI source element"); + + return gst_element_register (plugin, "urisourcebin", GST_RANK_NONE, +- GST_TYPE_URI_DECODE_BIN); ++ GST_TYPE_URI_SOURCE_BIN); + } +2.18.0 + diff --git a/package/gstreamer1/gstreamer1/0001-Backport-GstStreamCollection-from-1.14.patch b/package/gstreamer1/gstreamer1/0001-Backport-GstStreamCollection-from-1.14.patch new file mode 100644 index 000000000000..4b3bc55eb05d --- /dev/null +++ b/package/gstreamer1/gstreamer1/0001-Backport-GstStreamCollection-from-1.14.patch @@ -0,0 +1,151 @@ +From d1939242bafd17959b033f2d01e33646a2125d6f Mon Sep 17 00:00:00 2001 +From: Thibault Saunier +Date: Wed, 5 Sep 2018 09:28:46 -0300 +Subject: [PATCH 1/2] Backport GstStreamCollection from 1.14 + +--- + gst/gststreamcollection.c | 31 ++++++++++++++++++++++++++----- + gst/gststreamcollection.h | 11 +++++++++-- + 2 files changed, 35 insertions(+), 7 deletions(-) + +diff --git a/gst/gststreamcollection.c b/gst/gststreamcollection.c +index 8e848ded9..f1fa1a41a 100644 +--- a/gst/gststreamcollection.c ++++ b/gst/gststreamcollection.c +@@ -26,6 +26,7 @@ + + /** + * SECTION:gststreamcollection ++ * @title: GstStreamCollection + * @short_description: Base class for collection of streams + * + * Since: 1.10 +@@ -67,6 +68,7 @@ enum + static guint gst_stream_collection_signals[LAST_SIGNAL] = { 0 }; + + static void gst_stream_collection_dispose (GObject * object); ++static void gst_stream_collection_finalize (GObject * object); + + static void gst_stream_collection_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +@@ -128,6 +130,7 @@ gst_stream_collection_class_init (GstStreamCollectionClass * klass) + 2, GST_TYPE_STREAM, G_TYPE_PARAM); + + gobject_class->dispose = gst_stream_collection_dispose; ++ gobject_class->finalize = gst_stream_collection_finalize; + } + + static void +@@ -158,28 +161,46 @@ gst_stream_collection_dispose (GObject * object) + if (collection->priv->streams) { + g_queue_foreach (collection->priv->streams, + (GFunc) release_gst_stream, collection); +- g_queue_free (collection->priv->streams); +- collection->priv->streams = NULL; ++ g_queue_clear (collection->priv->streams); + } + + G_OBJECT_CLASS (parent_class)->dispose (object); + } + ++static void ++gst_stream_collection_finalize (GObject * object) ++{ ++ GstStreamCollection *collection = GST_STREAM_COLLECTION_CAST (object); ++ ++ if (collection->priv->streams) ++ g_queue_free (collection->priv->streams); ++ ++ G_OBJECT_CLASS (parent_class)->finalize (object); ++} ++ + /** + * gst_stream_collection_new: + * @upstream_id: (allow-none): The stream id of the parent stream + * + * Create a new #GstStreamCollection. + * +- * Returns: The new #GstStreamCollection. ++ * Returns: (transfer full): The new #GstStreamCollection. + * + * Since: 1.10 + */ + GstStreamCollection * + gst_stream_collection_new (const gchar * upstream_id) + { +- return g_object_new (GST_TYPE_STREAM_COLLECTION, "upstream-id", upstream_id, ++ GstStreamCollection *collection; ++ ++ collection = ++ g_object_new (GST_TYPE_STREAM_COLLECTION, "upstream-id", upstream_id, + NULL); ++ ++ /* Clear floating flag */ ++ g_object_ref_sink (collection); ++ ++ return collection; + } + + static void +@@ -321,7 +342,7 @@ gst_stream_collection_get_size (GstStreamCollection * collection) + * + * The caller should not modify the returned #GstStream + * +- * Returns: (transfer none): A #GstStream ++ * Returns: (transfer none) (nullable): A #GstStream + * + * Since: 1.10 + */ +diff --git a/gst/gststreamcollection.h b/gst/gststreamcollection.h +index 49734bd7c..4fe69c266 100644 +--- a/gst/gststreamcollection.h ++++ b/gst/gststreamcollection.h +@@ -26,7 +26,6 @@ + #define __GST_STREAM_COLLECTION_H__ + + #include +-#include + + G_BEGIN_DECLS + +@@ -42,6 +41,9 @@ typedef struct _GstStreamCollection GstStreamCollection; + typedef struct _GstStreamCollectionClass GstStreamCollectionClass; + typedef struct _GstStreamCollectionPrivate GstStreamCollectionPrivate; + ++#include ++#include ++ + /** + * GstStreamCollection: + * +@@ -65,9 +67,9 @@ typedef struct _GstStreamCollectionPrivate GstStreamCollectionPrivate; + * Since: 1.10 + */ + struct _GstStreamCollection { ++ /*< private >*/ + GstObject object; + +- /*< private >*/ + gchar *upstream_id; + GstStreamCollectionPrivate *priv; + +@@ -98,11 +100,16 @@ GstStreamCollection *gst_stream_collection_new (const gchar *upstream_id); + const gchar *gst_stream_collection_get_upstream_id (GstStreamCollection *collection); + + guint gst_stream_collection_get_size (GstStreamCollection *collection); ++ + GstStream *gst_stream_collection_get_stream (GstStreamCollection *collection, guint index); + + gboolean gst_stream_collection_add_stream (GstStreamCollection *collection, + GstStream *stream); + ++#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstStreamCollection, gst_object_unref) ++#endif ++ + G_END_DECLS + + #endif /* __GST_STREAM_COLLECTION_H__ */ +-- +2.18.0 + diff --git a/package/gstreamer1/gstreamer1/0002-Backport-GstStream-from-1.14.patch b/package/gstreamer1/gstreamer1/0002-Backport-GstStream-from-1.14.patch new file mode 100644 index 000000000000..66113a275eba --- /dev/null +++ b/package/gstreamer1/gstreamer1/0002-Backport-GstStream-from-1.14.patch @@ -0,0 +1,156 @@ +From e25748b258dcaa646819c8639d60f8f49a6f0265 Mon Sep 17 00:00:00 2001 +From: Thibault Saunier +Date: Wed, 5 Sep 2018 09:30:11 -0300 +Subject: [PATCH 2/2] Backport GstStream from 1.14 + +--- + gst/gststreams.c | 14 +++++++++++--- + gst/gststreams.h | 38 ++++++++++++++++++++++++++------------ + 2 files changed, 37 insertions(+), 15 deletions(-) + +diff --git a/gst/gststreams.c b/gst/gststreams.c +index 28741d8b2..9c3272dfe 100644 +--- a/gst/gststreams.c ++++ b/gst/gststreams.c +@@ -26,6 +26,7 @@ + + /** + * SECTION:gststreams ++ * @title: GstStreams + * @short_description: Base class for stream objects + * + * A #GstStream is a high-level object defining a stream of data which is, or +@@ -209,7 +210,7 @@ gst_stream_finalize (GObject * object) + * Create a new #GstStream for the given @stream_id, @caps, @type + * and @flags + * +- * Returns: The new #GstStream ++ * Returns: (transfer full): The new #GstStream + * + * Since: 1.10 + */ +@@ -217,8 +218,15 @@ GstStream * + gst_stream_new (const gchar * stream_id, GstCaps * caps, GstStreamType type, + GstStreamFlags flags) + { +- return g_object_new (GST_TYPE_STREAM, "stream-id", stream_id, "caps", caps, ++ GstStream *stream; ++ ++ stream = g_object_new (GST_TYPE_STREAM, "stream-id", stream_id, "caps", caps, + "stream-type", type, "stream-flags", flags, NULL); ++ ++ /* Clear floating flag */ ++ gst_object_ref_sink (stream); ++ ++ return stream; + } + + static void +@@ -517,7 +525,7 @@ gst_stream_get_property (GObject * object, guint prop_id, + * + * Get a descriptive string for a given #GstStreamType + * +- * Returns: A string describing the stream type ++ * Returns: (nullable): A string describing the stream type + * + * Since: 1.10 + */ +diff --git a/gst/gststreams.h b/gst/gststreams.h +index 2df1c7630..4520b84b6 100644 +--- a/gst/gststreams.h ++++ b/gst/gststreams.h +@@ -26,7 +26,6 @@ + #define __GST_STREAMS_H__ + + #include +-#include + + G_BEGIN_DECLS + +@@ -52,6 +51,8 @@ G_BEGIN_DECLS + * Note that this is a flag, and therefore users should not assume it + * will be a single value. Do not use the equality operator for checking + * whether a stream is of a certain type. ++ * ++ * Since: 1.10 + */ + typedef enum { + GST_STREAM_TYPE_UNKNOWN = 1 << 0, +@@ -69,7 +70,7 @@ typedef struct _GstStreamPrivate GstStreamPrivate; + /** + * GstStream: + * @stream_id: The Stream Identifier for this #GstStream +- * ++ * + * A high-level object representing a single stream. It might be backed, or + * not, by an actual flow of data in a pipeline (#GstPad). + * +@@ -82,8 +83,11 @@ typedef struct _GstStreamPrivate GstStreamPrivate; + * + * Elements can subclass a #GstStream for internal usage (to contain information + * pertinent to streams of data). ++ * ++ * Since: 1.10 + */ + struct _GstStream { ++ /*< private >*/ + GstObject object; + + /*< public >*/ +@@ -103,33 +107,43 @@ struct _GstStream { + */ + struct _GstStreamClass { + GstObjectClass parent_class; +- ++ + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; + }; + + GType gst_stream_get_type (void); + ++#include ++ + GstStream *gst_stream_new (const gchar *stream_id, + GstCaps *caps, + GstStreamType type, + GstStreamFlags flags); +- +-const gchar *gst_stream_get_stream_id (GstStream *stream); ++const gchar * gst_stream_get_stream_id (GstStream *stream); + + void gst_stream_set_stream_flags (GstStream *stream, GstStreamFlags flags); ++ + GstStreamFlags gst_stream_get_stream_flags (GstStream *stream); + +-void gst_stream_set_stream_type (GstStream *stream, GstStreamType stream_type); +-GstStreamType gst_stream_get_stream_type (GstStream *stream); ++void gst_stream_set_stream_type (GstStream *stream, GstStreamType stream_type); ++ ++GstStreamType gst_stream_get_stream_type (GstStream *stream); ++ ++void gst_stream_set_tags (GstStream *stream, GstTagList *tags); ++ ++GstTagList * gst_stream_get_tags (GstStream *stream); ++ ++void gst_stream_set_caps (GstStream *stream, GstCaps *caps); ++ ++GstCaps * gst_stream_get_caps (GstStream *stream); + +-void gst_stream_set_tags (GstStream *stream, GstTagList *tags); +-GstTagList *gst_stream_get_tags (GstStream *stream); ++const gchar * gst_stream_type_get_name (GstStreamType stype); + +-void gst_stream_set_caps (GstStream *stream, GstCaps *caps); +-GstCaps *gst_stream_get_caps (GstStream *stream); ++#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstStream, gst_object_unref) ++#endif + +-const gchar *gst_stream_type_get_name (GstStreamType stype); + G_END_DECLS + + #endif /* __GST_STREAMS_H__ */ +-- +2.18.0 + diff --git a/package/gstreamer1/gstreamer1/gstreamer-0001-utils-Never-return-a-group_id-of-0-add-GST_GROUP_ID_.patch b/package/gstreamer1/gstreamer1/gstreamer-0001-utils-Never-return-a-group_id-of-0-add-GST_GROUP_ID_.patch new file mode 100644 index 000000000000..7d50bfe1d245 --- /dev/null +++ b/package/gstreamer1/gstreamer1/gstreamer-0001-utils-Never-return-a-group_id-of-0-add-GST_GROUP_ID_.patch @@ -0,0 +1,84 @@ +From f11000c5455aa88b2519e0c6ee730f5150d44fe0 Mon Sep 17 00:00:00 2001 +From: Edward Hervey +Date: Tue, 5 Dec 2017 17:28:55 +0100 +Subject: [PATCH] utils: Never return a group_id of 0, add GST_GROUP_ID_INVALID + +Various plugins use special values (0 or G_MAXUINT32) as an +invalid/unset group_id, but nothing guarantees a groupid won't have +that value. + +Instead define a value which group_id will never have and make +gst_group_id_next() always return a value different from that. + +API: GST_GROUP_ID_INVALID +--- + docs/gst/gstreamer-sections.txt | 1 + + gst/gstutils.c | 12 ++++++++++-- + gst/gstutils.h | 12 ++++++++++++ + 3 files changed, 23 insertions(+), 2 deletions(-) + +diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt +index 935515577..8aa76d545 100644 +--- a/docs/gst/gstreamer-sections.txt ++++ b/docs/gst/gstreamer-sections.txt +@@ -3486,6 +3486,7 @@ GFLOAT_FROM_LE + GFLOAT_SWAP_LE_BE + GFLOAT_TO_BE + GFLOAT_TO_LE ++GST_GROUP_ID_INVALID + + gst_guint64_to_gdouble + gst_gdouble_to_guint64 +diff --git a/gst/gstutils.c b/gst/gstutils.c +index e64b41fba..cb3be74ce 100644 +--- a/gst/gstutils.c ++++ b/gst/gstutils.c +@@ -4037,12 +4037,20 @@ gst_pad_get_stream (GstPad * pad) + * This function is used to generate a new group-id for the + * stream-start event. + * ++ * This function never returns %GST_GROUP_ID_INVALID (which is 0) ++ * + * Returns: A constantly incrementing unsigned integer, which might + * overflow back to 0 at some point. + */ + guint + gst_util_group_id_next (void) + { +- static gint counter = 0; +- return g_atomic_int_add (&counter, 1); ++ static gint counter = 1; ++ gint ret = g_atomic_int_add (&counter, 1); ++ ++ /* Make sure we don't return GST_GROUP_ID_INVALID */ ++ if (G_UNLIKELY (ret == GST_GROUP_ID_INVALID)) ++ ret = g_atomic_int_add (&counter, 1); ++ ++ return ret; + } +diff --git a/gst/gstutils.h b/gst/gstutils.h +index 9360c53e4..433ba024d 100644 +--- a/gst/gstutils.h ++++ b/gst/gstutils.h +@@ -75,6 +75,18 @@ guint64 gst_util_uint64_scale_int_ceil (guint64 val, gint num, gint den + guint32 gst_util_seqnum_next (void); + gint32 gst_util_seqnum_compare (guint32 s1, guint32 s2); + ++/** ++ * GST_GROUP_ID_INVALID: ++ * ++ * A value which is guaranteed to never be returned by ++ * gst_util_group_id_next(). ++ * ++ * Can be used as a default value in variables used to store group_id. ++ * ++ * Since: 1.14 ++ */ ++#define GST_GROUP_ID_INVALID (0) ++ + guint gst_util_group_id_next (void); + + /** +-- +2.18.0 + diff --git a/package/libvpx/0001-vpx_mem-vpx_mem.h-Fix-compilation-with-uClibc.patch b/package/libvpx/0001-vpx_mem-vpx_mem.h-Fix-compilation-with-uClibc.patch deleted file mode 100644 index 20f08c905a0b..000000000000 --- a/package/libvpx/0001-vpx_mem-vpx_mem.h-Fix-compilation-with-uClibc.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 90845a3a263e0f37b9c756c0b01377b9d1a225c9 Mon Sep 17 00:00:00 2001 -From: Bernd Kuhls -Date: Sun, 31 Jul 2016 10:37:05 +0200 -Subject: [PATCH] vpx_mem/vpx_mem.h: do not include - -The header does not exist in most C libraries, and including -it causes build failures. - -Signed-off-by: Bernd Kuhls ---- - vpx_mem/vpx_mem.h | 3 --- - 1 file changed, 3 deletions(-) - -diff --git a/vpx_mem/vpx_mem.h b/vpx_mem/vpx_mem.h -index c14f288..82df745 100644 ---- a/vpx_mem/vpx_mem.h -+++ b/vpx_mem/vpx_mem.h -@@ -12,9 +12,6 @@ - #define VPX_MEM_VPX_MEM_H_ - - #include "vpx_config.h" --#if defined(__uClinux__) --# include --#endif - - #include - #include --- -2.8.1 - diff --git a/package/libvpx/libvpx.mk b/package/libvpx/libvpx.mk index 4ad552249841..5a04b42ebf51 100644 --- a/package/libvpx/libvpx.mk +++ b/package/libvpx/libvpx.mk @@ -4,9 +4,9 @@ # ################################################################################ -LIBVPX_VERSION = 1.6.0 -LIBVPX_SOURCE = libvpx-$(LIBVPX_VERSION).tar.bz2 -LIBVPX_SITE = http://storage.googleapis.com/downloads.webmproject.org/releases/webm +LIBVPX_VERSION = ec4afbf74a4beebadee3e1b15b43c5d4e3d3bd1c +LIBVPX_SITE_METHOD = git +LIBVPX_SITE = https://chromium.googlesource.com/webm/libvpx.git LIBVPX_LICENSE = BSD-3c LIBVPX_LICENSE_FILES = LICENSE PATENTS LIBVPX_INSTALL_STAGING = YES diff --git a/package/wpe/wpewebkit/Config.in b/package/wpe/wpewebkit/Config.in index c90d905f5edb..2aeb622e2eee 100644 --- a/package/wpe/wpewebkit/Config.in +++ b/package/wpe/wpewebkit/Config.in @@ -10,8 +10,14 @@ config BR2_PACKAGE_WPEWEBKIT select BR2_PACKAGE_FONTCONFIG if !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC select BR2_PACKAGE_GLIB_NETWORKING if !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC select BR2_PACKAGE_GST1_PLUGINS_BASE if BR2_PACKAGE_GSTREAMER1 && !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC + select BR2_PACKAGE_GST1_PLUGINS_BASE if BR2_PACKAGE_GSTREAMER1 && !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC select BR2_PACKAGE_GST1_PLUGINS_GOOD if BR2_PACKAGE_GSTREAMER1 && !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC select BR2_PACKAGE_GST1_PLUGINS_BAD if BR2_PACKAGE_GSTREAMER1 && !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC + select BR2_PACKAGE_GST1_PLUGINS_BAD if BR2_PACKAGE_GSTREAMER1 && !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC + select BR2_PACKAGE_ALSA_LIB if BR2_PACKAGE_GSTREAMER1 && !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC + select BR2_PACKAGE_ALSA_LIB_MIXER if BR2_PACKAGE_GSTREAMER1 && !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC + select BR2_PACKAGE_ALSA_LIB_PCM if BR2_PACKAGE_GSTREAMER1 && !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC + select BR2_PACKAGE_ALSA_LIB_SEQ if BR2_PACKAGE_GSTREAMER1 && !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC select BR2_PACKAGE_HARFBUZZ if !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC select BR2_PACKAGE_ICU if !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC select BR2_PACKAGE_JPEG if !BR2_PACKAGE_WPEWEBKIT_ONLY_JSC diff --git a/package/wpe/wpewebkit/wpewebkit.mk b/package/wpe/wpewebkit/wpewebkit.mk index d1e3a9e8d2e1..7dbf2c1a2c2c 100644 --- a/package/wpe/wpewebkit/wpewebkit.mk +++ b/package/wpe/wpewebkit/wpewebkit.mk @@ -31,7 +31,7 @@ WPEWEBKIT_DEPENDENCIES = host-bison host-cmake host-flex host-gperf host-ruby ic ifeq ($(WPEWEBKIT_BUILD_WEBKIT),y) WPEWEBKIT_DEPENDENCIES += wpebackend libgcrypt libgles libegl libepoxy cairo freetype \ - fontconfig harfbuzz libxml2 libxslt sqlite libsoup jpeg libpng + fontconfig harfbuzz libxml2 libxslt sqlite libsoup jpeg libpng libvpx libevent alsa-lib endif WPEWEBKIT_EXTRA_FLAGS = -DCMAKE_EXPORT_COMPILE_COMMANDS=ON @@ -68,7 +68,9 @@ WPEWEBKIT_FLAGS = \ -DENABLE_INDEXED_DATABASE=ON \ -DENABLE_MEDIA_STATISTICS=ON \ -DENABLE_FETCH_API=ON \ - -DENABLE_WEBDRIVER=ON + -DENABLE_WEBDRIVER=ON \ + -DENABLE_WEB_RTC=ON\ + -DENABLE_MEDIA_STREAM=ON ifeq ($(BR2_TOOLCHAIN_USES_MUSL),y) WPEWEBKIT_FLAGS += -DENABLE_SAMPLING_PROFILER=OFF