summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Schmidt <thaytan@mad.scientist.com>2008-06-17 01:08:14 +0000
committerJan Schmidt <thaytan@mad.scientist.com>2008-06-17 01:08:14 +0000
commit0951e00dc05236b54a03e25c2c331bf1be332dc5 (patch)
tree553db40a5b2a59e9615e2db5a10a5927e35bcfab
parentbcc41766b852bae2a8014c87bc2cb67ff7453f6a (diff)
downloadgst-plugins-bad-0951e00dc05236b54a03e25c2c331bf1be332dc5.tar.gz
gst-plugins-bad-0951e00dc05236b54a03e25c2c331bf1be332dc5.tar.bz2
gst-plugins-bad-0951e00dc05236b54a03e25c2c331bf1be332dc5.zip
configure.ac: Check for libdvdnav to build resindvd.
Original commit message from CVS: * configure.ac: Check for libdvdnav to build resindvd. * ext/Makefile.am: * ext/resindvd/Makefile.am: * ext/resindvd/gstmpegdefs.h: * ext/resindvd/gstmpegdemux.c: * ext/resindvd/gstmpegdemux.h: * ext/resindvd/gstmpegdesc.c: * ext/resindvd/gstmpegdesc.h: * ext/resindvd/gstpesfilter.c: * ext/resindvd/gstpesfilter.h: * ext/resindvd/plugin.c: * ext/resindvd/resin-play: * ext/resindvd/resindvdbin.c: * ext/resindvd/resindvdbin.h: * ext/resindvd/resindvdsrc.c: * ext/resindvd/resindvdsrc.h: * ext/resindvd/rsnaudiomunge.c: * ext/resindvd/rsnaudiomunge.h: * ext/resindvd/rsnbasesrc.c: * ext/resindvd/rsnbasesrc.h: * ext/resindvd/rsnpushsrc.c: * ext/resindvd/rsnpushsrc.h: * ext/resindvd/rsnstreamselector.c: * ext/resindvd/rsnstreamselector.h: First commit of DVD-Video playback component 'rsndvdbin' and helper elements. Use --enable-experimental for now, but feel free to give it a try using the resin-play script. * gst/dvdspu/gstdvdspu.c: Add some extra guards for malformed events.
-rw-r--r--ChangeLog38
-rw-r--r--configure.ac33
-rw-r--r--ext/Makefile.am6
-rw-r--r--ext/resindvd/Makefile.am30
-rw-r--r--ext/resindvd/gstmpegdefs.h175
-rw-r--r--ext/resindvd/gstmpegdemux.c1943
-rw-r--r--ext/resindvd/gstmpegdemux.h127
-rw-r--r--ext/resindvd/gstmpegdesc.c156
-rw-r--r--ext/resindvd/gstmpegdesc.h249
-rw-r--r--ext/resindvd/gstpesfilter.c623
-rw-r--r--ext/resindvd/gstpesfilter.h88
-rw-r--r--ext/resindvd/plugin.c53
-rwxr-xr-xext/resindvd/resin-play11
-rw-r--r--ext/resindvd/resindvdbin.c758
-rw-r--r--ext/resindvd/resindvdbin.h80
-rw-r--r--ext/resindvd/resindvdsrc.c1426
-rw-r--r--ext/resindvd/resindvdsrc.h113
-rw-r--r--ext/resindvd/rsnaudiomunge.c378
-rw-r--r--ext/resindvd/rsnaudiomunge.h61
-rw-r--r--ext/resindvd/rsnbasesrc.c2423
-rw-r--r--ext/resindvd/rsnbasesrc.h257
-rw-r--r--ext/resindvd/rsnpushsrc.c101
-rw-r--r--ext/resindvd/rsnpushsrc.h76
-rw-r--r--ext/resindvd/rsnstreamselector.c784
-rw-r--r--ext/resindvd/rsnstreamselector.h62
-rw-r--r--gst/dvdspu/gstdvdspu.c10
26 files changed, 10061 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index d15a1c7c..e3c4500c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,41 @@
+2008-06-17 Jan Schmidt <thaytan@noraisin.net>
+
+ * configure.ac:
+ Check for libdvdnav to build resindvd.
+
+ * ext/Makefile.am:
+ * ext/resindvd/Makefile.am:
+ * ext/resindvd/gstmpegdefs.h:
+ * ext/resindvd/gstmpegdemux.c:
+ * ext/resindvd/gstmpegdemux.h:
+ * ext/resindvd/gstmpegdesc.c:
+ * ext/resindvd/gstmpegdesc.h:
+ * ext/resindvd/gstpesfilter.c:
+ * ext/resindvd/gstpesfilter.h:
+ * ext/resindvd/plugin.c:
+ * ext/resindvd/resin-play:
+ * ext/resindvd/resindvdbin.c:
+ * ext/resindvd/resindvdbin.h:
+ * ext/resindvd/resindvdsrc.c:
+ * ext/resindvd/resindvdsrc.h:
+ * ext/resindvd/rsnaudiomunge.c:
+ * ext/resindvd/rsnaudiomunge.h:
+ * ext/resindvd/rsnbasesrc.c:
+ * ext/resindvd/rsnbasesrc.h:
+ * ext/resindvd/rsnpushsrc.c:
+ * ext/resindvd/rsnpushsrc.h:
+ * ext/resindvd/rsnstreamselector.c:
+ * ext/resindvd/rsnstreamselector.h:
+
+ First commit of DVD-Video playback component 'rsndvdbin'
+ and helper elements.
+
+ Use --enable-experimental for now, but feel free to give it a
+ try using the resin-play script.
+
+ * gst/dvdspu/gstdvdspu.c:
+ Add some extra guards for malformed events.
+
2008-06-16 David Schleef <ds@schleef.org>
* configure.ac:
diff --git a/configure.ac b/configure.ac
index e818fa7b..ff70aeae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -477,6 +477,38 @@ return 0;
fi
])
+dnl *** dvdnav for resindvd ***
+USE_DVDNAV=$BUILD_EXPERIMENTAL
+translit(dnm, m, l) AM_CONDITIONAL(USE_DVDNAV, true)
+AG_GST_CHECK_FEATURE(DVDNAV, [dvdnav library], resindvd, [
+ translit(dnm, m, l) AC_SUBST(DVDNAV_LIBS)
+ translit(dnm, m, l) AC_SUBST(DVDNAV_CFLAGS)
+ AG_GST_CHECK_CONFIGPROG(DVDNAV, dvdnav-config)
+ if test x"$HAVE_DVDNAV" = x"yes"; then
+ dnl check version
+ DVDNAV_VERSION=`dvdnav-config --version|head -n 1|sed 's/^.*) //'|sed 's/ (.*)//'`
+ DVDNAV_MAJOR=`echo $DVDNAV_VERSION | cut -d. -f1 | sed s/[a-zA-Z\-].*//g`
+ DVDNAV_MINOR=`echo $DVDNAV_VERSION | cut -d. -f2 | sed s/[a-zA-Z\-].*//g`
+ DVDNAV_MICRO=`echo $DVDNAV_VERSION | cut -d. -f3 | sed s/[a-zA-Z\-].*//g`
+ if [[ "$DVDNAV_MAJOR" -eq "0" ]] && \
+ [[ "$DVDNAV_MINOR" -lt "1" ]]; then
+ AC_MSG_WARN([libdvdnav >= 0.1.7 is required, you have $DVDNAV_VERSION])
+ HAVE_DVDNAV="no"
+ elif [[ "$DVDNAV_MAJOR" -eq "0" ]] && \
+ [[ "$DVDNAV_MINOR" -eq "1" ]] && \
+ [[ "$DVDNAV_MICRO" -lt "7" ]]; then
+ AC_MSG_WARN([libdvdnav >= 0.1.7 is required, you have $DVDNAV_VERSION])
+ HAVE_DVDNAV="no"
+ fi
+ fi
+ dnl now check for dvdread/nav_print.h - see #133002
+ AC_CHECK_HEADER(dvdread/nav_print.h, , [
+ AC_MSG_WARN([header dvdread/nav_print.h from dvdread missing])
+ HAVE_DVDNAV="no"
+ ])
+ AS_SCRUB_INCLUDE(DVDNAV_CFLAGS)
+])
+
dnl *** METADATA ***
translit(dnm, m, l) AM_CONDITIONAL(USE_METADATA, true)
AG_GST_CHECK_FEATURE(METADATA, [METADATA muxer and demuxer], metadata, [
@@ -1225,6 +1257,7 @@ ext/musicbrainz/Makefile
ext/mythtv/Makefile
ext/neon/Makefile
ext/ofa/Makefile
+ext/resindvd/Makefile
ext/sdl/Makefile
ext/sndfile/Makefile
ext/soundtouch/Makefile
diff --git a/ext/Makefile.am b/ext/Makefile.am
index 8ed3191b..f1279177 100644
--- a/ext/Makefile.am
+++ b/ext/Makefile.am
@@ -76,6 +76,12 @@ else
DTS_DIR=
endif
+if USE_DVDNAV
+ DVDNAV_DIR = resindvd
+else
+ DVDNAV_DIR =
+endif
+
if USE_FAAC
FAAC_DIR=faac
else
diff --git a/ext/resindvd/Makefile.am b/ext/resindvd/Makefile.am
new file mode 100644
index 00000000..9f4d3c8f
--- /dev/null
+++ b/ext/resindvd/Makefile.am
@@ -0,0 +1,30 @@
+# plugindir is set in configure
+
+plugin_LTLIBRARIES = libresindvd.la
+
+libresindvd_la_SOURCES = \
+ plugin.c \
+ resindvdbin.c \
+ rsnaudiomunge.c \
+ rsnbasesrc.c \
+ rsnpushsrc.c \
+ rsnstreamselector.c \
+ resindvdsrc.c \
+ gstmpegdesc.c \
+ gstmpegdemux.c \
+ gstpesfilter.c
+
+libresindvd_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(DVDNAV_CFLAGS)
+libresindvd_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(DVDNAV_LIBS)
+libresindvd_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+noinst_HEADERS = resindvdbin.h \
+ rsnaudiomunge.h \
+ rsnbasesrc.h \
+ rsnpushsrc.h \
+ rsnstreamselector.h \
+ resindvdsrc.h \
+ gstmpegdefs.h \
+ gstmpegdesc.h \
+ gstmpegdemux.h \
+ gstpesfilter.h
diff --git a/ext/resindvd/gstmpegdefs.h b/ext/resindvd/gstmpegdefs.h
new file mode 100644
index 00000000..33a8249c
--- /dev/null
+++ b/ext/resindvd/gstmpegdefs.h
@@ -0,0 +1,175 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is Fluendo MPEG Demuxer plugin.
+ *
+ * The Initial Developer of the Original Code is Fluendo, S.L.
+ * Portions created by Fluendo, S.L. are Copyright (C) 2005
+ * Fluendo, S.L. All Rights Reserved.
+ *
+ * Contributor(s): Wim Taymans <wim@fluendo.com>
+ * Jan Schmidt <thaytan@noraisin.net>
+ */
+
+#ifndef __GST_MPEG_DEFS_H__
+#define __GST_MPEG_DEFS_H__
+
+/*
+ * 1011 1100 program_stream_map
+ * 1011 1101 private_stream_1
+ * 1011 1110 padding_stream
+ * 1011 1111 private_stream_2
+ * 110x xxxx ISO/IEC 13818-3 or ISO/IEC 11172-3 audio stream number x xxxx
+ * 1110 xxxx ITU-T Rec. H.262 | ISO/IEC 13818-2 or ISO/IEC 11172-2 video stream number xxxx
+ * 1111 0000 ECM_stream
+ * 1111 0001 EMM_stream
+ * 1111 0010 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A or ISO/IEC 13818-6_DSMCC_stream
+ * 1111 0011 ISO/IEC_13522_stream
+ * 1111 0100 ITU-T Rec. H.222.1 type A
+ * 1111 0101 ITU-T Rec. H.222.1 type B
+ * 1111 0110 ITU-T Rec. H.222.1 type C
+ * 1111 0111 ITU-T Rec. H.222.1 type D
+ * 1111 1000 ITU-T Rec. H.222.1 type E
+ * 1111 1001 ancillary_stream
+ * 1111 1010 E 1111 1110 reserved data stream
+ * 1111 1111 program_stream_directory
+ */
+
+#define ID_PS_END_CODE 0x000001B9
+#define ID_PS_PACK_START_CODE 0x000001BA
+#define ID_PS_SYSTEM_HEADER_START_CODE 0x000001BB
+#define ID_PS_PROGRAM_STREAM_MAP 0x000001BC
+#define ID_PRIVATE_STREAM_1 0x000001BD
+#define ID_PADDING_STREAM 0x000001BE
+#define ID_PRIVATE_STREAM_2 0x000001BF
+#define ID_ISO_IEC_MPEG12_AUDIO_STREAM_0 0x000001C0
+#define ID_ISO_IEC_MPEG12_AUDIO_STREAM_32 0x000001DF
+#define ID_ISO_IEC_MPEG12_VIDEO_STREAM_0 0x000001E0
+#define ID_ISO_IEC_MPEG12_VIDEO_STREAM_16 0x000001EF
+#define ID_ECM_STREAM 0x000001F0
+#define ID_EMM_STREAM 0x000001F1
+#define ID_DSMCC_STREAM 0x000001F2
+#define ID_ISO_IEC_13522_STREAM 0x000001F3
+#define ID_ITU_TREC_H222_TYPE_A_STREAM 0x000001F4
+#define ID_ITU_TREC_H222_TYPE_B_STREAM 0x000001F5
+#define ID_ITU_TREC_H222_TYPE_C_STREAM 0x000001F6
+#define ID_ITU_TREC_H222_TYPE_D_STREAM 0x000001F7
+#define ID_ITU_TREC_H222_TYPE_E_STREAM 0x000001F8
+#define ID_ANCILLARY_STREAM 0x000001F9
+#define ID_RESERVED_STREAM_1 0x000001FA
+#define ID_RESERVED_STREAM_2 0x000001FB
+#define ID_EXTENDED_METADATA 0x000001FC
+#define ID_EXTENDED_STREAM_ID 0x000001FD
+#define ID_RESERVED_STREAM_3 0x000001FE
+#define ID_PROGRAM_STREAM_DIRECTORY 0x000001FF
+
+#define PACKET_VIDEO_START_CODE 0x000001E0
+#define PACKET_AUDIO_START_CODE 0x000001C0
+#define PICTURE_START_CODE 0x00000100
+#define USER_DATA_START_CODE 0x000001B2
+#define SEQUENCE_HEADER_CODE 0x000001B3
+#define SEQUENCE_ERROR_CODE 0x000001B4
+#define EXTENSION_START_CODE 0x000001B5
+#define SEQUENCE_END_CODE 0x000001B7
+#define GROUP_START_CODE 0x000001B8
+
+#define AC3_SYNC_WORD 0x0b770000
+
+#define MPEG_TS_SYNC_BYTE 0x00000047
+
+#define PID_PROGRAM_ASSOCIATION_TABLE 0x0000
+#define PID_CONDITIONAL_ACCESS_TABLE 0x0001
+#define PID_RESERVED_FIRST 0x0002
+#define PID_RESERVED_LAST 0x0010
+#define PID_NULL_PACKET 0x1FFF
+
+#define PID_TYPE_UNKNOWN 0
+#define PID_TYPE_RESERVED 1
+#define PID_TYPE_PROGRAM_ASSOCIATION 2
+#define PID_TYPE_CONDITIONAL_ACCESS 3
+#define PID_TYPE_PROGRAM_MAP 4
+#define PID_TYPE_ELEMENTARY 5
+#define PID_TYPE_NULL_PACKET 6
+#define PID_TYPE_PRIVATE_SECTION 7
+
+/* Stream type assignments
+ *
+ * 0x00 ITU-T | ISO/IEC Reserved
+ * 0x01 ISO/IEC 11172 Video
+ * 0x02 ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or
+ * ISO/IEC 11172-2 constrained parameter video
+ * stream
+ * 0x03 ISO/IEC 11172 Audio
+ * 0x04 ISO/IEC 13818-3 Audio
+ * 0x05 ITU-T Rec. H.222.0 | ISO/IEC 13818-1
+ * private_sections
+ * 0x06 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES
+ * packets containing private data
+ * 0x07 ISO/IEC 13522 MHEG
+ * 0x08 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A
+ * DSM CC
+ * 0x09 ITU-T Rec. H.222.1
+ * 0x0A ISO/IEC 13818-6 type A
+ * 0x0B ISO/IEC 13818-6 type B
+ * 0x0C ISO/IEC 13818-6 type C
+ * 0x0D ISO/IEC 13818-6 type D
+ * 0x0E ISO/IEC 13818-1 auxiliary
+ * 0x0F-0x7F ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved
+ * 0x80-0xFF User Private
+ */
+#define ST_RESERVED 0x00
+#define ST_VIDEO_MPEG1 0x01
+#define ST_VIDEO_MPEG2 0x02
+#define ST_AUDIO_MPEG1 0x03
+#define ST_AUDIO_MPEG2 0x04
+#define ST_PRIVATE_SECTIONS 0x05
+#define ST_PRIVATE_DATA 0x06
+#define ST_MHEG 0x07
+#define ST_DSMCC 0x08
+#define ST_H222_1 0x09
+
+/* later extensions */
+#define ST_AUDIO_AAC 0x0f
+#define ST_VIDEO_MPEG4 0x10
+#define ST_VIDEO_H264 0x1b
+
+/* Un-official Dirac extension */
+#define ST_VIDEO_DIRAC 0xd1
+
+/* private stream types */
+#define ST_PS_AUDIO_AC3 0x81
+#define ST_PS_AUDIO_DTS 0x8a
+#define ST_PS_AUDIO_LPCM 0x8b
+#define ST_PS_DVD_SUBPICTURE 0xff
+
+/* Un-official time-code stream */
+#define ST_PS_TIMECODE 0xd2
+
+/* Internal stream types >= 0x100 */
+#define ST_GST_AUDIO_RAWA52 0x181
+ /* Used when we don't yet know which stream type it will be in a PS stream */
+#define ST_GST_VIDEO_MPEG1_OR_2 0x102
+
+#define CLOCK_BASE 9LL
+#define CLOCK_FREQ (CLOCK_BASE * 10000)
+
+#define MPEGTIME_TO_GSTTIME(time) (gst_util_uint64_scale ((time), \
+ GST_MSECOND/10, CLOCK_BASE))
+#define GSTTIME_TO_MPEGTIME(time) (gst_util_uint64_scale ((time), \
+ CLOCK_BASE, GST_MSECOND/10))
+
+#define MPEG_MUX_RATE_MULT 50
+
+/* some extra GstFlowReturn values used internally */
+#define GST_FLOW_NEED_MORE_DATA -100
+#define GST_FLOW_LOST_SYNC -101
+
+#endif /* __GST_MPEG_DEFS_H__ */
diff --git a/ext/resindvd/gstmpegdemux.c b/ext/resindvd/gstmpegdemux.c
new file mode 100644
index 00000000..5e1da719
--- /dev/null
+++ b/ext/resindvd/gstmpegdemux.c
@@ -0,0 +1,1943 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is Fluendo MPEG Demuxer plugin.
+ *
+ * The Initial Developer of the Original Code is Fluendo, S.L.
+ * Portions created by Fluendo, S.L. are Copyright (C) 2005
+ * Fluendo, S.L. All Rights Reserved.
+ *
+ * Contributor(s): Wim Taymans <wim@fluendo.com>
+ * Jan Schmidt <thaytan@noraisin.net>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "gstmpegdefs.h"
+#include "gstmpegdemux.h"
+
+#define MAX_DVD_AUDIO_STREAMS 8
+#define MAX_DVD_SUBPICTURE_STREAMS 32
+
+#define SEGMENT_THRESHOLD (GST_SECOND/2)
+
+/* The SCR_MUNGE value is used to offset the scr_adjust value, to avoid
+ * ever generating a negative timestamp */
+#define SCR_MUNGE (2 * GST_SECOND)
+
+/* We clamp scr delta with 0 so negative bytes won't be possible */
+#define GSTTIME_TO_BYTES(time) \
+ ((time != -1) ? gst_util_uint64_scale (MAX(0,(gint64) (GSTTIME_TO_MPEGTIME(time) - demux->first_scr)), demux->scr_rate_n, demux->scr_rate_d) : -1)
+#define BYTES_TO_GSTTIME(bytes) ((bytes != -1) ? MPEGTIME_TO_GSTTIME(gst_util_uint64_scale (bytes, demux->scr_rate_d, demux->scr_rate_n)) : -1)
+
+#define ADAPTER_OFFSET_FLUSH(_bytes_) demux->adapter_offset += (_bytes_)
+
+GST_DEBUG_CATEGORY_STATIC (gstflupsdemux_debug);
+#define GST_CAT_DEFAULT (gstflupsdemux_debug)
+
+GST_DEBUG_CATEGORY_EXTERN (gstflupesfilter_debug);
+
+/* elementfactory information */
+static GstElementDetails flups_demux_details = {
+ "MPEG Program Demuxer",
+ "Codec/Demuxer",
+ "Demultiplexes MPEG Program Streams",
+ "Wim Taymans <wim@fluendo.com>"
+};
+
+/* MPEG2Demux signals and args */
+enum
+{
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+enum
+{
+ ARG_0,
+ /* FILL ME */
+};
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-resin-dvd")
+ );
+
+static GstStaticPadTemplate video_template =
+ GST_STATIC_PAD_TEMPLATE ("video_%02x",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS ("video/mpeg, "
+ "mpegversion = (int) { 1, 2, 4 }, " "systemstream = (boolean) FALSE;"
+ "video/x-h264")
+ );
+
+static GstStaticPadTemplate audio_template =
+ GST_STATIC_PAD_TEMPLATE ("audio_%02x",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS ("audio/mpeg, "
+ "mpegversion = (int) 1;"
+ "audio/x-private1-lpcm; "
+ "audio/x-private1-ac3;" "audio/x-private1-dts;" "audio/ac3")
+ );
+
+static GstStaticPadTemplate subpicture_template =
+GST_STATIC_PAD_TEMPLATE ("subpicture_%02x",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS ("video/x-dvd-subpicture")
+ );
+
+static GstStaticPadTemplate private_template =
+GST_STATIC_PAD_TEMPLATE ("private_%d",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+static void gst_flups_demux_base_init (GstFluPSDemuxClass * klass);
+static void gst_flups_demux_class_init (GstFluPSDemuxClass * klass);
+static void gst_flups_demux_init (GstFluPSDemux * demux);
+static void gst_flups_demux_finalize (GstFluPSDemux * demux);
+static void gst_flups_demux_reset (GstFluPSDemux * demux);
+
+static gboolean gst_flups_demux_sink_event (GstPad * pad, GstEvent * event);
+static gboolean gst_flups_demux_src_event (GstPad * pad, GstEvent * event);
+static gboolean gst_flups_demux_src_query (GstPad * pad, GstQuery * query);
+static GstFlowReturn gst_flups_demux_chain (GstPad * pad, GstBuffer * buffer);
+
+static GstStateChangeReturn gst_flups_demux_change_state (GstElement * element,
+ GstStateChange transition);
+
+static GstElementClass *parent_class = NULL;
+
+/*static guint gst_flups_demux_signals[LAST_SIGNAL] = { 0 };*/
+
+GType
+gst_flups_demux_get_type (void)
+{
+ static GType flups_demux_type = 0;
+
+ if (!flups_demux_type) {
+ static const GTypeInfo flups_demux_info = {
+ sizeof (GstFluPSDemuxClass),
+ (GBaseInitFunc) gst_flups_demux_base_init,
+ NULL,
+ (GClassInitFunc) gst_flups_demux_class_init,
+ NULL,
+ NULL,
+ sizeof (GstFluPSDemux),
+ 0,
+ (GInstanceInitFunc) gst_flups_demux_init,
+ };
+
+ flups_demux_type =
+ g_type_register_static (GST_TYPE_ELEMENT, "RsnDVDDemux",
+ &flups_demux_info, 0);
+
+ GST_DEBUG_CATEGORY_INIT (gstflupsdemux_debug, "rsndvddemux", 0,
+ "MPEG program stream demultiplexer element");
+ }
+
+ return flups_demux_type;
+}
+
+static void
+gst_flups_demux_base_init (GstFluPSDemuxClass * klass)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+ klass->sink_template = gst_static_pad_template_get (&sink_template);
+ klass->video_template = gst_static_pad_template_get (&video_template);
+ klass->audio_template = gst_static_pad_template_get (&audio_template);
+ klass->subpicture_template =
+ gst_static_pad_template_get (&subpicture_template);
+ klass->private_template = gst_static_pad_template_get (&private_template);
+
+ gst_element_class_add_pad_template (element_class, klass->video_template);
+ gst_element_class_add_pad_template (element_class, klass->audio_template);
+ gst_element_class_add_pad_template (element_class, klass->private_template);
+ gst_element_class_add_pad_template (element_class,
+ klass->subpicture_template);
+ gst_element_class_add_pad_template (element_class, klass->sink_template);
+
+ gst_element_class_set_details (element_class, &flups_demux_details);
+}
+
+static void
+gst_flups_demux_class_init (GstFluPSDemuxClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ gobject_class->finalize = (GObjectFinalizeFunc) gst_flups_demux_finalize;
+
+ gstelement_class->change_state = gst_flups_demux_change_state;
+}
+
+static void
+gst_flups_demux_init (GstFluPSDemux * demux)
+{
+ GstFluPSDemuxClass *klass = GST_FLUPS_DEMUX_GET_CLASS (demux);
+
+ demux->sinkpad = gst_pad_new_from_template (klass->sink_template, "sink");
+ gst_pad_set_event_function (demux->sinkpad, gst_flups_demux_sink_event);
+ gst_pad_set_chain_function (demux->sinkpad, gst_flups_demux_chain);
+ gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
+
+ demux->streams =
+ g_malloc0 (sizeof (GstFluPSStream *) * (GST_FLUPS_DEMUX_MAX_STREAMS));
+
+ demux->scr_adjust = GSTTIME_TO_MPEGTIME (SCR_MUNGE);
+}
+
+static void
+gst_flups_demux_finalize (GstFluPSDemux * demux)
+{
+ gst_flups_demux_reset (demux);
+ g_free (demux->streams);
+
+ G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT (demux));
+}
+
+static void
+gst_flups_demux_reset (GstFluPSDemux * demux)
+{
+ /* Clean up the streams and pads we allocated */
+ gint i;
+ GstEvent **p_ev;
+
+ for (i = 0; i < GST_FLUPS_DEMUX_MAX_STREAMS; i++) {
+ GstFluPSStream *stream = demux->streams[i];
+
+ if (stream != NULL) {
+ if (stream->pad)
+ gst_element_remove_pad (GST_ELEMENT_CAST (demux), stream->pad);
+
+ g_free (stream);
+ demux->streams[i] = NULL;
+ }
+ }
+
+ p_ev = &demux->lang_codes;
+ gst_event_replace (p_ev, NULL);
+
+ demux->scr_adjust = GSTTIME_TO_MPEGTIME (SCR_MUNGE);
+}
+
+static GstFluPSStream *
+gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
+{
+ GstFluPSStream *stream;
+ GstPadTemplate *template;
+ gchar *name;
+ GstFluPSDemuxClass *klass = GST_FLUPS_DEMUX_GET_CLASS (demux);
+ GstCaps *caps;
+
+ name = NULL;
+ template = NULL;
+ caps = NULL;
+
+ GST_DEBUG_OBJECT (demux, "create stream id 0x%02x, type 0x%02x", id,
+ stream_type);
+
+ switch (stream_type) {
+ case ST_VIDEO_MPEG1:
+ case ST_VIDEO_MPEG2:
+ case ST_VIDEO_MPEG4:
+ case ST_GST_VIDEO_MPEG1_OR_2:
+ {
+ gint mpeg_version = 1;
+ if (stream_type == ST_VIDEO_MPEG2 ||
+ (stream_type == ST_GST_VIDEO_MPEG1_OR_2 && demux->is_mpeg2_pack)) {
+ mpeg_version = 2;
+ }
+ if (stream_type == ST_VIDEO_MPEG4) {
+ mpeg_version = 4;
+ }
+
+ template = klass->video_template;
+ name = g_strdup_printf ("video_%02x", id);
+ caps = gst_caps_new_simple ("video/mpeg",
+ "mpegversion", G_TYPE_INT, mpeg_version,
+ "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
+ break;
+ }
+ case ST_AUDIO_MPEG1:
+ case ST_AUDIO_MPEG2:
+ template = klass->audio_template;
+ name = g_strdup_printf ("audio_%02x", id);
+ caps = gst_caps_new_simple ("audio/mpeg",
+ "mpegversion", G_TYPE_INT, 1, NULL);
+ break;
+ case ST_PRIVATE_SECTIONS:
+ case ST_PRIVATE_DATA:
+ case ST_MHEG:
+ case ST_DSMCC:
+ case ST_AUDIO_AAC:
+ break;
+ case ST_VIDEO_H264:
+ template = klass->video_template;
+ name = g_strdup_printf ("video_%02x", id);
+ caps = gst_caps_new_simple ("video/x-h264", NULL);
+ break;
+ case ST_PS_AUDIO_AC3:
+ template = klass->audio_template;
+ name = g_strdup_printf ("audio_%02x", id);
+ caps = gst_caps_new_simple ("audio/x-private1-ac3", NULL);
+ break;
+ case ST_PS_AUDIO_DTS:
+ template = klass->audio_template;
+ name = g_strdup_printf ("audio_%02x", id);
+ caps = gst_caps_new_simple ("audio/x-private1-dts", NULL);
+ break;
+ case ST_PS_AUDIO_LPCM:
+ template = klass->audio_template;
+ name = g_strdup_printf ("audio_%02x", id);
+ caps = gst_caps_new_simple ("audio/x-private1-lpcm", NULL);
+ break;
+ case ST_PS_DVD_SUBPICTURE:
+ template = klass->subpicture_template;
+ name = g_strdup_printf ("subpicture_%02x", id);
+ caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL);
+ break;
+ case ST_GST_AUDIO_RAWA52:
+ template = klass->audio_template;
+ name = g_strdup_printf ("audio_%02x", id);
+ caps = gst_caps_new_simple ("audio/ac3", NULL);
+ break;
+ default:
+ break;
+ }
+
+ if (name == NULL || template == NULL || caps == NULL)
+ return NULL;
+
+ stream = g_new0 (GstFluPSStream, 1);
+ stream->id = id;
+ stream->discont = TRUE;
+ stream->notlinked = FALSE;
+ stream->type = stream_type;
+ stream->pad = gst_pad_new_from_template (template, name);
+ gst_pad_set_event_function (stream->pad, gst_flups_demux_src_event);
+ gst_pad_set_query_function (stream->pad, gst_flups_demux_src_query);
+ gst_pad_use_fixed_caps (stream->pad);
+ gst_pad_set_caps (stream->pad, caps);
+ gst_caps_unref (caps);
+ GST_DEBUG_OBJECT (demux, "create pad %s, caps %" GST_PTR_FORMAT, name, caps);
+ g_free (name);
+
+
+ return stream;
+}
+
+static GstFluPSStream *
+gst_flups_demux_get_stream (GstFluPSDemux * demux, gint id, gint type)
+{
+ GstFluPSStream *stream = demux->streams[id];
+
+ if (stream == NULL) {
+ if (!(stream = gst_flups_demux_create_stream (demux, id, type)))
+ goto unknown_stream;
+
+ GST_DEBUG_OBJECT (demux, "adding pad for stream id 0x%02x type 0x%02x", id,
+ type);
+
+ gst_pad_set_active (stream->pad, TRUE);
+ gst_element_add_pad (GST_ELEMENT (demux), stream->pad);
+
+ demux->streams[id] = stream;
+ }
+ return stream;
+
+ /* ERROR */
+unknown_stream:
+ {
+ GST_DEBUG_OBJECT (demux, "unknown stream id 0x%02x type 0x%02x", id, type);
+ return NULL;
+ }
+}
+
+static GstFlowReturn
+gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream,
+ GstBuffer * buf)
+{
+ GstFlowReturn result;
+ guint64 timestamp;
+ guint size;
+
+ if (stream == NULL)
+ goto no_stream;
+
+ /* timestamps */
+ if (demux->next_pts != G_MAXUINT64)
+ timestamp = MPEGTIME_TO_GSTTIME (demux->next_pts);
+ else
+ timestamp = GST_CLOCK_TIME_NONE;
+
+ if (demux->current_scr != G_MAXUINT64) {
+ GstClockTime cur_scr_time = MPEGTIME_TO_GSTTIME (demux->current_scr);
+
+ if (stream->last_ts == GST_CLOCK_TIME_NONE ||
+ stream->last_ts < cur_scr_time) {
+#if 0
+ g_print ("last_ts update on pad %s to time %" GST_TIME_FORMAT "\n",
+ GST_PAD_NAME (stream->pad), GST_TIME_ARGS (cur_scr_time));
+#endif
+ stream->last_ts = cur_scr_time;
+ }
+ }
+
+ /* OK, sent new segment now prepare the buffer for sending */
+ /* caps */
+ gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad));
+ GST_BUFFER_TIMESTAMP (buf) = timestamp;
+
+ /* Set the buffer discont flag, and clear discont state on the stream */
+ if (stream->discont) {
+ GST_DEBUG_OBJECT (demux, "discont buffer to pad %" GST_PTR_FORMAT
+ " with TS %" GST_TIME_FORMAT "\n", stream->pad,
+ GST_TIME_ARGS (timestamp));
+
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
+ stream->discont = FALSE;
+ }
+ size = GST_BUFFER_SIZE (buf);
+
+ demux->next_pts = G_MAXUINT64;
+ demux->next_dts = G_MAXUINT64;
+
+ result = gst_pad_push (stream->pad, buf);
+ GST_DEBUG_OBJECT (demux, "pushed stream id 0x%02x type 0x%02x, time: %"
+ GST_TIME_FORMAT ", size %d. result: %s",
+ stream->id, stream->type, GST_TIME_ARGS (timestamp),
+ size, gst_flow_get_name (result));
+
+ return result;
+
+ /* ERROR */
+no_stream:
+ {
+ GST_DEBUG_OBJECT (demux, "no stream given");
+ gst_buffer_unref (buf);
+ return GST_FLOW_OK;
+ }
+}
+
+static void
+gst_flups_demux_mark_discont (GstFluPSDemux * demux)
+{
+ gint id;
+
+ /* mark discont on all streams */
+ for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
+ GstFluPSStream *stream = demux->streams[id];
+
+ if (stream) {
+ stream->discont = TRUE;
+ GST_DEBUG_OBJECT (demux, "marked stream as discont %d", stream->discont);
+ }
+ }
+}
+
+static void
+gst_flups_demux_clear_times (GstFluPSDemux * demux)
+{
+ gint id;
+
+ /* Clear the last ts for all streams */
+ for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
+ GstFluPSStream *stream = demux->streams[id];
+
+ if (stream) {
+ stream->last_ts = GST_CLOCK_TIME_NONE;
+ }
+ }
+}
+
+static void
+gst_flups_demux_send_segment_updates (GstFluPSDemux * demux,
+ GstClockTime new_time)
+{
+ /* Advance all lagging streams by sending a segment update */
+ gint id;
+ GstEvent *event = NULL;
+
+ if (new_time > demux->src_segment.stop)
+ return;
+
+ for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
+ GstFluPSStream *stream = demux->streams[id];
+
+ if (stream) {
+ if (stream->last_ts == GST_CLOCK_TIME_NONE ||
+ stream->last_ts < demux->src_segment.start)
+ stream->last_ts = demux->src_segment.start;
+ if (stream->last_ts + SEGMENT_THRESHOLD < new_time) {
+#if 0
+ g_print ("Segment update to pad %s time %" GST_TIME_FORMAT "\n",
+ GST_PAD_NAME (stream->pad), GST_TIME_ARGS (new_time));
+#endif
+ if (event == NULL) {
+ event = gst_event_new_new_segment_full (TRUE,
+ demux->src_segment.rate, demux->src_segment.applied_rate,
+ GST_FORMAT_TIME, new_time,
+ demux->src_segment.stop,
+ demux->src_segment.time + (new_time - demux->src_segment.start));
+ }
+ gst_event_ref (event);
+ gst_pad_push_event (stream->pad, event);
+ stream->last_ts = new_time;
+ }
+ }
+ }
+
+ if (event)
+ gst_event_unref (event);
+}
+
+static void
+gst_flups_demux_send_segment_close (GstFluPSDemux * demux)
+{
+ gint id;
+ GstEvent *event = NULL;
+
+ for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
+ GstFluPSStream *stream = demux->streams[id];
+
+ if (stream) {
+ if (stream->last_ts == GST_CLOCK_TIME_NONE ||
+ stream->last_ts < demux->src_segment.start)
+ stream->last_ts = demux->src_segment.start;
+
+#if 0
+ g_print ("Segment update to pad %s start %" GST_TIME_FORMAT
+ " stop %" GST_TIME_FORMAT "\n",
+ GST_PAD_NAME (stream->pad), GST_TIME_ARGS (stream->last_ts),
+ GST_TIME_ARGS (demux->src_segment.stop));
+#endif
+ event = gst_event_new_new_segment_full (TRUE,
+ demux->src_segment.rate, demux->src_segment.applied_rate,
+ GST_FORMAT_TIME, stream->last_ts,
+ demux->src_segment.stop,
+ demux->src_segment.time +
+ (stream->last_ts - demux->src_segment.start));
+ gst_pad_push_event (stream->pad, event);
+ }
+ }
+}
+
+static gboolean
+gst_flups_demux_send_event (GstFluPSDemux * demux, GstEvent * event)
+{
+ gint id;
+ gboolean ret = FALSE;
+
+ for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
+ GstFluPSStream *stream = demux->streams[id];
+
+ if (stream && !stream->notlinked) {
+ (void) gst_event_ref (event);
+
+ if (!gst_pad_push_event (stream->pad, event)) {
+ GST_DEBUG_OBJECT (stream, "event %s was not handled correctly by pad %"
+ GST_PTR_FORMAT, GST_EVENT_TYPE_NAME (event), stream->pad);
+ } else {
+ /* If at least one push returns TRUE, then we return TRUE. */
+ GST_DEBUG_OBJECT (stream, "event %s was handled correctly by pad %"
+ GST_PTR_FORMAT, GST_EVENT_TYPE_NAME (event), stream->pad);
+ ret = TRUE;
+ }
+ }
+ }
+
+ gst_event_unref (event);
+ return ret;
+}
+
+static gboolean
+gst_flups_demux_handle_dvd_event (GstFluPSDemux * demux, GstEvent * event)
+{
+ const GstStructure *structure = gst_event_get_structure (event);
+ const char *type = gst_structure_get_string (structure, "event");
+ gint i;
+ gchar cur_stream_name[32];
+ GstFluPSStream *temp;
+
+ if (strcmp (type, "dvd-lang-codes") == 0) {
+ GstEvent **p_ev;
+ /* Store the language codes event on the element, then iterate over the
+ * streams it specifies and retrieve them. The stream creation code then
+ * creates the pad appropriately and sends tag events as needed */
+ p_ev = &demux->lang_codes;
+ gst_event_replace (p_ev, event);
+
+ GST_ERROR_OBJECT (demux, "*********************\nLang codes event %s",
+ gst_structure_to_string (structure));
+
+ GST_DEBUG_OBJECT (demux, "Handling language codes event");
+
+ /* Create a video pad to ensure have it before emit no more pads */
+ temp = gst_flups_demux_get_stream (demux, 0xe0, ST_VIDEO_MPEG2);
+
+ /* Read out the languages for audio streams and request each one that
+ * is present */
+ for (i = 0; i < MAX_DVD_AUDIO_STREAMS; i++) {
+ gint stream_format;
+ gint stream_id;
+
+ g_snprintf (cur_stream_name, 32, "audio-%d-format", i);
+ if (!gst_structure_get_int (structure, cur_stream_name, &stream_format))
+ continue;
+
+ switch (stream_format) {
+ case 0x0:
+ /* AC3 */
+ stream_id = 0x80 + i;
+ temp = gst_flups_demux_get_stream (demux, stream_id, ST_PS_AUDIO_AC3);
+ break;
+ case 0x2:
+ case 0x3:
+ /* MPEG audio without and with extension stream are
+ * treated the same */
+ stream_id = 0xC0 + i;
+ temp = gst_flups_demux_get_stream (demux, stream_id, ST_AUDIO_MPEG1);
+ break;
+ case 0x4:
+ /* LPCM */
+ stream_id = 0xA0 + i;
+ temp =
+ gst_flups_demux_get_stream (demux, stream_id, ST_PS_AUDIO_LPCM);
+ break;
+ case 0x6:
+ /* DTS */
+ stream_id = 0x88 + i;
+ temp = gst_flups_demux_get_stream (demux, stream_id, ST_PS_AUDIO_DTS);
+ break;
+ case 0x7:
+ /* FIXME: What range is SDDS? */
+ break;
+ default:
+ GST_WARNING_OBJECT (demux,
+ "Unknown audio stream format in language code event: %d",
+ stream_format);
+ break;
+ }
+ }
+
+ /* And subtitle streams */
+ for (i = 0; i < MAX_DVD_SUBPICTURE_STREAMS; i++) {
+ gint stream_format;
+ g_snprintf (cur_stream_name, 32, "subpicture-%d-format", i);
+
+ if (!gst_structure_get_int (structure, cur_stream_name, &stream_format))
+ continue;
+
+ /* Retrieve the subpicture stream to force pad creation */
+ temp = gst_flups_demux_get_stream (demux, 0x20 + i, ST_PS_DVD_SUBPICTURE);
+ }
+
+ GST_DEBUG_OBJECT (demux, "Created all pads from Language Codes event, "
+ "signalling no-more-pads");
+
+ gst_element_no_more_pads (GST_ELEMENT (demux));
+ demux->need_no_more_pads = FALSE;
+ gst_event_unref (event);
+ } else {
+ gst_flups_demux_send_event (demux, event);
+ }
+ return TRUE;
+}
+
+static gboolean
+gst_flups_demux_sink_event (GstPad * pad, GstEvent * event)
+{
+ gboolean res = TRUE;
+ GstFluPSDemux *demux;
+
+ demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_FLUSH_START:
+ gst_flups_demux_send_event (demux, event);
+ break;
+ case GST_EVENT_FLUSH_STOP:
+ gst_flups_demux_send_event (demux, event);
+
+ gst_segment_init (&demux->sink_segment, GST_FORMAT_UNDEFINED);
+ gst_segment_init (&demux->src_segment, GST_FORMAT_TIME);
+ gst_adapter_clear (demux->adapter);
+ gst_adapter_clear (demux->rev_adapter);
+ demux->adapter_offset = G_MAXUINT64;
+ gst_pes_filter_drain (&demux->filter);
+ demux->current_scr = G_MAXUINT64;
+ demux->bytes_since_scr = 0;
+ demux->scr_adjust = GSTTIME_TO_MPEGTIME (SCR_MUNGE);
+ gst_flups_demux_clear_times (demux);
+ break;
+ case GST_EVENT_NEWSEGMENT:
+ {
+ gboolean update;
+ gdouble rate;
+ GstFormat format;
+ gint64 start, stop, time;
+ gint64 accum, dur;
+ gdouble arate;
+ GstClockTimeDiff adjust;
+
+ gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
+ &start, &stop, &time);
+
+ if (format != GST_FORMAT_TIME)
+ return FALSE;
+
+ dur = stop - start;
+
+ demux->first_scr = GSTTIME_TO_MPEGTIME (start);
+ demux->current_scr = demux->first_scr + demux->scr_adjust;
+ demux->base_time = time;
+ demux->bytes_since_scr = 0;
+
+ gst_segment_set_newsegment_full (&demux->sink_segment, update, rate,
+ arate, format, start, stop, time);
+
+ g_print ("demux: got segment update %d start %" G_GINT64_FORMAT
+ " stop %" G_GINT64_FORMAT " time %" G_GINT64_FORMAT "\n",
+ update, start, stop, time);
+
+ accum = demux->sink_segment.accum;
+ start = demux->sink_segment.start;
+ stop = demux->sink_segment.stop;
+
+ adjust = accum - start + SCR_MUNGE;
+ start = accum + SCR_MUNGE;
+
+ if (adjust >= 0)
+ demux->scr_adjust = GSTTIME_TO_MPEGTIME (adjust);
+ else
+ demux->scr_adjust = -GSTTIME_TO_MPEGTIME (-adjust);
+
+ if (stop != -1)
+ stop = start + dur;
+
+ GST_INFO_OBJECT (demux, "sending new segment: rate %g "
+ "format %d, start: %" G_GINT64_FORMAT ", stop: %" G_GINT64_FORMAT
+ ", time: %" G_GINT64_FORMAT, rate, format, start, stop, time);
+
+ g_print ("sending new segment: update %d rate %g format %d, start: %"
+ G_GINT64_FORMAT ", stop: %" G_GINT64_FORMAT ", time: %"
+ G_GINT64_FORMAT " scr_adjust: %" G_GINT64_FORMAT
+ "(%" GST_TIME_FORMAT ")\n",
+ update, rate, format, start, stop, time, demux->scr_adjust,
+ GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (demux->scr_adjust)));
+
+ gst_segment_set_newsegment_full (&demux->src_segment, update,
+ rate, arate, format, start, stop, time);
+
+ gst_event_unref (event);
+ if (update) {
+ /* Segment closing, send it as per-pad updates to manage the accum
+ * properly */
+ gst_flups_demux_send_segment_close (demux);
+ } else {
+ event = gst_event_new_new_segment_full (update,
+ rate, arate, GST_FORMAT_TIME, start, stop, time);
+ gst_flups_demux_send_event (demux, event);
+ }
+
+ break;
+ }
+ case GST_EVENT_EOS:
+ GST_INFO_OBJECT (demux, "Received EOS");
+ if (!gst_flups_demux_send_event (demux, event)) {
+ GST_WARNING_OBJECT (demux, "failed pushing EOS on streams");
+ GST_ELEMENT_ERROR (demux, STREAM, FAILED,
+ ("Internal data stream error."), ("Can't push EOS downstream"));
+ }
+ break;
+ case GST_EVENT_CUSTOM_DOWNSTREAM:
+ case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:
+ {
+ const GstStructure *structure = gst_event_get_structure (event);
+
+ if (structure != NULL
+ && gst_structure_has_name (structure, "application/x-gst-dvd")) {
+ res = gst_flups_demux_handle_dvd_event (demux, event);
+ } else {
+ gst_flups_demux_send_event (demux, event);
+ }
+ break;
+ }
+ default:
+ gst_flups_demux_send_event (demux, event);
+ break;
+ }
+
+ gst_object_unref (demux);
+
+ return res;
+}
+
+static gboolean
+gst_flups_demux_src_event (GstPad * pad, GstEvent * event)
+{
+ gboolean res = FALSE;
+ GstFluPSDemux *demux;
+
+ demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ {
+ gdouble rate;
+ GstFormat format;
+ GstSeekFlags flags;
+ GstSeekType start_type, stop_type;
+ gint64 start, stop;
+ gint64 bstart, bstop;
+ GstEvent *bevent;
+
+ gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
+ &stop_type, &stop);
+
+ GST_DEBUG_OBJECT (demux, "seek event, rate: %f start: %" GST_TIME_FORMAT
+ " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start),
+ GST_TIME_ARGS (stop));
+
+ if (format == GST_FORMAT_BYTES) {
+ GST_DEBUG_OBJECT (demux, "seek not supported on format %d", format);
+ goto not_supported;
+ }
+
+ GST_DEBUG_OBJECT (demux, "seek - trying directly upstream first");
+
+ /* first try original format seek */
+ (void) gst_event_ref (event);
+ if ((res = gst_pad_push_event (demux->sinkpad, event)))
+ goto done;
+
+ if (format != GST_FORMAT_TIME) {
+ /* From here down, we only support time based seeks */
+ GST_DEBUG_OBJECT (demux, "seek not supported on format %d", format);
+ goto not_supported;
+ }
+
+ /* We need to convert to byte based seek and we need a scr_rate for that. */
+ if (demux->scr_rate_n == G_MAXUINT64 || demux->scr_rate_d == G_MAXUINT64) {
+ GST_DEBUG_OBJECT (demux, "seek not possible, no scr_rate");
+ goto not_supported;
+ }
+
+ GST_DEBUG_OBJECT (demux, "try with scr_rate interpolation");
+
+ bstart = GSTTIME_TO_BYTES (start);
+ bstop = GSTTIME_TO_BYTES (stop);
+
+ GST_DEBUG_OBJECT (demux, "in bytes bstart %" G_GINT64_FORMAT " bstop %"
+ G_GINT64_FORMAT, bstart, bstop);
+ bevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, start_type,
+ bstart, stop_type, bstop);
+
+ res = gst_pad_push_event (demux->sinkpad, bevent);
+
+ done:
+ gst_event_unref (event);
+ break;
+ }
+ default:
+ res = gst_pad_push_event (demux->sinkpad, event);
+ break;
+ }
+
+ gst_object_unref (demux);
+
+ return res;
+
+not_supported:
+ {
+ gst_object_unref (demux);
+ gst_event_unref (event);
+
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_flups_demux_src_query (GstPad * pad, GstQuery * query)
+{
+ gboolean res = FALSE;
+ GstFluPSDemux *demux;
+
+ demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad));
+
+ GST_LOG_OBJECT (demux, "Have query of type %d on pad %" GST_PTR_FORMAT,
+ GST_QUERY_TYPE (query), pad);
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_POSITION:
+ {
+ GstPad *peer;
+ GstFormat format;
+ gint64 position;
+
+ gst_query_parse_position (query, &format, NULL);
+
+ if ((peer = gst_pad_get_peer (demux->sinkpad)) != NULL) {
+ res = gst_pad_query (peer, query);
+ gst_object_unref (peer);
+ if (res)
+ break;
+ }
+
+ if (format != GST_FORMAT_TIME) {
+ GST_DEBUG_OBJECT (demux, "position not supported for format %d",
+ format);
+ goto not_supported;
+ }
+
+ position = demux->base_time;
+ if (demux->current_scr != G_MAXUINT64 && demux->first_scr != G_MAXUINT64) {
+ position +=
+ MPEGTIME_TO_GSTTIME (demux->current_scr - demux->scr_adjust -
+ demux->first_scr);
+ }
+
+ GST_LOG_OBJECT (demux, "Position at GStreamer Time:%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (position));
+
+ gst_query_set_position (query, format, position);
+ res = TRUE;
+ break;
+ }
+ case GST_QUERY_DURATION:
+ {
+ GstFormat format;
+ gint64 duration;
+ GstPad *peer;
+
+ gst_query_parse_duration (query, &format, NULL);
+
+ if ((peer = gst_pad_get_peer (demux->sinkpad)) == NULL) {
+ GST_DEBUG_OBJECT (demux, "duration not possible, no peer");
+ goto not_supported;
+ }
+
+ /* For any format other than bytes, see if upstream knows first */
+ if (format == GST_FORMAT_BYTES) {
+ GST_DEBUG_OBJECT (demux, "duration not supported for format %d",
+ format);
+ gst_object_unref (peer);
+ goto not_supported;
+ }
+
+ if (gst_pad_query (peer, query)) {
+ gst_object_unref (peer);
+ res = TRUE;
+ break;
+ }
+
+ /* Upstream didn't know, so we can only answer TIME queries from
+ * here on */
+ if (format != GST_FORMAT_TIME) {
+ GST_DEBUG_OBJECT (demux, "duration not supported for format %d",
+ format);
+ gst_object_unref (peer);
+ goto not_supported;
+ }
+
+ if (demux->mux_rate == -1) {
+ GST_DEBUG_OBJECT (demux, "duration not possible, no mux_rate");
+ gst_object_unref (peer);
+ goto not_supported;
+ }
+
+ gst_query_set_duration (query, GST_FORMAT_BYTES, -1);
+
+ if (!gst_pad_query (peer, query)) {
+ GST_LOG_OBJECT (demux, "query on peer pad failed");
+ gst_object_unref (peer);
+ goto not_supported;
+ }
+ gst_object_unref (peer);
+
+ gst_query_parse_duration (query, &format, &duration);
+
+ duration = BYTES_TO_GSTTIME (duration);
+
+ gst_query_set_duration (query, GST_FORMAT_TIME, duration);
+ res = TRUE;
+ break;
+ }
+ default:
+ res = gst_pad_query_default (pad, query);
+ break;
+ }
+
+ gst_object_unref (demux);
+
+ return res;
+
+not_supported:
+ {
+ gst_object_unref (demux);
+
+ return FALSE;
+ }
+}
+
+static void
+gst_flups_demux_reset_psm (GstFluPSDemux * demux)
+{
+ gint i;
+
+#define FILL_TYPE(start, stop, type) \
+ for (i=start; i <= stop; i++) \
+ demux->psm[i] = type;
+
+ FILL_TYPE (0x00, 0x1f, -1);
+ FILL_TYPE (0x20, 0x3f, ST_PS_DVD_SUBPICTURE);
+ FILL_TYPE (0x40, 0x7f, -1);
+ FILL_TYPE (0x80, 0x87, ST_PS_AUDIO_AC3);
+ FILL_TYPE (0x88, 0x9f, ST_PS_AUDIO_DTS);
+ FILL_TYPE (0xa0, 0xbf, ST_PS_AUDIO_LPCM);
+ FILL_TYPE (0xbd, 0xbd, -1);
+ FILL_TYPE (0xc0, 0xdf, ST_AUDIO_MPEG1);
+ FILL_TYPE (0xe0, 0xef, ST_GST_VIDEO_MPEG1_OR_2);
+ FILL_TYPE (0xf0, 0xff, -1);
+
+#undef FILL_TYPE
+}
+
+static GstFlowReturn
+gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
+{
+ const guint8 *data;
+ guint length;
+ guint32 scr1, scr2;
+ guint64 scr, scr_adjusted, new_rate;
+ GstClockTime new_time;
+
+ GST_DEBUG ("parsing pack start");
+
+ /* fixed length to begin with, start code and two scr values */
+ length = 8 + 4;
+
+ if (!(data = gst_adapter_peek (demux->adapter, length)))
+ goto need_more_data;
+
+ /* skip start code */
+ data += 4;
+
+ scr1 = GUINT32_FROM_BE (*(guint32 *) data);
+ scr2 = GUINT32_FROM_BE (*(guint32 *) (data + 4));
+
+ /* start parsing the stream */
+ if ((*data & 0xc0) == 0x40) {
+ guint32 scr_ext;
+ guint32 next32;
+ guint8 stuffing_bytes;
+
+ GST_DEBUG ("Found MPEG2 stream");
+ demux->is_mpeg2_pack = TRUE;
+
+ /* mpeg2 has more data */
+ length += 2;
+ if (gst_adapter_available (demux->adapter) < length)
+ goto need_more_data;
+
+ /* :2=01 ! scr:3 ! marker:1==1 ! scr:15 ! marker:1==1 ! scr:15 */
+
+ /* check markers */
+ if ((scr1 & 0xc4000400) != 0x44000400)
+ goto lost_sync;
+
+ scr = ((guint64) scr1 & 0x38000000) << 3;
+ scr |= ((guint64) scr1 & 0x03fff800) << 4;
+ scr |= ((guint64) scr1 & 0x000003ff) << 5;
+ scr |= ((guint64) scr2 & 0xf8000000) >> 27;
+
+ /* marker:1==1 ! scr_ext:9 ! marker:1==1 */
+ if ((scr2 & 0x04010000) != 0x04010000)
+ goto lost_sync;
+
+ scr_ext = (scr2 & 0x03fe0000) >> 17;
+ /* We keep the offset of this scr */
+ demux->last_scr_offset = demux->adapter_offset + 12;
+
+ GST_DEBUG_OBJECT (demux, "SCR: 0x%08x SCRE: 0x%08x", scr, scr_ext);
+
+ if (scr_ext) {
+ scr = (scr * 300 + scr_ext % 300) / 300;
+ }
+ /* SCR has been converted into units of 90Khz ticks to make it comparable
+ to DTS/PTS, that also implies 1 tick rounding error */
+ data += 6;
+ /* PMR:22 ! :2==11 ! reserved:5 ! stuffing_len:3 */
+ next32 = (GUINT32_FROM_BE ((*(guint32 *) data)));
+ if ((next32 & 0x00000300) != 0x00000300)
+ goto lost_sync;
+
+ new_rate = (next32 & 0xfffffc00) >> 10;
+
+ stuffing_bytes = (next32 & 0x07);
+ GST_DEBUG_OBJECT (demux, "stuffing bytes: %d", stuffing_bytes);
+
+ data += 4;
+ while (stuffing_bytes--) {
+ if (*data++ != 0xff)
+ goto lost_sync;
+ }
+ } else {
+ GST_DEBUG ("Found MPEG1 stream");
+ demux->is_mpeg2_pack = FALSE;
+
+ /* check markers */
+ if ((scr1 & 0xf1000100) != 0x21000100)
+ goto lost_sync;
+
+ if ((scr2 & 0x01800001) != 0x01800001)
+ goto lost_sync;
+
+ /* :4=0010 ! scr:3 ! marker:1==1 ! scr:15 ! marker:1==1 ! scr:15 ! marker:1==1 */
+ scr = ((guint64) scr1 & 0x0e000000) << 5;
+ scr |= ((guint64) scr1 & 0x00fffe00) << 6;
+ scr |= ((guint64) scr1 & 0x000000ff) << 7;
+ scr |= ((guint64) scr2 & 0xfe000000) >> 25;
+
+ /* We keep the offset of this scr */
+ demux->last_scr_offset = demux->adapter_offset + 8;
+
+ /* marker:1==1 ! mux_rate:22 ! marker:1==1 */
+ new_rate = (scr2 & 0x007ffffe) >> 1;
+
+ data += 8;
+ }
+ new_rate *= MPEG_MUX_RATE_MULT;
+
+ /* scr adjusted is the new scr found + the colected adjustment */
+ scr_adjusted = scr + demux->scr_adjust;
+
+ /* keep the first src in order to calculate delta time */
+ if (demux->first_scr == G_MAXUINT64) {
+ demux->first_scr = scr;
+ demux->first_scr_offset = demux->last_scr_offset;
+
+ if (demux->sink_segment.format == GST_FORMAT_TIME) {
+ demux->base_time = demux->sink_segment.time;
+ } else {
+ demux->base_time = MPEGTIME_TO_GSTTIME (demux->first_scr);
+ }
+ /* at begin consider the new_rate as the scr rate, bytes/clock ticks */
+ demux->scr_rate_n = new_rate;
+ demux->scr_rate_d = CLOCK_FREQ;
+ } else if (demux->first_scr_offset != demux->last_scr_offset) {
+ /* estimate byte rate related to the SCR */
+ demux->scr_rate_n = demux->last_scr_offset - demux->first_scr_offset;
+ demux->scr_rate_d = scr - demux->first_scr;
+ }
+
+ GST_DEBUG_OBJECT (demux,
+ "SCR: %" G_GINT64_FORMAT " (%" G_GINT64_FORMAT "), mux_rate %"
+ G_GINT64_FORMAT ", GStreamer Time:%" GST_TIME_FORMAT,
+ scr, scr_adjusted, new_rate,
+ GST_TIME_ARGS (MPEGTIME_TO_GSTTIME ((guint64) scr - demux->first_scr)));
+
+ GST_DEBUG_OBJECT (demux, "%s mode scr: %" G_GUINT64_FORMAT " at %"
+ G_GUINT64_FORMAT ", first scr: %" G_GUINT64_FORMAT
+ " at %" G_GUINT64_FORMAT ", scr rate: %" G_GUINT64_FORMAT
+ "/%" G_GUINT64_FORMAT "(%f)",
+ ((demux->sink_segment.rate >= 0.0) ? "forward" : "backward"),
+ scr, demux->last_scr_offset,
+ demux->first_scr, demux->first_scr_offset,
+ demux->scr_rate_n, demux->scr_rate_d,
+ (float) demux->scr_rate_n / demux->scr_rate_d);
+
+ /* adjustment of the SCR */
+ if (demux->current_scr != G_MAXUINT64) {
+ gint64 diff;
+ guint64 old_scr, old_mux_rate, bss, adjust;
+
+ /* keep SCR of the previous packet */
+ old_scr = demux->current_scr;
+ old_mux_rate = demux->mux_rate;
+
+ /* Bytes since SCR is the amount we placed in the adapter since then
+ * (demux->bytes_since_scr) minus the amount remaining in the adapter,
+ * clamped to >= 0 */
+ bss = MAX (0, (gint) (demux->bytes_since_scr -
+ gst_adapter_available (demux->adapter)));
+
+ /* estimate the new SCR using the previous one according the notes
+ on point 2.5.2.2 of the ISO/IEC 13818-1 document */
+ adjust = (bss * CLOCK_FREQ) / old_mux_rate;
+ if (demux->sink_segment.rate >= 0.0)
+ demux->next_scr = old_scr + adjust;
+ else
+ demux->next_scr = old_scr - adjust;
+
+ GST_DEBUG_OBJECT (demux,
+ "bss: %" G_GUINT64_FORMAT ", next_scr: %" G_GUINT64_FORMAT
+ ", old_scr: %" G_GUINT64_FORMAT ", scr: %" G_GUINT64_FORMAT,
+ bss, demux->next_scr, old_scr, scr_adjusted);
+
+ /* calculate the absolute deference between the last scr and
+ the new one */
+ if (old_scr > scr_adjusted)
+ diff = old_scr - scr_adjusted;
+ else
+ diff = scr_adjusted - old_scr;
+
+ /* if the difference is more than 1 second we need to reconfigure
+ adjustment */
+ if (diff > CLOCK_FREQ) {
+#if 0
+ demux->scr_adjust = demux->next_scr - scr;
+ GST_DEBUG_OBJECT (demux, "discont found, diff: %" G_GINT64_FORMAT
+ ", adjust %" G_GINT64_FORMAT, diff, demux->scr_adjust);
+ scr_adjusted = demux->next_scr;
+#else
+ g_print ("Unexpected SCR diff of %" G_GINT64_FORMAT "\n", diff);
+#endif
+ } else {
+ demux->next_scr = scr_adjusted;
+ }
+ }
+
+ /* update the current_scr and rate members */
+ demux->mux_rate = new_rate;
+ demux->current_scr = scr_adjusted;
+
+ new_time = MPEGTIME_TO_GSTTIME (scr_adjusted);
+ if (new_time != GST_CLOCK_TIME_NONE) {
+ // g_print ("SCR now %" GST_TIME_FORMAT "\n", GST_TIME_ARGS (new_time));
+ gst_flups_demux_send_segment_updates (demux, new_time);
+ demux->src_segment.last_stop = new_time;
+ }
+
+ /* Reset the bytes_since_scr value to count the data remaining in the
+ * adapter */
+ demux->bytes_since_scr = gst_adapter_available (demux->adapter);
+
+ gst_adapter_flush (demux->adapter, length);
+ ADAPTER_OFFSET_FLUSH (length);
+ return GST_FLOW_OK;
+
+lost_sync:
+ {
+ GST_DEBUG_OBJECT (demux, "lost sync");
+ return GST_FLOW_LOST_SYNC;
+ }
+need_more_data:
+ {
+ GST_DEBUG_OBJECT (demux, "need more data");
+ return GST_FLOW_NEED_MORE_DATA;
+ }
+}
+
+static GstFlowReturn
+gst_flups_demux_parse_sys_head (GstFluPSDemux * demux)
+{
+ guint16 length;
+ const guint8 *data;
+ gboolean csps;
+
+ /* start code + length */
+ if (!(data = gst_adapter_peek (demux->adapter, 6)))
+ goto need_more_data;
+
+ /* skip start code */
+ data += 4;
+
+ length = GST_READ_UINT16_BE (data);
+ GST_DEBUG_OBJECT (demux, "length %d", length);
+
+ length += 6;
+
+ if (!(data = gst_adapter_peek (demux->adapter, length)))
+ goto need_more_data;
+
+ /* skip start code and length */
+ data += 6;
+
+ /* marker:1==1 ! rate_bound:22 | marker:1==1 */
+ if ((*data & 0x80) != 0x80)
+ goto marker_expected;
+
+ {
+ guint32 rate_bound;
+
+ if ((data[2] & 0x01) != 0x01)
+ goto marker_expected;
+
+ rate_bound = ((guint32) data[0] & 0x7f) << 15;
+ rate_bound |= ((guint32) data[1]) << 7;
+ rate_bound |= ((guint32) data[2] & 0xfe) >> 1;
+ rate_bound *= MPEG_MUX_RATE_MULT;
+
+ GST_DEBUG_OBJECT (demux, "rate bound %u", rate_bound);
+
+ data += 3;
+ }
+
+ /* audio_bound:6==1 ! fixed:1 | constrained:1 */
+ {
+ guint8 audio_bound;
+ gboolean fixed;
+
+ /* max number of simultaneous audio streams active */
+ audio_bound = (data[0] & 0xfc) >> 2;
+ /* fixed or variable bitrate */
+ fixed = (data[0] & 0x02) == 0x02;
+ /* meeting constraints */
+ csps = (data[0] & 0x01) == 0x01;
+
+ GST_DEBUG_OBJECT (demux, "audio_bound %d, fixed %d, constrained %d",
+ audio_bound, fixed, csps);
+ data += 1;
+ }
+
+ /* audio_lock:1 | video_lock:1 | marker:1==1 | video_bound:5 */
+ {
+ gboolean audio_lock;
+ gboolean video_lock;
+ guint8 video_bound;
+
+ audio_lock = (data[0] & 0x80) == 0x80;
+ video_lock = (data[0] & 0x40) == 0x40;
+
+ if ((data[0] & 0x20) != 0x20)
+ goto marker_expected;
+
+ /* max number of simultaneous video streams active */
+ video_bound = (data[0] & 0x1f);
+
+ GST_DEBUG_OBJECT (demux, "audio_lock %d, video_lock %d, video_bound %d",
+ audio_lock, video_lock, video_bound);
+ data += 1;
+ }
+
+ /* packet_rate_restriction:1 | reserved:7==0x7F */
+ {
+ gboolean packet_rate_restriction;
+
+ if ((data[0] & 0x7f) != 0x7f)
+ goto marker_expected;
+
+ /* only valid if csps is set */
+ if (csps) {
+ packet_rate_restriction = (data[0] & 0x80) == 0x80;
+
+ GST_DEBUG_OBJECT (demux, "packet_rate_restriction %d",
+ packet_rate_restriction);
+ }
+ }
+ data += 1;
+
+ {
+ gint stream_count = (length - 12) / 3;
+ gint i;
+
+ GST_DEBUG_OBJECT (demux, "number of streams: %d ", stream_count);
+
+ for (i = 0; i < stream_count; i++) {
+ guint8 stream_id;
+ gboolean STD_buffer_bound_scale;
+ guint16 STD_buffer_size_bound;
+ guint32 buf_byte_size_bound;
+
+ stream_id = *data++;
+ if (!(stream_id & 0x80))
+ goto sys_len_error;
+
+ /* check marker bits */
+ if ((*data & 0xC0) != 0xC0)
+ goto no_placeholder_bits;
+
+ STD_buffer_bound_scale = *data & 0x20;
+ STD_buffer_size_bound = ((guint16) (*data++ & 0x1F)) << 8;
+ STD_buffer_size_bound |= *data++;
+
+ if (STD_buffer_bound_scale == 0) {
+ buf_byte_size_bound = STD_buffer_size_bound * 128;
+ } else {
+ buf_byte_size_bound = STD_buffer_size_bound * 1024;
+ }
+
+ GST_DEBUG_OBJECT (demux, "STD_buffer_bound_scale %d",
+ STD_buffer_bound_scale);
+ GST_DEBUG_OBJECT (demux, "STD_buffer_size_bound %d or %d bytes",
+ STD_buffer_size_bound, buf_byte_size_bound);
+ }
+ }
+
+ gst_adapter_flush (demux->adapter, length);
+ ADAPTER_OFFSET_FLUSH (length);
+ return GST_FLOW_OK;
+
+ /* ERRORS */
+marker_expected:
+ {
+ GST_DEBUG_OBJECT (demux, "expecting marker");
+ return GST_FLOW_LOST_SYNC;
+ }
+no_placeholder_bits:
+ {
+ GST_DEBUG_OBJECT (demux, "expecting placeholder bit values"
+ " '11' after stream id");
+ return GST_FLOW_LOST_SYNC;
+ }
+sys_len_error:
+ {
+ GST_DEBUG_OBJECT (demux, "error in system header length");
+ return GST_FLOW_LOST_SYNC;
+ }
+need_more_data:
+ {
+ GST_DEBUG_OBJECT (demux, "need more data");
+ return GST_FLOW_NEED_MORE_DATA;
+ }
+}
+
+static GstFlowReturn
+gst_flups_demux_parse_psm (GstFluPSDemux * demux)
+{
+ guint16 length = 0, info_length = 0, es_map_length = 0;
+ guint8 psm_version = 0;
+ const guint8 *data, *es_map_base;
+ gboolean applicable;
+
+ /* start code + length */
+ if (!(data = gst_adapter_peek (demux->adapter, 6)))
+ goto need_more_data;
+
+ /* skip start code */
+ data += 4;
+
+ length = GST_READ_UINT16_BE (data);
+ GST_DEBUG_OBJECT (demux, "length %u", length);
+
+ if (G_UNLIKELY (length > 0x3FA))
+ goto psm_len_error;
+
+ length += 6;
+
+ if (!(data = gst_adapter_peek (demux->adapter, length)))
+ goto need_more_data;
+
+ /* skip start code and length */
+ data += 6;
+
+ /* Read PSM applicable bit together with version */
+ psm_version = GST_READ_UINT8 (data);
+ applicable = (psm_version & 0x80) >> 7;
+ psm_version &= 0x1F;
+ GST_DEBUG_OBJECT (demux, "PSM version %u (applicable now %u)", psm_version,
+ applicable);
+
+ /* Jump over version and marker bit */
+ data += 2;
+
+ /* Read PS info length */
+ info_length = GST_READ_UINT16_BE (data);
+ /* Cap it to PSM length - needed bytes for ES map length and CRC */
+ info_length = MIN (length - 16, info_length);
+ GST_DEBUG_OBJECT (demux, "PS info length %u bytes", info_length);
+
+ /* Jump over that section */
+ data += (2 + info_length);
+
+ /* Read ES map length */
+ es_map_length = GST_READ_UINT16_BE (data);
+ /* Cap it to PSM remaining length - CRC */
+ es_map_length = MIN (length - (16 + info_length), es_map_length);
+ GST_DEBUG_OBJECT (demux, "ES map length %u bytes", es_map_length);
+
+ /* Jump over the size */
+ data += 2;
+
+ /* Now read the ES map */
+ es_map_base = data;
+ while (es_map_base + 4 <= data + es_map_length) {
+ guint8 stream_type = 0, stream_id = 0;
+ guint16 stream_info_length = 0;
+
+ stream_type = GST_READ_UINT8 (es_map_base);
+ es_map_base++;
+ stream_id = GST_READ_UINT8 (es_map_base);
+ es_map_base++;
+ stream_info_length = GST_READ_UINT16_BE (es_map_base);
+ es_map_base += 2;
+ /* Cap stream_info_length */
+ stream_info_length = MIN (data + es_map_length - es_map_base,
+ stream_info_length);
+
+ GST_DEBUG_OBJECT (demux, "Stream type %02X with id %02X and %u bytes info",
+ stream_type, stream_id, stream_info_length);
+ demux->psm[stream_id] = stream_type;
+ es_map_base += stream_info_length;
+ }
+
+ gst_adapter_flush (demux->adapter, length);
+ ADAPTER_OFFSET_FLUSH (length);
+ return GST_FLOW_OK;
+
+psm_len_error:
+ {
+ GST_DEBUG_OBJECT (demux, "error in PSM length");
+ return GST_FLOW_LOST_SYNC;
+ }
+need_more_data:
+ {
+ GST_DEBUG_OBJECT (demux, "need more data");
+ return GST_FLOW_NEED_MORE_DATA;
+ }
+}
+
+static void
+gst_flups_demux_resync_cb (GstPESFilter * filter, GstFluPSDemux * demux)
+{
+}
+
+static GstFlowReturn
+gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first,
+ GstBuffer * buffer, GstFluPSDemux * demux)
+{
+ GstBuffer *out_buf;
+ GstFlowReturn ret = GST_FLOW_OK;
+ gint stream_type;
+ guint32 start_code;
+ guint8 id;
+ guint8 *data;
+ guint datalen;
+ guint offset = 0;
+
+ data = GST_BUFFER_DATA (buffer);
+ datalen = GST_BUFFER_SIZE (buffer);
+
+ start_code = filter->start_code;
+ id = filter->id;
+
+ if (first) {
+ /* find the stream type */
+ stream_type = demux->psm[id];
+ if (stream_type == -1) {
+ /* no stream type, if PS1, get the new id */
+ if (start_code == ID_PRIVATE_STREAM_1 && datalen >= 2) {
+ guint8 nframes;
+
+ /* VDR writes A52 streams without any header bytes
+ * (see ftp://ftp.mplayerhq.hu/MPlayer/samples/MPEG-VOB/vdr-AC3) */
+ if (datalen >= 4) {
+ guint hdr = GST_READ_UINT32_BE (data);
+
+ if (G_UNLIKELY ((hdr & 0xffff0000) == AC3_SYNC_WORD)) {
+ id = 0x80;
+ stream_type = demux->psm[id] = ST_GST_AUDIO_RAWA52;
+ GST_DEBUG_OBJECT (demux, "Found VDR raw A52 stream");
+ }
+ }
+
+ if (G_LIKELY (stream_type == -1)) {
+ /* new id is in the first byte */
+ id = data[offset++];
+ datalen--;
+
+ /* and remap */
+ stream_type = demux->psm[id];
+
+ /* Now, if it's a subpicture stream - no more, otherwise
+ * take the first byte too, since it's the frame count in audio
+ * streams and our backwards compat convention is to strip it off */
+ if (stream_type != ST_PS_DVD_SUBPICTURE) {
+ /* Number of audio frames in this packet */
+ nframes = data[offset++];
+ datalen--;
+ GST_DEBUG_OBJECT (demux, "private type 0x%02x, %d frames", id,
+ nframes);
+ } else {
+ GST_DEBUG_OBJECT (demux, "private type 0x%02x, stream type %d", id,
+ stream_type);
+ }
+ }
+ }
+ if (stream_type == -1)
+ goto unknown_stream_type;
+ }
+ if (filter->pts != -1) {
+ demux->next_pts = filter->pts + demux->scr_adjust;
+ GST_DEBUG_OBJECT (demux, "PTS = %" G_GUINT64_FORMAT
+ "(%" G_GUINT64_FORMAT ")", filter->pts, demux->next_pts);
+ } else
+ demux->next_pts = G_MAXUINT64;
+
+ if (filter->dts != -1) {
+ demux->next_dts = filter->dts + demux->scr_adjust;
+ } else {
+ demux->next_dts = demux->next_pts;
+ }
+ GST_DEBUG_OBJECT (demux, "DTS = orig %" G_GUINT64_FORMAT
+ " (%" G_GUINT64_FORMAT ")", filter->dts, demux->next_dts);
+
+ demux->current_stream = gst_flups_demux_get_stream (demux, id, stream_type);
+ }
+
+ if (demux->current_stream == NULL) {
+ GST_DEBUG_OBJECT (demux, "Dropping buffer for unknown stream id 0x%02x",
+ id);
+ goto done;
+ }
+
+ /* After 2 seconds of bitstream emit no more pads */
+ if (demux->need_no_more_pads
+ && (demux->current_scr - demux->first_scr - demux->scr_adjust) >
+ 2 * CLOCK_FREQ) {
+ GST_DEBUG_OBJECT (demux, "no more pads, notifying");
+ gst_element_no_more_pads (GST_ELEMENT_CAST (demux));
+ demux->need_no_more_pads = FALSE;
+ }
+
+ /* If the stream is not-linked, don't bother creating a sub-buffer
+ * to send to it, unless we're processing a discont (which resets
+ * the not-linked status and tries again */
+ if (demux->current_stream->discont) {
+ GST_DEBUG_OBJECT (demux, "stream is discont");
+ demux->current_stream->notlinked = FALSE;
+ }
+
+ if (demux->current_stream->notlinked == FALSE) {
+ out_buf = gst_buffer_create_sub (buffer, offset, datalen);
+
+ ret = gst_flups_demux_send_data (demux, demux->current_stream, out_buf);
+ if (ret == GST_FLOW_NOT_LINKED) {
+ demux->current_stream->notlinked = TRUE;
+ ret = GST_FLOW_OK;
+ }
+ }
+
+done:
+ gst_buffer_unref (buffer);
+
+ return ret;
+
+ /* ERRORS */
+unknown_stream_type:
+ {
+ GST_DEBUG_OBJECT (demux, "unknown stream type %02x", id);
+ ret = GST_FLOW_OK;
+ goto done;
+ }
+}
+
+static gboolean
+gst_flups_demux_resync (GstFluPSDemux * demux, gboolean save)
+{
+ const guint8 *data;
+ gint avail;
+ guint32 code;
+ gint offset;
+ gboolean found;
+
+ avail = gst_adapter_available (demux->adapter);
+ if (avail < 4)
+ goto need_data;
+
+ /* Common case, read 4 bytes an check it */
+ data = gst_adapter_peek (demux->adapter, 4);
+
+ /* read currect code */
+ code = GST_READ_UINT32_BE (data);
+
+ /* The common case is that the sync code is at 0 bytes offset */
+ if (G_LIKELY ((code & 0xffffff00) == 0x100L)) {
+ GST_LOG_OBJECT (demux, "Found resync code %08x after 0 bytes", code);
+ demux->last_sync_code = code;
+ return TRUE;
+ }
+
+ /* Otherwise, we are starting at byte 4 and we need to search
+ the sync code in all available data in the adapter */
+ offset = 4;
+ if (offset >= avail)
+ goto need_data; /* Not enough data to find sync */
+
+ data = gst_adapter_peek (demux->adapter, avail);
+
+ do {
+ code = (code << 8) | data[offset++];
+ found = (code & 0xffffff00) == 0x100L;
+ } while (offset < avail && !found);
+
+ if (!save || demux->sink_segment.rate >= 0.0) {
+ GST_LOG_OBJECT (demux, "flushing %d bytes", offset - 4);
+ /* forward playback, we can discard and flush the skipped bytes */
+ gst_adapter_flush (demux->adapter, offset - 4);
+ ADAPTER_OFFSET_FLUSH (offset - 4);
+ } else {
+ if (found) {
+ GST_LOG_OBJECT (demux, "reverse saving %d bytes", offset - 4);
+ /* reverse playback, we keep the flushed bytes and we will append them to
+ * the next buffer in the chain function, which is the previous buffer in
+ * the stream. */
+ gst_adapter_push (demux->rev_adapter,
+ gst_adapter_take_buffer (demux->adapter, offset - 4));
+ } else {
+ GST_LOG_OBJECT (demux, "reverse saving %d bytes", avail);
+ /* nothing found, keep all bytes */
+ gst_adapter_push (demux->rev_adapter,
+ gst_adapter_take_buffer (demux->adapter, avail));
+ }
+ }
+
+ if (found) {
+ GST_LOG_OBJECT (demux, "Found resync code %08x after %d bytes",
+ code, offset - 4);
+ demux->last_sync_code = code;
+ } else {
+ GST_LOG_OBJECT (demux, "No resync after skipping %d", offset);
+ }
+
+ return found;
+
+need_data:
+ {
+ GST_LOG_OBJECT (demux, "we need more data for resync %d", avail);
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_flups_demux_is_pes_sync (guint32 sync)
+{
+ return ((sync & 0xfc) == 0xbc) ||
+ ((sync & 0xe0) == 0xc0) || ((sync & 0xf0) == 0xe0);
+}
+
+static GstFlowReturn
+gst_flups_demux_chain (GstPad * pad, GstBuffer * buffer)
+{
+ GstFluPSDemux *demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad));
+ GstFlowReturn ret = GST_FLOW_OK;
+ guint32 avail;
+ gboolean save, discont;
+
+ discont = GST_BUFFER_IS_DISCONT (buffer);
+
+ if (discont) {
+ GST_LOG_OBJECT (demux, "Received buffer with discont flag and"
+ " offset %" G_GUINT64_FORMAT, GST_BUFFER_OFFSET (buffer));
+
+ gst_pes_filter_drain (&demux->filter);
+ gst_flups_demux_mark_discont (demux);
+
+ /* mark discont on all streams */
+ if (demux->sink_segment.rate >= 0.0) {
+ demux->current_scr = G_MAXUINT64;
+ demux->bytes_since_scr = 0;
+ }
+ } else {
+ GST_LOG_OBJECT (demux, "Received buffer with offset %" G_GUINT64_FORMAT,
+ GST_BUFFER_OFFSET (buffer));
+ }
+
+ /* We keep the offset to interpolate SCR */
+ demux->adapter_offset = GST_BUFFER_OFFSET (buffer);
+
+ gst_adapter_push (demux->adapter, buffer);
+ demux->bytes_since_scr += GST_BUFFER_SIZE (buffer);
+
+ avail = gst_adapter_available (demux->rev_adapter);
+ if (avail > 0) {
+ GST_LOG_OBJECT (demux, "appending %u saved bytes", avail);
+ /* if we have a previous reverse chunk, append this now */
+ /* FIXME this code assumes we receive discont buffers all thei
+ * time */
+ gst_adapter_push (demux->adapter,
+ gst_adapter_take_buffer (demux->rev_adapter, avail));
+ }
+
+ avail = gst_adapter_available (demux->adapter);
+ GST_LOG_OBJECT (demux, "avail now: %d, state %d", avail, demux->filter.state);
+
+ switch (demux->filter.state) {
+ case STATE_DATA_SKIP:
+ case STATE_DATA_PUSH:
+ ret = gst_pes_filter_process (&demux->filter);
+ break;
+ case STATE_HEADER_PARSE:
+ break;
+ default:
+ break;
+ }
+
+ switch (ret) {
+ case GST_FLOW_NEED_MORE_DATA:
+ /* Go and get more data */
+ ret = GST_FLOW_OK;
+ goto done;
+ case GST_FLOW_LOST_SYNC:
+ /* for FLOW_OK or lost-sync, carry onto resync */
+ ret = GST_FLOW_OK;
+ break;
+ case GST_FLOW_OK:
+ break;
+ default:
+ /* Any other return value should be sent upstream immediately */
+ goto done;
+ }
+
+ /* align adapter data to sync boundary, we keep the data up to the next sync
+ * point. */
+ save = TRUE;
+ while (gst_flups_demux_resync (demux, save)) {
+ gboolean ps_sync = TRUE;
+
+ /* now switch on last synced byte */
+ switch (demux->last_sync_code) {
+ case ID_PS_PACK_START_CODE:
+ ret = gst_flups_demux_parse_pack_start (demux);
+ break;
+ case ID_PS_SYSTEM_HEADER_START_CODE:
+ ret = gst_flups_demux_parse_sys_head (demux);
+ break;
+ case ID_PS_END_CODE:
+ ret = GST_FLOW_OK;
+ goto done;
+ case ID_PS_PROGRAM_STREAM_MAP:
+ ret = gst_flups_demux_parse_psm (demux);
+ break;
+ default:
+ if (gst_flups_demux_is_pes_sync (demux->last_sync_code)) {
+ ret = gst_pes_filter_process (&demux->filter);
+ } else {
+ GST_DEBUG_OBJECT (demux, "sync_code=%08x, non PES sync found"
+ ", continuing", demux->last_sync_code);
+ ps_sync = FALSE;
+ ret = GST_FLOW_LOST_SYNC;
+ }
+ break;
+ }
+ /* if we found a ps sync, we stop saving the data, any non-ps sync gets
+ * saved up to the next ps sync. */
+ if (ps_sync)
+ save = FALSE;
+
+ switch (ret) {
+ case GST_FLOW_NEED_MORE_DATA:
+ GST_DEBUG_OBJECT (demux, "need more data");
+ ret = GST_FLOW_OK;
+ goto done;
+ case GST_FLOW_LOST_SYNC:
+ if (!save || demux->sink_segment.rate >= 0.0) {
+ GST_DEBUG_OBJECT (demux, "flushing 3 bytes");
+ gst_adapter_flush (demux->adapter, 3);
+ ADAPTER_OFFSET_FLUSH (3);
+ } else {
+ GST_DEBUG_OBJECT (demux, "saving 3 bytes");
+ gst_adapter_push (demux->rev_adapter,
+ gst_adapter_take_buffer (demux->adapter, 3));
+ }
+ ret = GST_FLOW_OK;
+ break;
+ default:
+ break;
+ }
+ }
+done:
+ gst_object_unref (demux);
+
+ return ret;
+}
+
+static GstStateChangeReturn
+gst_flups_demux_change_state (GstElement * element, GstStateChange transition)
+{
+ GstFluPSDemux *demux = GST_FLUPS_DEMUX (element);
+ GstStateChangeReturn result;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ demux->adapter = gst_adapter_new ();
+ demux->rev_adapter = gst_adapter_new ();
+ demux->adapter_offset = G_MAXUINT64;
+ gst_pes_filter_init (&demux->filter, demux->adapter,
+ &demux->adapter_offset);
+ gst_pes_filter_set_callbacks (&demux->filter,
+ (GstPESFilterData) gst_flups_demux_data_cb,
+ (GstPESFilterResync) gst_flups_demux_resync_cb, demux);
+ demux->filter.gather_pes = TRUE;
+ demux->first_scr = G_MAXUINT64;
+ demux->bytes_since_scr = 0;
+ demux->current_scr = G_MAXUINT64;
+ demux->base_time = G_MAXUINT64;
+ demux->scr_rate_n = G_MAXUINT64;
+ demux->scr_rate_d = G_MAXUINT64;
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ demux->current_scr = G_MAXUINT64;
+ demux->mux_rate = G_MAXUINT64;
+ demux->next_pts = G_MAXUINT64;
+ demux->next_dts = G_MAXUINT64;
+ demux->first_scr = G_MAXUINT64;
+ demux->bytes_since_scr = 0;
+ demux->base_time = G_MAXUINT64;
+ demux->scr_rate_n = G_MAXUINT64;
+ demux->scr_rate_d = G_MAXUINT64;
+ demux->need_no_more_pads = TRUE;
+
+ gst_flups_demux_reset_psm (demux);
+ gst_segment_init (&demux->sink_segment, GST_FORMAT_UNDEFINED);
+ gst_segment_init (&demux->src_segment, GST_FORMAT_TIME);
+ break;
+ default:
+ break;
+ }
+
+ result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ gst_flups_demux_reset (demux);
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ gst_pes_filter_uninit (&demux->filter);
+ g_object_unref (demux->adapter);
+ demux->adapter = NULL;
+ g_object_unref (demux->rev_adapter);
+ demux->rev_adapter = NULL;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+gboolean
+gst_flups_demux_plugin_init (GstPlugin * plugin)
+{
+ GST_DEBUG_CATEGORY_INIT (gstflupesfilter_debug, "rsnpesfilter", 0,
+ "MPEG program stream PES filter debug");
+
+ GST_DEBUG_CATEGORY_INIT (gstflupsdemux_debug, "rsndvddemux", 0,
+ "MPEG program stream demuxer debug");
+
+ return TRUE;
+}
diff --git a/ext/resindvd/gstmpegdemux.h b/ext/resindvd/gstmpegdemux.h
new file mode 100644
index 00000000..fd566afa
--- /dev/null
+++ b/ext/resindvd/gstmpegdemux.h
@@ -0,0 +1,127 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is Fluendo MPEG Demuxer plugin.
+ *
+ * The Initial Developer of the Original Code is Fluendo, S.L.
+ * Portions created by Fluendo, S.L. are Copyright (C) 2005
+ * Fluendo, S.L. All Rights Reserved.
+ *
+ * Contributor(s): Wim Taymans <wim@fluendo.com>
+ * Jan Schmidt <thaytan@noraisin.net>
+ */
+
+#ifndef __GST_FLUPS_DEMUX_H__
+#define __GST_FLUPS_DEMUX_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+
+#include "gstpesfilter.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_FLUPS_DEMUX (gst_flups_demux_get_type())
+#define GST_FLUPS_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FLUPS_DEMUX,GstFluPSDemux))
+#define GST_FLUPS_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FLUPS_DEMUX,GstFluPSDemuxClass))
+#define GST_FLUPS_DEMUX_GET_CLASS(klass) (G_TYPE_INSTANCE_GET_CLASS((klass),GST_TYPE_FLUPS_DEMUX,GstFluPSDemuxClass))
+#define GST_IS_FLUPS_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FLUPS_DEMUX))
+#define GST_IS_FLUPS_DEMUX_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FLUPS_DEMUX))
+
+typedef struct _GstFluPSStream GstFluPSStream;
+typedef struct _GstFluPSDemux GstFluPSDemux;
+typedef struct _GstFluPSDemuxClass GstFluPSDemuxClass;
+
+#define GST_FLUPS_DEMUX_MAX_STREAMS 256
+#define GST_FLUPS_DEMUX_MAX_PSM 256
+
+typedef enum {
+ STATE_FLUPS_DEMUX_NEED_SYNC,
+ STATE_FLUPS_DEMUX_SYNCED,
+ STATE_FLUPS_DEMUX_NEED_MORE_DATA,
+} GstFluPSDemuxState;
+
+/* Information associated with a single FluPS stream. */
+struct _GstFluPSStream {
+ GstPad * pad;
+
+ gint id;
+ gint type;
+ gint size_bound;
+
+ gboolean discont;
+ gboolean notlinked;
+ gboolean need_segment;
+
+ GstClockTime last_ts;
+};
+
+struct _GstFluPSDemux {
+ GstElement parent;
+
+ GstPad * sinkpad;
+
+ GstAdapter * adapter;
+ GstAdapter * rev_adapter;
+ guint64 adapter_offset;
+ guint32 last_sync_code;
+ GstPESFilter filter;
+
+ gint64 mux_rate;
+ guint64 first_scr;
+ guint64 first_dts;
+ guint64 base_time;
+ guint64 current_scr;
+ guint64 next_scr;
+ guint64 bytes_since_scr;
+ gint64 scr_adjust;
+ guint64 scr_rate_n;
+ guint64 scr_rate_d;
+ guint64 first_scr_offset;
+ guint64 last_scr_offset;
+
+ gint16 psm[GST_FLUPS_DEMUX_MAX_PSM];
+
+ GstSegment sink_segment;
+ GstSegment src_segment;
+
+ /* stream output */
+ GstFluPSStream * current_stream;
+ guint64 next_pts;
+ guint64 next_dts;
+ GstFluPSStream ** streams;
+ gboolean need_no_more_pads;
+
+ /* Indicates an MPEG-2 stream */
+ gboolean is_mpeg2_pack;
+
+ /* Language codes event is stored when a dvd-lang-codes
+ * custom event arrives from upstream */
+ GstEvent * lang_codes;
+};
+
+struct _GstFluPSDemuxClass {
+ GstElementClass parent_class;
+
+ GstPadTemplate *sink_template;
+ GstPadTemplate *video_template;
+ GstPadTemplate *audio_template;
+ GstPadTemplate *private_template;
+ GstPadTemplate *subpicture_template;
+};
+
+GType gst_flups_demux_get_type (void);
+
+gboolean gst_flups_demux_plugin_init (GstPlugin *plugin);
+
+G_END_DECLS
+
+#endif /* __GST_FLUPS_DEMUX_H__ */
diff --git a/ext/resindvd/gstmpegdesc.c b/ext/resindvd/gstmpegdesc.c
new file mode 100644
index 00000000..514e5d31
--- /dev/null
+++ b/ext/resindvd/gstmpegdesc.c
@@ -0,0 +1,156 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is Fluendo MPEG Demuxer plugin.
+ *
+ * The Initial Developer of the Original Code is Fluendo, S.L.
+ * Portions created by Fluendo, S.L. are Copyright (C) 2005
+ * Fluendo, S.L. All Rights Reserved.
+ *
+ * Contributor(s): Wim Taymans <wim@fluendo.com>
+ * Jan Schmidt <thaytan@noraisin.net>
+ */
+
+#include <string.h>
+
+#include <gst/gst.h>
+
+#include "gstmpegdesc.h"
+
+void
+gst_mpeg_descriptor_free (GstMPEGDescriptor * desc)
+{
+ g_return_if_fail (desc != NULL);
+
+ g_free (desc);
+}
+
+static guint
+gst_mpeg_descriptor_parse_1 (guint8 * data, guint size)
+{
+ guint8 tag;
+ guint8 length;
+
+ /* need at least 2 bytes for tag and length */
+ if (size < 2)
+ return 0;
+
+ tag = *data++;
+ length = *data++;
+ size -= 2;
+
+ GST_DEBUG ("tag: 0x%02x, length: %d", tag, length);
+
+ if (length > size)
+ return 0;
+
+ return length + 2;;
+}
+
+GstMPEGDescriptor *
+gst_mpeg_descriptor_parse (guint8 * data, guint size)
+{
+ guint8 *current;
+ guint consumed, total, n_desc;
+ GstMPEGDescriptor *result;
+
+ g_return_val_if_fail (data != NULL, NULL);
+
+ current = data;
+ total = 0;
+ n_desc = 0;
+
+ do {
+ consumed = gst_mpeg_descriptor_parse_1 (current, size);
+
+ if (consumed > 0) {
+ current += consumed;
+ total += consumed;
+ size -= consumed;
+ n_desc++;
+ }
+ }
+ while (consumed > 0);
+
+ GST_DEBUG ("parsed %d descriptors", n_desc);
+
+ if (total == 0)
+ return NULL;
+
+ result = g_malloc (sizeof (GstMPEGDescriptor) + total);
+ result->n_desc = n_desc;
+ result->data_length = total;
+ result->data = ((guint8 *) result) + sizeof (GstMPEGDescriptor);
+
+ memcpy (result->data, data, total);
+
+ return result;
+}
+
+guint
+gst_mpeg_descriptor_n_desc (GstMPEGDescriptor * desc)
+{
+ g_return_val_if_fail (desc != NULL, 0);
+
+ return desc->n_desc;
+}
+
+guint8 *
+gst_mpeg_descriptor_find (GstMPEGDescriptor * desc, gint tag)
+{
+ gint length;
+ guint8 *current;
+ guint size;
+
+ g_return_val_if_fail (desc != NULL, NULL);
+
+ current = desc->data;
+ length = desc->data_length;
+
+ while (length > 0) {
+ if (DESC_TAG (current) == tag)
+ return current;
+
+ size = DESC_LENGTH (current) + 2;
+
+ current += size;
+ length -= size;
+ }
+ return NULL;
+}
+
+guint8 *
+gst_mpeg_descriptor_nth (GstMPEGDescriptor * desc, guint i)
+{
+ gint length;
+ guint8 *current;
+ guint size;
+
+ g_return_val_if_fail (desc != NULL, NULL);
+
+ if (i > desc->n_desc)
+ return NULL;
+
+ current = desc->data;
+ length = desc->data_length;
+
+ while (length > 0) {
+ if (i == 0)
+ return current;
+
+ size = DESC_LENGTH (current) + 2;
+
+ current += size;
+ length -= size;
+ i--;
+ }
+ return NULL;
+}
diff --git a/ext/resindvd/gstmpegdesc.h b/ext/resindvd/gstmpegdesc.h
new file mode 100644
index 00000000..5b056128
--- /dev/null
+++ b/ext/resindvd/gstmpegdesc.h
@@ -0,0 +1,249 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is Fluendo MPEG Demuxer plugin.
+ *
+ * The Initial Developer of the Original Code is Fluendo, S.L.
+ * Portions created by Fluendo, S.L. are Copyright (C) 2005
+ * Fluendo, S.L. All Rights Reserved.
+ *
+ * Contributor(s): Wim Taymans <wim@fluendo.com>
+ * Jan Schmidt <thaytan@noraisin.net>
+ */
+
+#ifndef __GST_MPEG_DESC_H__
+#define __GST_MPEG_DESC_H__
+
+#include <glib.h>
+/*
+ * descriptor_tag TS PS Identification
+ * 0 n/a n/a Reserved
+ * 1 n/a n/a Reserved
+ * 2 X X video_stream_descriptor
+ * 3 X X audio_stream_descriptor
+ * 4 X X hierarchy_descriptor
+ * 5 X X registration_descriptor
+ * 6 X X data_stream_alignment_descriptor
+ * 7 X X target_background_grid_descriptor
+ * 8 X X video_window_descriptor
+ * 9 X X CA_descriptor
+ * 10 X X ISO_639_language_descriptor
+ * 11 X X system_clock_descriptor
+ * 12 X X multiplex_buffer_utilization_descriptor
+ * 13 X X copyright_descriptor
+ * 14 X maximum bitrate descriptor
+ * 15 X X private data indicator descriptor
+ * 16 X X smoothing buffer descriptor
+ * 17 X STD_descriptor
+ * 18 X X IBP descriptor
+ * 19-63 n/a n/a ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved
+ * 64-255 n/a n/a User Private
+ */
+#define DESC_VIDEO_STREAM 2
+#define DESC_AUDIO_STREAM 3
+#define DESC_HIERARCHY 4
+#define DESC_REGISTRATION 5
+#define DESC_DATA_STREAM_ALIGNMENT 6
+#define DESC_TARGET_BACKGROUND_GRID 7
+#define DESC_VIDEO_WINDOW 8
+#define DESC_CA 9
+#define DESC_ISO_639_LANGUAGE 10
+#define DESC_SYSTEM_CLOCK 11
+#define DESC_MULTIPLEX_BUFFER_UTILISATION 12
+#define DESC_COPYRIGHT 13
+#define DESC_MAXIMUM_BITRATE 14
+#define DESC_PRIVATE_DATA_INDICATOR 15
+#define DESC_SMOOTHING_BUFFER 16
+#define DESC_STD 17
+#define DESC_IBP 18
+
+#define DESC_DIRAC_TC_PRIVATE 0xAC
+
+/* DVB tags */
+#define DESC_DVB_NETWORK_NAME 0x40
+#define DESC_DVB_SERVICE_LIST 0x41
+#define DESC_DVB_STUFFING 0x42
+#define DESC_DVB_SATELLITE_DELIVERY_SYSTEM 0x43
+#define DESC_DVB_CABLE_DELIVERY_SYSTEM 0x44
+#define DESC_DVB_VBI_DATA 0x45
+#define DESC_DVB_VBI_TELETEXT 0x46
+#define DESC_DVB_BOUQUET_NAME 0x47
+#define DESC_DVB_SERVICE 0x48
+#define DESC_DVB_COUNTRY_AVAILABILITY 0x49
+#define DESC_DVB_LINKAGE 0x4A
+#define DESC_DVB_NVOD_REFERENCE 0x4B
+#define DESC_DVB_TIME_SHIFTED_SERVICE 0x4C
+#define DESC_DVB_SHORT_EVENT 0x4D
+#define DESC_DVB_EXTENDED_EVENT 0x4E
+#define DESC_DVB_TIME_SHIFTED_EVENT 0x4F
+#define DESC_DVB_COMPONENT 0x50
+#define DESC_DVB_MOSAIC 0x51
+#define DESC_DVB_STREAM_IDENTIFIER 0x52
+#define DESC_DVB_CA_IDENTIFIER 0x53
+#define DESC_DVB_CONTENT 0x54
+#define DESC_DVB_PARENTAL_RATING 0x55
+#define DESC_DVB_TELETEXT 0x56
+#define DESC_DVB_TELEPHONE 0x57
+#define DESC_DVB_LOCAL_TIME_OFFSET 0x58
+#define DESC_DVB_SUBTITLING 0x59
+#define DESC_DVB_TERRESTRIAL_DELIVERY_SYSTEM 0x5A
+#define DESC_DVB_MULTILINGUAL_NETWORK_NAME 0x5B
+#define DESC_DVB_MULTILINGUAL_BOUQUET_NAME 0x5C
+#define DESC_DVB_MULTILINGUAL_SERVICE_NAME 0x5D
+#define DESC_DVB_MULTILINGUAL_COMPONENT 0x5E
+#define DESC_DVB_PRIVATE_DATA 0x5F
+#define DESC_DVB_SERVICE_MOVE 0x60
+#define DESC_DVB_SHORT_SMOOTHING_BUFFER 0x61
+#define DESC_DVB_FREQUENCY_LIST 0x62
+#define DESC_DVB_PARTIAL_TRANSPORT_STREAM 0x63
+#define DESC_DVB_DATA_BROADCAST 0x64
+#define DESC_DVB_SCRAMBLING 0x65
+#define DESC_DVB_DATA_BROADCAST_ID 0x66
+#define DESC_DVB_TRANSPORT_STREAM 0x67
+#define DESC_DVB_DSNG 0x68
+#define DESC_DVB_PDC 0x69
+#define DESC_DVB_AC3 0x6A
+#define DESC_DVB_ANCILLARY_DATA 0x6B
+#define DESC_DVB_CELL_LIST 0x6C
+#define DESC_DVB_CELL_FREQUENCY_LINK 0x6D
+#define DESC_DVB_ANNOUNCEMENT_SUPPORT 0x6E
+#define DESC_DVB_APPLICATION_SIGNALLING 0x6F
+#define DESC_DVB_ADAPTATION_FIELD_DATA 0x70
+#define DESC_DVB_SERVICE_IDENTIFIER 0x71
+#define DESC_DVB_SERVICE_AVAILABILITY 0x72
+#define DESC_DVB_DEFAULT_AUTHORITY 0x73
+#define DESC_DVB_RELATED_CONTENT 0x74
+#define DESC_DVB_TVA_ID 0x75
+#define DESC_DVB_CONTENT_IDENTIFIER 0x76
+#define DESC_DVB_TIMESLICE_FEC_IDENTIFIER 0x77
+#define DESC_DVB_ECM_REPETITION_RATE 0x78
+#define DESC_DVB_S2_SATELLITE_DELIVERY_SYSTEM 0x79
+#define DESC_DVB_ENHANCED_AC3 0x7A
+#define DESC_DVB_DTS 0x7B
+#define DESC_DVB_AAC 0x7C
+/* 0x7D and 0x7E are reserved for future use */
+#define DESC_DVB_EXTENSION 0x7F
+/* 0x80 - 0xFE are user defined */
+/* 0xFF is forbidden */
+
+/* common for all descriptors */
+#define DESC_TAG(desc) (desc[0])
+#define DESC_LENGTH(desc) (desc[1])
+
+/* video_stream_descriptor */
+#define DESC_VIDEO_STREAM_multiple_framerate_flag(desc) (((desc)[2] & 0x80) == 0x80)
+#define DESC_VIDEO_STREAM_frame_rate_code(desc) (((desc)[2] & 0x38) >> 3)
+#define DESC_VIDEO_STREAM_MPEG_1_only_flag(desc) (((desc)[2] & 0x04) == 0x04)
+#define DESC_VIDEO_STREAM_constrained_parameter_flag(desc) (((desc)[2] & 0x02) == 0x02)
+#define DESC_VIDEO_STREAM_still_picture_flag(desc) (((desc)[2] & 0x01) == 0x01)
+/* if (MPEG_1_only_flag == 1) */
+#define DESC_VIDEO_STREAM_profile_and_level_indication(desc) ((desc)[3])
+#define DESC_VIDEO_STREAM_chroma_format(desc) (((desc)[4] & 0xc0) >> 6)
+#define DESC_VIDEO_STREAM_frame_rate_extension_flag(desc) (((desc)[4] & 0x20) == 0x20)
+
+/* audio_stream_descriptor */
+#define DESC_AUDIO_STREAM_free_format_flag(desc) (((desc)[2] & 0x80) == 0x80)
+#define DESC_AUDIO_STREAM_ID(desc) (((desc)[2] & 0x40) == 0x40)
+#define DESC_AUDIO_STREAM_layer(desc) (((desc)[2] & 0x30) >> 4)
+#define DESC_AUDIO_STREAM_variable_rate_audio_indicator(desc) (((desc)[2] & 0x08) == 0x08)
+
+/* hierarchy_descriptor */
+#define DESC_HIERARCHY_hierarchy_type(desc) (((desc)[2] & 0x0f))
+#define DESC_HIERARCHY_hierarchy_layer_index(desc) (((desc)[3] & 0x3f))
+#define DESC_HIERARCHY_hierarchy_embedded_layer_index(desc) (((desc)[4] & 0x3f))
+#define DESC_HIERARCHY_hierarchy_channel(desc) (((desc)[5] & 0x3f))
+
+/* registration_descriptor */
+#define DESC_REGISTRATION_format_identifier(desc) (GST_READ_UINT32_BE ((desc)+2))
+#define DESC_REGISTRATION_additional_ident_info_len(desc) ((desc)[1] - 4)
+#define DESC_REGISTRATION_additional_ident_info(desc) (&(desc)[6])
+
+/* data_stream_alignment_descriptor */
+#define DESC_DATA_STREAM_ALIGNMENT_alignment_type(desc) ((desc)[2])
+
+/* target_background_grid_descriptor */
+#define DESC_TARGET_BACKGROUND_GRID_horizontal_size(desc) (GST_READ_UINT16_BE ((desc)+2) >> 2)
+#define DESC_TARGET_BACKGROUND_GRID_vertical_size(desc) ((GST_READ_UINT32_BE ((desc)+2) & 0x0003fff0) >> 4)
+#define DESC_TARGET_BACKGROUND_GRID_aspect_ratio_information(desc) ((desc)[5] & 0x0f)
+
+/* video_window_descriptor */
+#define DESC_VIDEO_WINDOW_horizontal_offset(desc) (GST_READ_UINT16_BE ((desc)+2) >> 2)
+#define DESC_VIDEO_WINDOW_vertical_offset(desc) ((GST_READ_UINT32_BE ((desc)+2) & 0x0003fff0) >> 4)
+#define DESC_VIDEO_WINDOW_window_priority(desc) ((desc)[5] & 0x0f)
+
+/* CA_descriptor */
+#define DESC_CA_system_ID(desc) (GST_READ_UINT16_BE ((desc)+2))
+#define DESC_CA_PID(desc) (GST_READ_UINT16_BE ((desc)+2) & 0x1fff)
+
+/* ISO_639_language_descriptor */
+#define DESC_ISO_639_LANGUAGE_codes_n(desc) ((desc[1]) >> 2)
+#define DESC_ISO_639_LANGUAGE_language_code_nth(desc,i) (&(desc[2 + (4*i)]))
+#define DESC_ISO_639_LANGUAGE_audio_type_nth(desc,i) ((desc)[5 + (4*i)])
+
+/* system_clock_descriptor */
+#define DESC_SYSTEM_CLOCK_external_clock_reference_indicator(desc) (((desc)[2] & 0x80) == 0x80)
+#define DESC_SYSTEM_CLOCK_clock_accuracy_integer(desc) ((desc)[2] & 0x3f)
+#define DESC_SYSTEM_CLOCK_clock_accuracy_exponent(desc) (((desc)[3] & 0xe0) >> 5)
+
+/* multiplex_buffer_utilization_descriptor */
+#define DESC_MULTIPLEX_BUFFER_UTILISATION_bound_valid_flag(desc) (((desc)[2] & 0x80) == 0x80)
+#define DESC_MULTIPLEX_BUFFER_UTILISATION_LTW_offset_lower_bound(desc) (GST_READ_UINT16_BE ((desc)+2) & 0x7fff)
+#define DESC_MULTIPLEX_BUFFER_UTILISATION_LTW_offset_upper_bound(desc) (GST_READ_UINT16_BE ((desc)+4) & 0x7fff)
+
+/* copyright_descriptor */
+#define DESC_COPYRIGHT_copyright_identifier(desc) (GST_READ_UINT32_BE ((desc)+2))
+#define DESC_COPYRIGHT_additional_copyright_info_len(desc) ((desc)[1] - 4)
+#define DESC_COPYRIGHT_additional_copyright_info(desc) (&(desc)[6])
+
+/* maximum_bitrate_descriptor */
+#define DESC_MAXIMUM_BITRAT_maximum_bitrate(desc) (((((guint32)desc[2]) & 0x3f) << 16) | \
+ GST_READ_UINT16_BE ((desc)+3))
+
+/* private_data_indicator_descriptor */
+#define DESC_PRIVATE_DATA_INDICATOR_indicator(desc) (GST_READ_UINT32_BE(&desc[2]))
+
+/* smoothing_buffer_descriptor */
+#define DESC_SMOOTHING_BUFFER_sb_leak_rate(desc) (((((guint32)desc[2]) & 0x3f) << 16) | \
+ GST_READ_UINT16_BE ((desc)+3))
+#define DESC_SMOOTHING_BUFFER_sb_size(desc) (((((guint32)desc[5]) & 0x3f) << 16) | \
+ GST_READ_UINT16_BE ((desc)+6))
+/* STD_descriptor */
+#define DESC_STD_leak_valid_flag(desc) (((desc)[2] & 0x01) == 0x01)
+
+/* ibp_descriptor */
+#define DESC_IBP_closed_gop_flag(desc) (((desc)[2] & 0x80) == 0x80)
+#define DESC_IBP_identical_gop_flag(desc) (((desc)[2] & 0x40) == 0x40)
+#define DESC_IBP_max_gop_length(desc) (GST_READ_UINT16_BE ((desc)+6) & 0x3fff)
+
+/* time_code descriptor */
+#define DESC_TIMECODE_video_pid(desc) (GST_READ_UINT16_BE ((desc) + 2) & 0x1fff)
+
+/* Stream identifier descriptor */
+#define DESC_DVB_STREAM_IDENTIFIER_component_tag(desc) (desc[2])
+
+/* DVB Network Name descriptor */
+#define DESC_DVB_NETWORK_NAME_length(desc) (GST_READ_UINT8((desc)+1))
+#define DESC_DVB_NETWORK_NAME_text(desc) (desc+2)
+
+typedef struct {
+ guint n_desc;
+ guint8 data_length;
+ guint8 *data;
+} GstMPEGDescriptor;
+
+GstMPEGDescriptor* gst_mpeg_descriptor_parse (guint8 *data, guint size);
+void gst_mpeg_descriptor_free (GstMPEGDescriptor *desc);
+
+guint gst_mpeg_descriptor_n_desc (GstMPEGDescriptor *desc);
+guint8* gst_mpeg_descriptor_find (GstMPEGDescriptor *desc, gint tag);
+guint8* gst_mpeg_descriptor_nth (GstMPEGDescriptor *desc, guint i);
+
+#endif /* __GST_MPEG_DESC_H__ */
diff --git a/ext/resindvd/gstpesfilter.c b/ext/resindvd/gstpesfilter.c
new file mode 100644
index 00000000..f0a8cb54
--- /dev/null
+++ b/ext/resindvd/gstpesfilter.c
@@ -0,0 +1,623 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is Fluendo MPEG Demuxer plugin.
+ *
+ * The Initial Developer of the Original Code is Fluendo, S.L.
+ * Portions created by Fluendo, S.L. are Copyright (C) 2005
+ * Fluendo, S.L. All Rights Reserved.
+ *
+ * Contributor(s): Wim Taymans <wim@fluendo.com>
+ * Jan Schmidt <thaytan@noraisin.net>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstmpegdefs.h"
+#include "gstpesfilter.h"
+
+GST_DEBUG_CATEGORY (gstflupesfilter_debug);
+#define GST_CAT_DEFAULT (gstflupesfilter_debug)
+
+static GstFlowReturn gst_pes_filter_data_push (GstPESFilter * filter,
+ gboolean first, GstBuffer * buffer);
+
+#define ADAPTER_OFFSET_FLUSH(_bytes_) if (filter->adapter_offset) *filter->adapter_offset = *filter->adapter_offset + (_bytes_)
+
+/* May pass null for adapter to have the filter create one */
+void
+gst_pes_filter_init (GstPESFilter * filter, GstAdapter * adapter,
+ guint64 * adapter_offset)
+{
+ g_return_if_fail (filter != NULL);
+
+ if (adapter != NULL)
+ g_object_ref (adapter);
+ else
+ adapter = gst_adapter_new ();
+
+ filter->adapter = adapter;
+ filter->adapter_offset = adapter_offset;
+ filter->state = STATE_HEADER_PARSE;
+ filter->gather_pes = FALSE;
+ filter->allow_unbounded = FALSE;
+}
+
+void
+gst_pes_filter_uninit (GstPESFilter * filter)
+{
+ g_return_if_fail (filter != NULL);
+
+ if (filter->adapter)
+ g_object_unref (filter->adapter);
+ filter->adapter = NULL;
+ filter->adapter_offset = NULL;
+}
+
+void
+gst_pes_filter_set_callbacks (GstPESFilter * filter,
+ GstPESFilterData data_cb, GstPESFilterResync resync_cb, gpointer user_data)
+{
+ g_return_if_fail (filter != NULL);
+
+ filter->data_cb = data_cb;
+ filter->resync_cb = resync_cb;
+ filter->user_data = user_data;
+}
+
+/* sync:4 == 00xx ! pts:3 ! 1 ! pts:15 ! 1 | pts:15 ! 1 */
+#define READ_TS(data, target, lost_sync_label) \
+ if ((*data & 0x01) != 0x01) goto lost_sync_label; \
+ target = ((guint64) (*data++ & 0x0E)) << 29; \
+ target |= ((guint64) (*data++ )) << 22; \
+ if ((*data & 0x01) != 0x01) goto lost_sync_label; \
+ target |= ((guint64) (*data++ & 0xFE)) << 14; \
+ target |= ((guint64) (*data++ )) << 7; \
+ if ((*data & 0x01) != 0x01) goto lost_sync_label; \
+ target |= ((guint64) (*data++ & 0xFE)) >> 1;
+
+static gboolean
+gst_pes_filter_is_sync (guint32 sync)
+{
+ return ((sync & 0xfffffffc) == 0x000001bc) ||
+ ((sync & 0xffffffe0) == 0x000001c0) ||
+ ((sync & 0xfffffff0) == 0x000001f0) ||
+ ((sync & 0xfffffff0) == 0x000001e0);
+}
+
+static GstFlowReturn
+gst_pes_filter_parse (GstPESFilter * filter)
+{
+ GstFlowReturn ret;
+ guint32 start_code;
+
+ gboolean STD_buffer_bound_scale;
+ guint16 STD_buffer_size_bound;
+ const guint8 *data;
+ gint avail, datalen;
+ gboolean have_size = FALSE;
+
+ /* read start code and length */
+ if (!(data = gst_adapter_peek (filter->adapter, 6)))
+ goto need_more_data;
+
+ /* get start code */
+ start_code = GST_READ_UINT32_BE (data);
+ if (!gst_pes_filter_is_sync (start_code))
+ goto lost_sync;
+
+ filter->start_code = start_code;
+ filter->id = data[3];
+
+ /* skip start code */
+ data += 4;
+
+ /* start parsing length */
+ filter->length = GST_READ_UINT16_BE (data);
+
+ /* see how much is available */
+ avail = gst_adapter_available (filter->adapter);
+
+ GST_DEBUG ("id 0x%02x length %d, avail %d start code 0x%02x", filter->id,
+ filter->length, avail, filter->start_code);
+
+ /* A data length of 0 indicates an unbounded packet in transport
+ * streams, but actually a 0 sized packet in program streams or
+ * for anything except video packets */
+
+ /* FIXME: Remove this hack that is checking start_code. Instead, we need
+ * a callback that a start_code has been collected, giving the caller a chance
+ * to set the allow_unbounded flag if they want */
+ if (filter->length == 0 &&
+ ((filter->start_code & 0xFFFFFFF0) == PACKET_VIDEO_START_CODE ||
+ filter->allow_unbounded)) {
+ GST_DEBUG ("id 0x%02x, unbounded length", filter->id);
+ filter->unbounded_packet = TRUE;
+ } else {
+ filter->unbounded_packet = FALSE;
+
+ if (filter->gather_pes && avail < filter->length + 6) {
+ GST_DEBUG ("id 0x%02x, bounded length %d, only have %d",
+ filter->id, filter->length + 6, avail);
+ goto need_more_data;
+ }
+
+ /* if we need more data from now on, we lost sync */
+ avail = MIN (avail, filter->length + 6);
+ }
+
+ /* read more data, either the whole packet if there is a length
+ * or whatever we have available if this in an unbounded packet. */
+ if (!(data = gst_adapter_peek (filter->adapter, avail)))
+ goto need_more_data;
+
+ /* This will make us flag LOST_SYNC if we run out of data from here onward */
+ have_size = TRUE;
+
+ /* skip start code and length */
+ data += 6;
+ datalen = avail - 6;
+
+ GST_DEBUG ("datalen %d", datalen);
+
+ switch (filter->start_code) {
+ case ID_PS_PROGRAM_STREAM_MAP:
+ case ID_PRIVATE_STREAM_2:
+ case ID_ECM_STREAM:
+ case ID_EMM_STREAM:
+ case ID_PROGRAM_STREAM_DIRECTORY:
+ case ID_DSMCC_STREAM:
+ case ID_ITU_TREC_H222_TYPE_E_STREAM:
+ goto skip;
+ case ID_PADDING_STREAM:
+ GST_DEBUG ("skipping padding stream");
+ goto skip;
+ default:
+ break;
+ }
+
+ if (datalen < 1)
+ goto need_more_data;
+
+ filter->pts = filter->dts = -1;
+
+ /* stuffing bits, first two bits are '10' for mpeg2 pes so this code is
+ * not triggered. */
+ while (TRUE) {
+ if (*data != 0xff)
+ break;
+
+ data++;
+ datalen--;
+
+ GST_DEBUG ("got stuffing bit");
+
+ if (datalen < 1)
+ goto need_more_data;
+ }
+
+ /* STD buffer size, never for mpeg2 */
+ if ((*data & 0xc0) == 0x40) {
+ GST_DEBUG ("have STD");
+
+ if (datalen < 3)
+ goto need_more_data;
+
+ STD_buffer_bound_scale = *data & 0x20;
+ STD_buffer_size_bound = ((guint16) (*data++ & 0x1F)) << 8;
+ STD_buffer_size_bound |= *data++;
+
+ datalen -= 2;
+ }
+
+ /* PTS but no DTS, never for mpeg2 */
+ if ((*data & 0xf0) == 0x20) {
+ GST_DEBUG ("PTS without DTS");
+
+ if (datalen < 5)
+ goto need_more_data;
+ READ_TS (data, filter->pts, lost_sync);
+ GST_DEBUG ("PTS found %" G_GUINT64_FORMAT, filter->pts);
+ datalen -= 5;
+ }
+ /* PTS and DTS, never for mpeg2 */
+ else if ((*data & 0xf0) == 0x30) {
+ GST_DEBUG ("PTS and DTS");
+
+ if (datalen < 10)
+ goto need_more_data;
+ READ_TS (data, filter->pts, lost_sync);
+ READ_TS (data, filter->dts, lost_sync);
+ GST_DEBUG ("PTS found %" G_GUINT64_FORMAT, filter->pts);
+ GST_DEBUG ("DTS found %" G_GUINT64_FORMAT, filter->dts);
+ datalen -= 10;
+ } else if ((*data & 0xc0) == 0x80) {
+ /* mpeg2 case */
+ guchar flags;
+ guint8 header_data_length = 0;
+
+ GST_DEBUG ("MPEG2 PES packet");
+
+ if (datalen < 3)
+ goto need_more_data;
+ /* 2: '10'
+ * 2: PES_scrambling_control
+ * 1: PES_priority
+ * 1: data_alignment_indicator
+ * 1: copyright
+ * 1: original_or_copy
+ */
+ flags = *data++;
+
+ GST_DEBUG ("flags: 0x%02x", flags);
+ if ((flags & 0xc0) != 0x80)
+ goto lost_sync;
+
+ /* check PES scrambling control */
+ if ((flags & 0x30) != 0)
+ goto encrypted;
+
+ /* 2: PTS_DTS_flags
+ * 1: ESCR_flag
+ * 1: ES_rate_flag
+ * 1: DSM_trick_mode_flag
+ * 1: additional_copy_info_flag
+ * 1: PES_CRC_flag
+ * 1: PES_extension_flag
+ */
+ flags = *data++;
+
+ /* 8: PES_header_data_length */
+ header_data_length = *data++;
+ datalen -= 3;
+
+ GST_DEBUG ("header_data_length: %d, flags 0x%02x",
+ header_data_length, flags);
+
+ if (header_data_length > datalen)
+ goto need_more_data;
+
+ /* only DTS: this is invalid */
+ if ((flags & 0xc0) == 0x40)
+ goto lost_sync;
+
+ /* check for PTS */
+ if ((flags & 0x80)) {
+ if (datalen < 5)
+ goto need_more_data;
+
+ READ_TS (data, filter->pts, lost_sync);
+ GST_DEBUG ("PTS found %" G_GUINT64_FORMAT, filter->pts);
+ header_data_length -= 5;
+ datalen -= 5;
+ }
+ /* check for DTS */
+ if ((flags & 0x40)) {
+ READ_TS (data, filter->dts, lost_sync);
+ if (datalen < 5)
+ goto need_more_data;
+ GST_DEBUG ("DTS found %" G_GUINT64_FORMAT, filter->dts);
+ header_data_length -= 5;
+ datalen -= 5;
+ }
+ /* ESCR_flag */
+ if ((flags & 0x20)) {
+ GST_DEBUG ("%x ESCR found", filter->id);
+ if (datalen < 6)
+ goto need_more_data;
+ data += 6;
+ header_data_length -= 6;
+ datalen -= 6;
+ }
+ /* ES_rate_flag */
+ if ((flags & 0x10)) {
+ guint32 es_rate;
+
+ if (datalen < 3)
+ goto need_more_data;
+
+ es_rate = ((guint32) (*data++ & 0x07)) << 14;
+ es_rate |= ((guint32) (*data++)) << 7;
+ es_rate |= ((guint32) (*data++ & 0xFE)) >> 1;
+ GST_DEBUG ("%x ES Rate found %u", filter->id, es_rate);
+ header_data_length -= 3;
+ datalen -= 3;
+ }
+ /* DSM_trick_mode_flag */
+ if ((flags & 0x08)) {
+ guint8 trick_mode_flags;
+
+ if (datalen < 1)
+ goto need_more_data;
+
+ /* 3: trick_mode_control */
+ trick_mode_flags = *data++;
+ GST_DEBUG ("%x DSM trick mode found, flags 0x%02x", filter->id,
+ trick_mode_flags);
+
+ /* fast_forward */
+ if ((trick_mode_flags & 0xe0) == 0x00) {
+ }
+ /* slow motion */
+ else if ((trick_mode_flags & 0xe0) == 0x20) {
+ }
+ /* freeze frame */
+ else if ((trick_mode_flags & 0xe0) == 0x40) {
+ }
+ /* fast reverse */
+ else if ((trick_mode_flags & 0xe0) == 0x60) {
+ }
+ /* slow reverse */
+ else if ((trick_mode_flags & 0xe0) == 0x80) {
+ }
+ /* reserved */
+ else {
+ }
+
+ header_data_length -= 1;
+ datalen -= 1;
+ }
+ /* additional_copy_info_flag */
+ if ((flags & 0x04)) {
+ GST_DEBUG ("%x additional copy info, flags 0x%02x", filter->id, *data);
+ }
+ /* PES_CRC_flag */
+ if ((flags & 0x02)) {
+ GST_DEBUG ("%x PES_CRC", filter->id);
+ }
+ /* PES_extension_flag */
+ if ((flags & 0x01)) {
+ GST_DEBUG ("%x PES_extension", filter->id);
+ }
+
+ /* calculate the amount of real data in this PES packet */
+ data += header_data_length;
+ datalen -= header_data_length;
+ } else if (*data == 0x0f) {
+ /* Not sure what this clause is for */
+ data++;
+ datalen--;
+ } else {
+ /* Data byte wasn't recognised as a flags byte */
+ GST_DEBUG ("Unrecognised flags byte 0x%02x\n", *data);
+ goto lost_sync;
+ }
+
+ {
+ GstBuffer *out;
+ guint16 consumed;
+
+ consumed = avail - 6 - datalen;
+
+ if (filter->unbounded_packet == FALSE) {
+ filter->length -= avail - 6;
+ GST_DEBUG ("pushing %d, need %d more, consumed %d",
+ datalen, filter->length, consumed);
+ } else {
+ GST_DEBUG ("pushing %d, unbounded packet, consumed %d",
+ datalen, consumed);
+ }
+
+ if (datalen > 0) {
+ out = gst_buffer_new ();
+ GST_BUFFER_DATA (out) = g_memdup (data, datalen);
+ GST_BUFFER_SIZE (out) = datalen;
+ GST_BUFFER_MALLOCDATA (out) = GST_BUFFER_DATA (out);
+
+ ret = gst_pes_filter_data_push (filter, TRUE, out);
+ filter->first = FALSE;
+ } else {
+ GST_LOG ("first being set to TRUE");
+ filter->first = TRUE;
+ ret = GST_FLOW_OK;
+ }
+
+ if (filter->length > 0 || filter->unbounded_packet)
+ filter->state = STATE_DATA_PUSH;
+ }
+
+ gst_adapter_flush (filter->adapter, avail);
+ ADAPTER_OFFSET_FLUSH (avail);
+
+ return ret;
+
+need_more_data:
+ {
+ if (filter->unbounded_packet == FALSE) {
+ if (have_size == TRUE) {
+ GST_DEBUG ("bounded need more data %d, lost sync",
+ gst_adapter_available (filter->adapter));
+ ret = GST_FLOW_LOST_SYNC;
+ } else {
+ GST_DEBUG ("bounded need more data %d, breaking for more",
+ gst_adapter_available (filter->adapter));
+ ret = GST_FLOW_NEED_MORE_DATA;
+ }
+ } else {
+ GST_DEBUG ("unbounded need more data %d",
+ gst_adapter_available (filter->adapter));
+ ret = GST_FLOW_NEED_MORE_DATA;
+ }
+
+ return ret;
+ }
+skip:
+ {
+ GST_DEBUG ("skipping 0x%02x", filter->id);
+ gst_adapter_flush (filter->adapter, avail);
+ ADAPTER_OFFSET_FLUSH (avail);
+
+ filter->length -= avail - 6;
+ if (filter->length > 0 || filter->unbounded_packet)
+ filter->state = STATE_DATA_SKIP;
+ return GST_FLOW_OK;
+ }
+encrypted:
+ {
+ GST_DEBUG ("skipping encrypted 0x%02x", filter->id);
+ gst_adapter_flush (filter->adapter, avail);
+ ADAPTER_OFFSET_FLUSH (avail);
+
+ filter->length -= avail - 6;
+ if (filter->length > 0 || filter->unbounded_packet)
+ filter->state = STATE_DATA_SKIP;
+ return GST_FLOW_OK;
+ }
+lost_sync:
+ {
+ GST_DEBUG ("lost sync");
+ gst_adapter_flush (filter->adapter, 4);
+ ADAPTER_OFFSET_FLUSH (4);
+
+ return GST_FLOW_LOST_SYNC;
+ }
+}
+
+static GstFlowReturn
+gst_pes_filter_data_push (GstPESFilter * filter, gboolean first,
+ GstBuffer * buffer)
+{
+ GstFlowReturn ret;
+
+ GST_LOG ("pushing, first: %d", first);
+
+ if (filter->data_cb) {
+ ret = filter->data_cb (filter, first, buffer, filter->user_data);
+ } else {
+ gst_buffer_unref (buffer);
+ ret = GST_FLOW_OK;
+ }
+ return ret;
+}
+
+GstFlowReturn
+gst_pes_filter_push (GstPESFilter * filter, GstBuffer * buffer)
+{
+ GstFlowReturn ret;
+
+ g_return_val_if_fail (filter != NULL, GST_FLOW_ERROR);
+ g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
+
+ switch (filter->state) {
+ case STATE_HEADER_PARSE:
+ gst_adapter_push (filter->adapter, buffer);
+ ret = gst_pes_filter_parse (filter);
+ break;
+ case STATE_DATA_PUSH:
+ ret = gst_pes_filter_data_push (filter, filter->first, buffer);
+ filter->first = FALSE;
+ break;
+ case STATE_DATA_SKIP:
+ gst_buffer_unref (buffer);
+ ret = GST_FLOW_OK;
+ break;
+ default:
+ goto wrong_state;
+ }
+ return ret;
+
+ /* ERROR */
+wrong_state:
+ {
+ GST_DEBUG ("wrong internal state %d", filter->state);
+ return GST_FLOW_ERROR;
+ }
+}
+
+GstFlowReturn
+gst_pes_filter_process (GstPESFilter * filter)
+{
+ GstFlowReturn ret;
+ gboolean skip = FALSE;
+
+ g_return_val_if_fail (filter != NULL, GST_FLOW_ERROR);
+
+ switch (filter->state) {
+ case STATE_HEADER_PARSE:
+ ret = gst_pes_filter_parse (filter);
+ break;
+ case STATE_DATA_SKIP:
+ skip = TRUE;
+ /* fallthrough */
+ case STATE_DATA_PUSH:
+ if (filter->length > 0 || filter->unbounded_packet) {
+ gint avail;
+
+ avail = gst_adapter_available (filter->adapter);
+ if (filter->unbounded_packet == FALSE)
+ avail = MIN (avail, filter->length);
+
+ if (skip) {
+ gst_adapter_flush (filter->adapter, avail);
+ ADAPTER_OFFSET_FLUSH (avail);
+ ret = GST_FLOW_OK;
+ } else {
+ GstBuffer *out;
+ guint8 *data;
+
+ data = gst_adapter_take (filter->adapter, avail);
+
+ out = gst_buffer_new ();
+ GST_BUFFER_DATA (out) = data;
+ GST_BUFFER_SIZE (out) = avail;
+ GST_BUFFER_MALLOCDATA (out) = data;
+
+ ret = gst_pes_filter_data_push (filter, filter->first, out);
+ filter->first = FALSE;
+ }
+
+ if (filter->unbounded_packet == FALSE) {
+ filter->length -= avail;
+ if (filter->length == 0)
+ filter->state = STATE_HEADER_PARSE;
+ }
+ } else {
+ filter->state = STATE_HEADER_PARSE;
+ ret = GST_FLOW_OK;
+ }
+ break;
+ default:
+ goto wrong_state;
+ }
+ return ret;
+
+ /* ERROR */
+wrong_state:
+ {
+ GST_DEBUG ("wrong internal state %d", filter->state);
+ return GST_FLOW_ERROR;
+ }
+}
+
+void
+gst_pes_filter_flush (GstPESFilter * filter)
+{
+ g_return_if_fail (filter != NULL);
+
+ if (filter->adapter) {
+ gst_adapter_clear (filter->adapter);
+ if (filter->adapter_offset)
+ *filter->adapter_offset = G_MAXUINT64;
+ }
+ filter->state = STATE_HEADER_PARSE;
+}
+
+GstFlowReturn
+gst_pes_filter_drain (GstPESFilter * filter)
+{
+ g_return_val_if_fail (filter != NULL, GST_FLOW_ERROR);
+
+ gst_pes_filter_flush (filter);
+
+ return GST_FLOW_OK;
+}
diff --git a/ext/resindvd/gstpesfilter.h b/ext/resindvd/gstpesfilter.h
new file mode 100644
index 00000000..27c618c2
--- /dev/null
+++ b/ext/resindvd/gstpesfilter.h
@@ -0,0 +1,88 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is Fluendo MPEG Demuxer plugin.
+ *
+ * The Initial Developer of the Original Code is Fluendo, S.L.
+ * Portions created by Fluendo, S.L. are Copyright (C) 2005
+ * Fluendo, S.L. All Rights Reserved.
+ *
+ * Contributor(s): Wim Taymans <wim@fluendo.com>
+ * Jan Schmidt <thaytan@noraisin.net>
+ */
+
+#ifndef __GST_PES_FILTER_H__
+#define __GST_PES_FILTER_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstPESFilter GstPESFilter;
+
+typedef GstFlowReturn (*GstPESFilterData) (GstPESFilter * filter, gboolean first, GstBuffer * buffer,
+ gpointer user_data);
+typedef void (*GstPESFilterResync) (GstPESFilter * filter, gpointer user_data);
+typedef void (*GstPESFilterIndex) (GstPESFilter * filter, gpointer user_data);
+
+typedef enum {
+ STATE_HEADER_PARSE,
+ STATE_DATA_PUSH,
+ STATE_DATA_SKIP
+} GstPESFilterState;
+
+struct _GstPESFilter {
+ GstAdapter * adapter;
+ guint64 * adapter_offset;
+
+ GstPESFilterState state;
+ /* Whether to collect entire PES packets before
+ * outputting */
+ gboolean gather_pes;
+ /* Whether unbounded packets are allowed in this
+ * stream */
+ gboolean allow_unbounded;
+
+ gboolean first;
+ GstPESFilterData data_cb;
+ GstPESFilterResync resync_cb;
+ gpointer user_data;
+
+ guint32 start_code;
+ guint8 id;
+ gboolean unbounded_packet;
+ guint16 length;
+
+ guint8 type;
+
+ gint64 pts;
+ gint64 dts;
+};
+
+void gst_pes_filter_init (GstPESFilter * filter, GstAdapter * adapter, guint64 * adapter_offset);
+
+void gst_pes_filter_uninit (GstPESFilter * filter);
+
+void gst_pes_filter_set_callbacks (GstPESFilter * filter,
+ GstPESFilterData data_cb,
+ GstPESFilterResync resync_cb,
+ gpointer user_data);
+
+GstFlowReturn gst_pes_filter_push (GstPESFilter * filter, GstBuffer * buffer);
+GstFlowReturn gst_pes_filter_process (GstPESFilter * filter);
+
+void gst_pes_filter_flush (GstPESFilter * filter);
+GstFlowReturn gst_pes_filter_drain (GstPESFilter * filter);
+
+G_END_DECLS
+
+#endif /* __GST_PES_FILTER_H__ */
diff --git a/ext/resindvd/plugin.c b/ext/resindvd/plugin.c
new file mode 100644
index 00000000..bc0a9640
--- /dev/null
+++ b/ext/resindvd/plugin.c
@@ -0,0 +1,53 @@
+/* GStreamer
+ * Copyright (C) 2008 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * 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 <gst/gst.h>
+
+#include "resindvdbin.h"
+
+#include "gstmpegdemux.h"
+
+GST_DEBUG_CATEGORY (resindvd_debug);
+#define GST_CAT_DEFAULT resindvd_debug
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ gboolean result = TRUE;
+
+ GST_DEBUG_CATEGORY_INIT (resindvd_debug, "resindvd elements",
+ 0, "DVD playback elements from resindvd");
+
+ result &= gst_element_register (plugin, "rsndvdbin",
+ GST_RANK_PRIMARY, RESIN_TYPE_DVDBIN);
+
+ result &= gst_flups_demux_plugin_init (plugin);
+
+ return result;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "resindvd",
+ "Resin DVD playback elements",
+ plugin_init, VERSION, "GPL", "GStreamer", "http://gstreamer.net/")
diff --git a/ext/resindvd/resin-play b/ext/resindvd/resin-play
new file mode 100755
index 00000000..c2368116
--- /dev/null
+++ b/ext/resindvd/resin-play
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+if [ $# -ge 1 ]; then
+ DEVICE_OPT="device=$1"
+else
+ DEVICE_OPT=""
+fi
+
+gst-launch rsndvdbin name=dvd "$DEVICE_OPT" \
+ dvd. ! ffmpegcolorspace ! videoscale ! autovideosink \
+ dvd. ! audioconvert ! autoaudiosink
diff --git a/ext/resindvd/resindvdbin.c b/ext/resindvd/resindvdbin.c
new file mode 100644
index 00000000..9fe091f2
--- /dev/null
+++ b/ext/resindvd/resindvdbin.c
@@ -0,0 +1,758 @@
+/* GStreamer
+ * Copyright (C) 2008 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * 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 <string.h>
+
+#include <gst/gst.h>
+
+#include "resindvdbin.h"
+#include "resindvdsrc.h"
+#include "rsnstreamselector.h"
+#include "rsnaudiomunge.h"
+
+#include "gstmpegdemux.h"
+
+GST_DEBUG_CATEGORY_EXTERN (resindvd_debug);
+#define GST_CAT_DEFAULT resindvd_debug
+
+#define DECODEBIN_AUDIO 0
+
+#define DVDBIN_LOCK(d) g_mutex_lock((d)->dvd_lock)
+#define DVDBIN_UNLOCK(d) g_mutex_unlock((d)->dvd_lock)
+
+#define DEFAULT_DEVICE "/dev/dvd"
+enum
+{
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+enum
+{
+ ARG_0,
+ ARG_DEVICE
+};
+
+/* FIXME: Could list specific video and audio caps: */
+static GstStaticPadTemplate video_src_template =
+GST_STATIC_PAD_TEMPLATE ("video",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate audio_src_template =
+GST_STATIC_PAD_TEMPLATE ("audio",
+ GST_PAD_SRC,
+ GST_PAD_SOMETIMES,
+ GST_STATIC_CAPS_ANY);
+
+static void rsn_dvdbin_do_init (GType rsn_dvdbin_type);
+static void rsn_dvdbin_finalize (GObject * object);
+static void rsn_dvdbin_uri_handler_init (gpointer g_iface, gpointer iface_data);
+
+GST_BOILERPLATE_FULL (RsnDvdBin, rsn_dvdbin, GstBin,
+ GST_TYPE_BIN, rsn_dvdbin_do_init);
+
+static void demux_pad_added (GstElement * element, GstPad * pad,
+ RsnDvdBin * dvdbin);
+static void viddec_pad_added (GstElement * element, GstPad * pad,
+ gboolean last, RsnDvdBin * dvdbin);
+#if DECODEBIN_AUDIO
+static void auddec_pad_added (GstElement * element, GstPad * pad,
+ gboolean last, RsnDvdBin * dvdbin);
+#endif
+static void rsn_dvdbin_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void rsn_dvdbin_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static GstStateChangeReturn rsn_dvdbin_change_state (GstElement * element,
+ GstStateChange transition);
+
+static void
+rsn_dvdbin_base_init (gpointer gclass)
+{
+ static GstElementDetails element_details = {
+ "rsndvdbin",
+ "Generic/Bin/Player",
+ "DVD playback element",
+ "Jan Schmidt <thaytan@noraisin.net>"
+ };
+ GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&video_src_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&audio_src_template));
+ gst_element_class_set_details (element_class, &element_details);
+
+ element_class->change_state = GST_DEBUG_FUNCPTR (rsn_dvdbin_change_state);
+}
+
+static void
+rsn_dvdbin_class_init (RsnDvdBinClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ gobject_class->finalize = rsn_dvdbin_finalize;
+ gobject_class->set_property = rsn_dvdbin_set_property;
+ gobject_class->get_property = rsn_dvdbin_get_property;
+
+ g_object_class_install_property (gobject_class, ARG_DEVICE,
+ g_param_spec_string ("device", "Device", "DVD device location",
+ NULL, G_PARAM_READWRITE));
+}
+
+static void
+rsn_dvdbin_do_init (GType rsn_dvdbin_type)
+{
+ static const GInterfaceInfo urihandler_info = {
+ rsn_dvdbin_uri_handler_init,
+ NULL,
+ NULL
+ };
+
+ g_type_add_interface_static (rsn_dvdbin_type, GST_TYPE_URI_HANDLER,
+ &urihandler_info);
+}
+
+static void
+rsn_dvdbin_init (RsnDvdBin * dvdbin, RsnDvdBinClass * gclass)
+{
+ dvdbin->dvd_lock = g_mutex_new ();
+}
+
+static void
+rsn_dvdbin_finalize (GObject * object)
+{
+ RsnDvdBin *dvdbin = RESINDVDBIN (object);
+
+ g_mutex_free (dvdbin->dvd_lock);
+ g_free (dvdbin->last_uri);
+ g_free (dvdbin->device);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/* URI interface */
+static guint
+rsn_dvdbin_uri_get_type (void)
+{
+ return GST_URI_SRC;
+}
+
+static gchar **
+rsn_dvdbin_uri_get_protocols (void)
+{
+ static gchar *protocols[] = { "dvd", NULL };
+
+ return protocols;
+}
+
+static const gchar *
+rsn_dvdbin_uri_get_uri (GstURIHandler * handler)
+{
+ RsnDvdBin *dvdbin = RESINDVDBIN (handler);
+
+ DVDBIN_LOCK (dvdbin);
+ g_free (dvdbin->last_uri);
+ if (dvdbin->device)
+ dvdbin->last_uri = g_strdup_printf ("dvd://%s", dvdbin->device);
+ else
+ dvdbin->last_uri = g_strdup ("dvd://");
+ DVDBIN_UNLOCK (dvdbin);
+
+ return dvdbin->last_uri;
+}
+
+static gboolean
+rsn_dvdbin_uri_set_uri (GstURIHandler * handler, const gchar * uri)
+{
+ // RsnDvdBin *dvdbin = RESINDVDBIN (handler);
+ gboolean ret;
+ gchar *protocol, *location;
+
+ protocol = gst_uri_get_protocol (uri);
+
+ ret = (protocol && !strcmp (protocol, "dvd")) ? TRUE : FALSE;
+
+ g_free (protocol);
+ protocol = NULL;
+
+ if (!ret)
+ return ret;
+
+ location = gst_uri_get_location (uri);
+ if (!location)
+ return ret;
+ /*
+ * Parse out the device name
+ */
+#if 0
+ /*
+ * Parse out the new t/c/a and seek to them
+ */
+ {
+ gchar **strs;
+ gchar **strcur;
+ gint pos = 0;
+
+ location = gst_uri_get_location (uri);
+
+ if (!location)
+ return ret;
+
+ strcur = strs = g_strsplit (location, ",", 0);
+ while (strcur && *strcur) {
+ gint val;
+
+ if (!sscanf (*strcur, "%d", &val))
+ break;
+
+ switch (pos) {
+ case 0:
+ if (val != dvdbin->uri_title) {
+ dvdbin->uri_title = val;
+ dvdbin->new_seek = TRUE;
+ }
+ break;
+ case 1:
+ if (val != dvdbin->uri_chapter) {
+ dvdbin->uri_chapter = val;
+ dvdbin->new_seek = TRUE;
+ }
+ break;
+ case 2:
+ dvdbin->uri_angle = val;
+ break;
+ }
+
+ strcur++;
+ pos++;
+ }
+
+ g_strfreev (strs);
+ }
+#endif
+
+ g_free (location);
+
+ return ret;
+}
+
+static void
+rsn_dvdbin_uri_handler_init (gpointer g_iface, gpointer iface_data)
+{
+ GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
+
+ iface->get_type = rsn_dvdbin_uri_get_type;
+ iface->get_protocols = rsn_dvdbin_uri_get_protocols;
+ iface->get_uri = rsn_dvdbin_uri_get_uri;
+ iface->set_uri = rsn_dvdbin_uri_set_uri;
+}
+
+static gboolean
+try_create_piece (RsnDvdBin * dvdbin, gint index,
+ const gchar * factory, GType type, const gchar * name, const gchar * descr)
+{
+ GstElement *e;
+
+ DVDBIN_LOCK (dvdbin);
+ if (dvdbin->pieces[index] != NULL) {
+ DVDBIN_UNLOCK (dvdbin);
+ return TRUE; /* Already exists */
+ }
+ DVDBIN_UNLOCK (dvdbin);
+
+ if (factory != NULL) {
+ e = gst_element_factory_make (factory, name);
+ } else {
+ if (name)
+ e = g_object_new (type, "name", name, NULL);
+ else
+ e = g_object_new (type, NULL);
+ }
+ if (e == NULL)
+ goto create_failed;
+
+ if (!gst_bin_add (GST_BIN (dvdbin), e))
+ goto add_failed;
+
+ GST_DEBUG_OBJECT (dvdbin, "Added %s element: %" GST_PTR_FORMAT, descr, e);
+
+ DVDBIN_LOCK (dvdbin);
+ dvdbin->pieces[index] = e;
+ DVDBIN_UNLOCK (dvdbin);
+
+ return TRUE;
+create_failed:
+ GST_ELEMENT_ERROR (dvdbin, CORE, MISSING_PLUGIN, (NULL),
+ ("Could not create %s element '%s'", descr, factory));
+ return FALSE;
+add_failed:
+ gst_object_unref (e);
+ GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
+ ("Could not add %s element to bin", descr));
+ return FALSE;
+}
+
+static gboolean
+create_elements (RsnDvdBin * dvdbin)
+{
+ GstPad *src = NULL;
+ GstPad *sink = NULL;
+ GstPad *ghost = NULL;
+
+ if (!try_create_piece (dvdbin, DVD_ELEM_SOURCE, NULL,
+ RESIN_TYPE_DVDSRC, "dvdsrc", "DVD source")) {
+ return FALSE;
+ }
+
+ /* FIXME: Locking */
+ if (dvdbin->device) {
+ g_object_set (G_OBJECT (dvdbin->pieces[DVD_ELEM_SOURCE]),
+ "device", dvdbin->device, NULL);
+ }
+
+ if (!try_create_piece (dvdbin, DVD_ELEM_DEMUX,
+ NULL, GST_TYPE_FLUPS_DEMUX, "dvddemux", "DVD demuxer"))
+ return FALSE;
+
+ if (gst_element_link (dvdbin->pieces[DVD_ELEM_SOURCE],
+ dvdbin->pieces[DVD_ELEM_DEMUX]) == FALSE)
+ goto failed_connect;
+
+ /* Listen for new pads from the demuxer */
+ g_signal_connect (G_OBJECT (dvdbin->pieces[DVD_ELEM_DEMUX]), "pad-added",
+ G_CALLBACK (demux_pad_added), dvdbin);
+
+ if (!try_create_piece (dvdbin, DVD_ELEM_MQUEUE, "multiqueue", 0, "mq",
+ "multiqueue"))
+ return FALSE;
+
+ g_object_set (dvdbin->pieces[DVD_ELEM_MQUEUE],
+ "max-size-time", (7 * GST_SECOND / 10), "max-size-bytes", 0,
+ "max-size-buffers", 0, NULL);
+
+ /* Decodebin will throw a missing element message to find an MPEG decoder */
+ if (!try_create_piece (dvdbin, DVD_ELEM_VIDDEC, "decodebin", 0, "viddec",
+ "video decoder"))
+ return FALSE;
+
+ g_signal_connect (G_OBJECT (dvdbin->pieces[DVD_ELEM_VIDDEC]),
+ "new-decoded-pad", G_CALLBACK (viddec_pad_added), dvdbin);
+
+ if (!try_create_piece (dvdbin, DVD_ELEM_VIDQ, "queue", 0, "vid_q",
+ "video decoder buffer"))
+ return FALSE;
+
+ g_object_set (dvdbin->pieces[DVD_ELEM_VIDQ],
+ "max-size-time", G_GUINT64_CONSTANT (0), "max-size-bytes", 0,
+ "max-size-buffers", 3, NULL);
+
+ if (!try_create_piece (dvdbin, DVD_ELEM_SPU_SELECT, NULL,
+ RSN_TYPE_STREAM_SELECTOR, "subpselect", "Subpicture stream selector"))
+ return FALSE;
+
+ if (!try_create_piece (dvdbin, DVD_ELEM_SPU, "dvdspu", 0, "spu",
+ "Subpicture overlay"))
+ return FALSE;
+
+ sink = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_SPU], "video");
+ src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_VIDQ], "src");
+ if (src == NULL || sink == NULL)
+ goto failed_spu_connect;
+ if (GST_PAD_LINK_FAILED (gst_pad_link (src, sink)))
+ goto failed_spu_connect;
+ gst_object_unref (sink);
+ gst_object_unref (src);
+ src = sink = NULL;
+
+ sink =
+ gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_SPU], "subpicture");
+ src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_SPU_SELECT], "src");
+ if (src == NULL || sink == NULL)
+ goto failed_spu_connect;
+ if (GST_PAD_LINK_FAILED (gst_pad_link (src, sink)))
+ goto failed_spu_connect;
+ gst_object_unref (sink);
+ gst_object_unref (src);
+ src = sink = NULL;
+
+ src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_SPU], "src");
+ if (src == NULL)
+ goto failed_spu_ghost;
+ ghost = gst_ghost_pad_new ("video", src);
+ if (ghost == NULL)
+ goto failed_spu_ghost;
+ if (!gst_element_add_pad (GST_ELEMENT (dvdbin), ghost))
+ goto failed_spu_ghost;
+ gst_object_unref (src);
+ src = sink = NULL;
+
+ if (!try_create_piece (dvdbin, DVD_ELEM_AUD_SELECT, NULL,
+ RSN_TYPE_STREAM_SELECTOR, "audioselect", "Audio stream selector"))
+ return FALSE;
+
+ /* rsnaudiomunge goes after the audio decoding to regulate the stream */
+ if (!try_create_piece (dvdbin, DVD_ELEM_AUD_MUNGE, NULL,
+ RSN_TYPE_AUDIOMUNGE, "audiomunge", "Audio output filter"))
+ return FALSE;
+
+#if DECODEBIN_AUDIO
+ /* Decodebin will throw a missing element message to find a suitable
+ * decoder */
+ if (!try_create_piece (dvdbin, DVD_ELEM_AUDDEC, "decodebin", 0, "auddec",
+ "audio decoder"))
+ return FALSE;
+
+ g_signal_connect (G_OBJECT (dvdbin->pieces[DVD_ELEM_AUDDEC]),
+ "new-decoded-pad", G_CALLBACK (auddec_pad_added), dvdbin);
+#else
+ if (!try_create_piece (dvdbin, DVD_ELEM_AUDDEC, "a52dec", 0, "auddec",
+ "audio decoder"))
+ return FALSE;
+
+ src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_AUDDEC], "src");
+ sink =
+ gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_AUD_MUNGE], "sink");
+ if (src == NULL || sink == NULL)
+ goto failed_aud_connect;
+ if (GST_PAD_LINK_FAILED (gst_pad_link (src, sink)))
+ goto failed_aud_connect;
+ gst_object_unref (sink);
+ gst_object_unref (src);
+ src = sink = NULL;
+#endif
+
+ src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_AUD_SELECT], "src");
+ sink = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_AUDDEC], "sink");
+ if (src == NULL || sink == NULL)
+ goto failed_aud_connect;
+ if (GST_PAD_LINK_FAILED (gst_pad_link (src, sink)))
+ goto failed_aud_connect;
+ gst_object_unref (sink);
+ gst_object_unref (src);
+ src = sink = NULL;
+
+ /* ghost audio munge output pad onto bin */
+ src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_AUD_MUNGE], "src");
+ if (src == NULL)
+ goto failed_aud_ghost;
+ ghost = gst_ghost_pad_new ("audio", src);
+ if (ghost == NULL)
+ goto failed_aud_ghost;
+ if (!gst_element_add_pad (GST_ELEMENT (dvdbin), ghost))
+ goto failed_aud_ghost;
+ gst_object_unref (src);
+ src = sink = NULL;
+
+ return TRUE;
+
+failed_connect:
+ GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
+ ("Could not connect DVD source and demuxer elements"));
+ return FALSE;
+failed_spu_connect:
+ GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
+ ("Could not connect DVD video buffer and spu elements"));
+ if (src != NULL)
+ gst_object_unref (src);
+ if (sink != NULL)
+ gst_object_unref (sink);
+ return FALSE;
+failed_spu_ghost:
+ GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
+ ("Could not ghost SPU output pad"));
+ if (src != NULL)
+ gst_object_unref (src);
+ if (ghost != NULL)
+ gst_object_unref (ghost);
+ return FALSE;
+failed_aud_connect:
+ GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
+ ("Could not connect DVD audio decoder"));
+ if (src != NULL)
+ gst_object_unref (src);
+ if (sink != NULL)
+ gst_object_unref (sink);
+ return FALSE;
+failed_aud_ghost:
+ GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
+ ("Could not ghost audio output pad"));
+ if (ghost != NULL)
+ gst_object_unref (ghost);
+ return FALSE;
+}
+
+static void
+remove_elements (RsnDvdBin * dvdbin)
+{
+ gint i;
+ GList *tmp;
+
+ if (dvdbin->pieces[DVD_ELEM_MQUEUE] != NULL) {
+ for (tmp = dvdbin->mq_req_pads; tmp; tmp = g_list_next (tmp)) {
+ gst_element_release_request_pad (dvdbin->pieces[DVD_ELEM_MQUEUE],
+ GST_PAD (tmp->data));
+ }
+ }
+ g_list_free (dvdbin->mq_req_pads);
+ dvdbin->mq_req_pads = NULL;
+
+ for (i = 0; i < DVD_ELEM_LAST; i++) {
+ DVDBIN_LOCK (dvdbin);
+ if (dvdbin->pieces[i] != NULL) {
+ GstElement *piece = dvdbin->pieces[i];
+ dvdbin->pieces[i] = NULL;
+ DVDBIN_UNLOCK (dvdbin);
+
+ gst_element_set_state (piece, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (dvdbin), piece);
+ } else
+ DVDBIN_UNLOCK (dvdbin);
+ }
+}
+
+static GstPad *
+connect_thru_mq (RsnDvdBin * dvdbin, GstPad * pad)
+{
+ GstPad *mq_sink;
+ GstPad *mq_src;
+ gchar *tmp, *sinkname, *srcname;
+
+ /* Request a pad from multiqueue, then connect this one, then
+ * discover the corresponding output pad and return it */
+ mq_sink = gst_element_get_request_pad (dvdbin->pieces[DVD_ELEM_MQUEUE],
+ "sink%d");
+ if (mq_sink == NULL)
+ return FALSE;
+ dvdbin->mq_req_pads = g_list_prepend (dvdbin->mq_req_pads, mq_sink);
+
+ if (gst_pad_link (pad, mq_sink) != GST_PAD_LINK_OK)
+ return FALSE;
+
+ sinkname = gst_pad_get_name (mq_sink);
+ tmp = sinkname + 4;
+ srcname = g_strdup_printf ("src%s", tmp);
+
+ mq_src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_MQUEUE],
+ srcname);
+
+ g_free (sinkname);
+ g_free (srcname);
+
+ return mq_src;
+}
+
+static void
+demux_pad_added (GstElement * element, GstPad * pad, RsnDvdBin * dvdbin)
+{
+ gboolean skip_mq = FALSE;
+ GstPad *mq_pad = NULL;
+ GstPad *dest_pad = NULL;
+ GstCaps *caps;
+ GstStructure *s;
+
+ GST_ERROR_OBJECT (dvdbin, "New pad: %" GST_PTR_FORMAT, pad);
+
+ caps = gst_pad_get_caps (pad);
+ if (caps == NULL) {
+ GST_WARNING_OBJECT (dvdbin, "NULL caps from pad %" GST_PTR_FORMAT, pad);
+ return;
+ }
+ if (!gst_caps_is_fixed (caps)) {
+ GST_WARNING_OBJECT (dvdbin, "Unfixed caps %" GST_PTR_FORMAT
+ " on pad %" GST_PTR_FORMAT, caps, pad);
+ gst_caps_unref (caps);
+ return;
+ }
+
+ s = gst_caps_get_structure (caps, 0);
+ g_return_if_fail (s != NULL);
+
+ if (g_str_equal (gst_structure_get_name (s), "video/mpeg")) {
+ dest_pad =
+ gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_VIDDEC], "sink");
+ } else if (g_str_equal (gst_structure_get_name (s), "video/x-dvd-subpicture")) {
+ dest_pad =
+ gst_element_get_request_pad (dvdbin->pieces[DVD_ELEM_SPU_SELECT],
+ "sink%d");
+ skip_mq = TRUE;
+ } else if (g_str_equal (gst_structure_get_name (s), "audio/x-private1-ac3")) {
+ dest_pad =
+ gst_element_get_request_pad (dvdbin->pieces[DVD_ELEM_AUD_SELECT],
+ "sink%d");
+ }
+
+ gst_caps_unref (caps);
+
+ if (dest_pad == NULL) {
+ GST_DEBUG_OBJECT (dvdbin, "Don't know how to handle pad. Ignoring");
+ return;
+ }
+
+ if (skip_mq) {
+ mq_pad = gst_object_ref (pad);
+ } else {
+ mq_pad = connect_thru_mq (dvdbin, pad);
+ if (mq_pad == NULL)
+ goto failed;
+ GST_ERROR_OBJECT (dvdbin, "Linking new pad %" GST_PTR_FORMAT
+ " through multiqueue to %" GST_PTR_FORMAT, pad, dest_pad);
+ }
+
+ gst_pad_link (mq_pad, dest_pad);
+
+ gst_object_unref (mq_pad);
+ gst_object_unref (dest_pad);
+
+ return;
+failed:
+ GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL),
+ ("Failed to handle new demuxer pad %s", GST_PAD_NAME (pad)));
+ if (mq_pad)
+ gst_object_unref (mq_pad);
+ if (dest_pad)
+ gst_object_unref (dest_pad);
+ return;
+}
+
+static void
+viddec_pad_added (GstElement * element, GstPad * pad, gboolean last,
+ RsnDvdBin * dvdbin)
+{
+ GstPad *q_pad;
+
+ GST_ERROR_OBJECT (dvdbin, "New video pad: %" GST_PTR_FORMAT, pad);
+
+ q_pad = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_VIDQ], "sink");
+ gst_pad_link (pad, q_pad);
+
+ gst_object_unref (q_pad);
+}
+
+#if DECODEBIN_AUDIO
+static void
+auddec_pad_added (GstElement * element, GstPad * pad, gboolean last,
+ RsnDvdBin * dvdbin)
+{
+ GstPad *out_pad;
+
+ GST_ERROR_OBJECT (dvdbin, "New audio pad: %" GST_PTR_FORMAT, pad);
+
+ out_pad =
+ gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_AUD_MUNGE], "sink");
+ gst_pad_link (pad, out_pad);
+
+ gst_object_unref (out_pad);
+}
+#endif
+
+static void
+rsn_dvdbin_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ RsnDvdBin *dvdbin = RESINDVDBIN (object);
+
+ switch (prop_id) {
+ case ARG_DEVICE:
+ DVDBIN_LOCK (dvdbin);
+ g_free (dvdbin->device);
+ if (g_value_get_string (value) == NULL)
+ dvdbin->device = g_strdup (DEFAULT_DEVICE);
+ else
+ dvdbin->device = g_value_dup_string (value);
+
+ if (dvdbin->pieces[DVD_ELEM_SOURCE]) {
+ g_object_set_property (G_OBJECT (dvdbin->pieces[DVD_ELEM_SOURCE]),
+ "device", value);
+ }
+ DVDBIN_UNLOCK (dvdbin);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+rsn_dvdbin_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ RsnDvdBin *dvdbin = RESINDVDBIN (object);
+
+ switch (prop_id) {
+ case ARG_DEVICE:
+ DVDBIN_LOCK (dvdbin);
+ if (dvdbin->device)
+ g_value_set_string (value, dvdbin->device);
+ else if (dvdbin->pieces[DVD_ELEM_SOURCE])
+ g_object_get_property (G_OBJECT (dvdbin->pieces[DVD_ELEM_SOURCE]),
+ "device", value);
+ else
+ g_value_set_string (value, DEFAULT_DEVICE);
+ DVDBIN_UNLOCK (dvdbin);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GstStateChangeReturn
+rsn_dvdbin_change_state (GstElement * element, GstStateChange transition)
+{
+ GstStateChangeReturn ret;
+ RsnDvdBin *dvdbin = RESINDVDBIN (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ if (!create_elements (dvdbin)) {
+ remove_elements (dvdbin);
+ return GST_STATE_CHANGE_FAILURE;
+ }
+ gst_element_no_more_pads (GST_ELEMENT (dvdbin));
+ break;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ return ret;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ remove_elements (dvdbin);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
diff --git a/ext/resindvd/resindvdbin.h b/ext/resindvd/resindvdbin.h
new file mode 100644
index 00000000..87f4261e
--- /dev/null
+++ b/ext/resindvd/resindvdbin.h
@@ -0,0 +1,80 @@
+/* GStreamer
+ * Copyright (C) 2008 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * 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 __RESINDVDBIN_H__
+#define __RESINDVDBIN_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+/* #defines don't like whitespacey bits */
+#define RESIN_TYPE_DVDBIN \
+ (rsn_dvdbin_get_type())
+#define RESINDVDBIN(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),RESIN_TYPE_DVDBIN,RsnDvdBin))
+#define RESINDVDBIN_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),RESIN_TYPE_DVDBIN,RsnDvdBinClass))
+#define IS_RESINDVDBIN(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),RESIN_TYPE_DVDBIN))
+#define IS_RESINDVDBIN_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),RESIN_TYPE_DVDBIN))
+
+typedef struct _RsnDvdBin RsnDvdBin;
+typedef struct _RsnDvdBinClass RsnDvdBinClass;
+
+#define DVD_ELEM_SOURCE 0
+#define DVD_ELEM_DEMUX 1
+#define DVD_ELEM_MQUEUE 2
+#define DVD_ELEM_SPU 3
+#define DVD_ELEM_VIDDEC 4
+#define DVD_ELEM_AUDDEC 5
+#define DVD_ELEM_VIDQ 6
+#define DVD_ELEM_SPU_SELECT 7
+#define DVD_ELEM_AUD_SELECT 8
+#define DVD_ELEM_AUD_MUNGE 9
+#define DVD_ELEM_LAST 10
+
+struct _RsnDvdBin
+{
+ GstBin element;
+
+ /* Protects pieces list and properties */
+ GMutex *dvd_lock;
+
+ gchar *device;
+ gchar *last_uri;
+ GstElement *pieces[DVD_ELEM_LAST];
+
+ GstPad *video_pad;
+ GstPad *audio_pad;
+
+ GList *mq_req_pads;
+};
+
+struct _RsnDvdBinClass
+{
+ GstBinClass parent_class;
+};
+
+GType rsn_dvdbin_get_type (void);
+
+G_END_DECLS
+
+#endif /* __RESINDVDBIN_H__ */
diff --git a/ext/resindvd/resindvdsrc.c b/ext/resindvd/resindvdsrc.c
new file mode 100644
index 00000000..c98870c7
--- /dev/null
+++ b/ext/resindvd/resindvdsrc.c
@@ -0,0 +1,1426 @@
+/* GStreamer
+ * Copyright (C) 2008 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * 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 <string.h>
+
+#include <gst/gst.h>
+// #include <gst/gst-i18n-plugin.h>
+#define _(s) s /* FIXME - add i18n bits to build */
+
+#include "resindvdsrc.h"
+
+GST_DEBUG_CATEGORY_STATIC (rsndvdsrc_debug);
+#define GST_CAT_DEFAULT rsndvdsrc_debug
+
+#define DEFAULT_DEVICE "/dev/dvd"
+
+#define GST_FLOW_WOULD_BLOCK GST_FLOW_CUSTOM_SUCCESS
+
+#define CLOCK_BASE 9LL
+#define CLOCK_FREQ CLOCK_BASE * 10000
+
+#define MPEGTIME_TO_GSTTIME(time) (((time) * (GST_MSECOND/10)) / CLOCK_BASE)
+#define GSTTIME_TO_MPEGTIME(time) (((time) * CLOCK_BASE) / (GST_MSECOND/10))
+
+typedef enum
+{
+ RSN_NAV_RESULT_NONE,
+ RSN_NAV_RESULT_HIGHLIGHT,
+ RSN_NAV_RESULT_BRANCH
+} RsnNavResult;
+
+typedef enum
+{
+ RSN_NAV_ACTION_ACTIVATE,
+ RSN_NAV_ACTION_LEFT,
+ RSN_NAV_ACTION_RIGHT,
+ RSN_NAV_ACTION_DOWN,
+ RSN_NAV_ACTION_UP
+} RsnNavAction;
+
+enum
+{
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+enum
+{
+ ARG_0,
+ ARG_DEVICE
+};
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-resin-dvd")
+ );
+
+/* Private seek format for private flushing */
+static GstFormat rsndvd_format;
+
+static void rsn_dvdsrc_register_extra (GType rsn_dvdsrc_type);
+
+GST_BOILERPLATE_FULL (resinDvdSrc, rsn_dvdsrc, RsnPushSrc,
+ RSN_TYPE_PUSH_SRC, rsn_dvdsrc_register_extra);
+
+static gboolean read_vts_info (resinDvdSrc * src);
+
+static void rsn_dvdsrc_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void rsn_dvdsrc_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static void rsn_dvdsrc_finalize (GObject * object);
+
+static gboolean rsn_dvdsrc_start (RsnBaseSrc * bsrc);
+static gboolean rsn_dvdsrc_stop (RsnBaseSrc * bsrc);
+static gboolean rsn_dvdsrc_unlock (RsnBaseSrc * bsrc);
+static gboolean rsn_dvdsrc_unlock_stop (RsnBaseSrc * bsrc);
+
+static gboolean rsn_dvdsrc_prepare_seek (RsnBaseSrc * bsrc, GstEvent * event,
+ GstSegment * segment);
+static gboolean rsn_dvdsrc_do_seek (RsnBaseSrc * bsrc, GstSegment * segment);
+
+static void rsn_dvdsrc_prepare_spu_stream_event (resinDvdSrc * src,
+ guint8 phys_stream, gboolean forced_only);
+static void rsn_dvdsrc_prepare_audio_stream_event (resinDvdSrc * src,
+ guint8 phys_stream);
+static gboolean rsn_dvdsrc_prepare_streamsinfo_event (resinDvdSrc * src);
+static void rsn_dvdsrc_prepare_clut_change_event (resinDvdSrc * src,
+ const guint32 * clut);
+static void rsn_dvdsrc_update_highlight (resinDvdSrc * src);
+
+static GstFlowReturn rsn_dvdsrc_create (RsnPushSrc * psrc, GstBuffer ** buf);
+static gboolean rsn_dvdsrc_src_event (RsnBaseSrc * basesrc, GstEvent * event);
+
+static void
+rsn_dvdsrc_register_extra (GType rsn_dvdsrc_type)
+{
+ GST_DEBUG_CATEGORY_INIT (rsndvdsrc_debug, "rsndvdsrc", 0,
+ "Resin DVD source element based on libdvdnav");
+
+ rsndvd_format = gst_format_register ("rsndvdsrc-internal",
+ "private Resin DVD src format");
+}
+
+static void
+rsn_dvdsrc_base_init (gpointer gclass)
+{
+ static GstElementDetails element_details = {
+ "Resin DVD Src",
+ "Source/DVD",
+ "DVD source element",
+ "Jan Schmidt <thaytan@noraisin.net>"
+ };
+ GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&src_factory));
+ gst_element_class_set_details (element_class, &element_details);
+}
+
+static void
+rsn_dvdsrc_class_init (resinDvdSrcClass * klass)
+{
+ GObjectClass *gobject_class;
+ RsnBaseSrcClass *gstbasesrc_class;
+ RsnPushSrcClass *gstpush_src_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
+ gstpush_src_class = GST_PUSH_SRC_CLASS (klass);
+
+ gobject_class->finalize = rsn_dvdsrc_finalize;
+ gobject_class->set_property = rsn_dvdsrc_set_property;
+ gobject_class->get_property = rsn_dvdsrc_get_property;
+
+ gstbasesrc_class->start = GST_DEBUG_FUNCPTR (rsn_dvdsrc_start);
+ gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (rsn_dvdsrc_stop);
+ gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (rsn_dvdsrc_unlock);
+ gstbasesrc_class->unlock_stop = GST_DEBUG_FUNCPTR (rsn_dvdsrc_unlock_stop);
+ gstbasesrc_class->event = GST_DEBUG_FUNCPTR (rsn_dvdsrc_src_event);
+ gstbasesrc_class->prepare_seek_segment =
+ GST_DEBUG_FUNCPTR (rsn_dvdsrc_prepare_seek);
+ gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (rsn_dvdsrc_do_seek);
+
+ gstpush_src_class->create = GST_DEBUG_FUNCPTR (rsn_dvdsrc_create);
+
+ g_object_class_install_property (gobject_class, ARG_DEVICE,
+ g_param_spec_string ("device", "Device", "DVD device location",
+ NULL, G_PARAM_READWRITE));
+}
+
+static void
+rsn_dvdsrc_init (resinDvdSrc * rsndvdsrc, resinDvdSrcClass * gclass)
+{
+ rsndvdsrc->device = g_strdup (DEFAULT_DEVICE);
+ rsndvdsrc->dvd_lock = g_mutex_new ();
+ rsndvdsrc->branch_lock = g_mutex_new ();
+ rsndvdsrc->branching = FALSE;
+ rsndvdsrc->still_cond = g_cond_new ();
+
+ rsn_base_src_set_format (GST_BASE_SRC (rsndvdsrc), GST_FORMAT_TIME);
+}
+
+static void
+rsn_dvdsrc_finalize (GObject * object)
+{
+ resinDvdSrc *src = RESINDVDSRC (object);
+ g_mutex_free (src->dvd_lock);
+ g_mutex_free (src->branch_lock);
+ g_cond_free (src->still_cond);
+
+ gst_buffer_replace (&src->alloc_buf, NULL);
+ gst_buffer_replace (&src->next_buf, NULL);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+rsn_dvdsrc_unlock (RsnBaseSrc * bsrc)
+{
+ resinDvdSrc *src = RESINDVDSRC (bsrc);
+
+ g_mutex_lock (src->branch_lock);
+ src->branching = TRUE;
+ g_cond_broadcast (src->still_cond);
+ g_mutex_unlock (src->branch_lock);
+
+ return TRUE;
+}
+
+static gboolean
+rsn_dvdsrc_unlock_stop (RsnBaseSrc * bsrc)
+{
+ resinDvdSrc *src = RESINDVDSRC (bsrc);
+
+ g_mutex_lock (src->branch_lock);
+ src->branching = FALSE;
+ g_mutex_unlock (src->branch_lock);
+
+ return TRUE;
+}
+
+static void
+rsn_dvdsrc_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ resinDvdSrc *src = RESINDVDSRC (object);
+
+ switch (prop_id) {
+ case ARG_DEVICE:
+ GST_OBJECT_LOCK (src);
+ g_free (src->device);
+ if (g_value_get_string (value) == NULL)
+ src->device = g_strdup (DEFAULT_DEVICE);
+ else
+ src->device = g_value_dup_string (value);
+ GST_OBJECT_UNLOCK (src);
+ g_print ("Device is now %s\n", src->device);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+rsn_dvdsrc_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ resinDvdSrc *src = RESINDVDSRC (object);
+
+ switch (prop_id) {
+ case ARG_DEVICE:
+ GST_OBJECT_LOCK (src);
+ g_value_set_string (value, src->device);
+ GST_OBJECT_UNLOCK (src);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+rsn_dvdsrc_start (RsnBaseSrc * bsrc)
+{
+ resinDvdSrc *src = RESINDVDSRC (bsrc);
+
+ g_mutex_lock (src->dvd_lock);
+ if (!read_vts_info (src)) {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+ (_("Could not read title information for DVD.")), GST_ERROR_SYSTEM);
+ goto fail;
+ }
+
+ if (dvdnav_open (&src->dvdnav, src->device) != DVDNAV_STATUS_OK) {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
+ (_("Failed to open DVD device '%s'."), src->device));
+ goto fail;
+ }
+
+ src->running = TRUE;
+ src->branching = FALSE;
+ src->discont = TRUE;
+ src->need_segment = TRUE;
+
+ src->cur_position = GST_CLOCK_TIME_NONE;
+ src->cur_start_ts = GST_CLOCK_TIME_NONE;
+ src->cur_end_ts = GST_CLOCK_TIME_NONE;
+
+ src->vts_n = 0;
+ src->in_menu = FALSE;
+
+ src->active_button = -1;
+ g_mutex_unlock (src->dvd_lock);
+
+ return TRUE;
+
+fail:
+ if (src->dvdnav) {
+ dvdnav_close (src->dvdnav);
+ src->dvdnav = NULL;
+ }
+ g_mutex_unlock (src->dvd_lock);
+ return FALSE;
+}
+
+/* Use libdvdread to read and cache info from the IFO file about
+ * streams in each VTS */
+static gboolean
+read_vts_info (resinDvdSrc * src)
+{
+ gint i;
+ gint n_vts;
+
+ if (src->vts_attrs) {
+ g_array_free (src->vts_attrs, TRUE);
+ src->vts_attrs = NULL;
+ }
+
+ if (src->dvdread)
+ DVDClose (src->dvdread);
+
+ src->dvdread = DVDOpen (src->device);
+ if (src->dvdread == NULL)
+ return FALSE;
+
+ if (!(src->vmg_file = ifoOpen (src->dvdread, 0))) {
+ GST_ERROR ("Can't open VMG ifo");
+ return FALSE;
+ }
+ n_vts = src->vmg_file->vts_atrt->nr_of_vtss;
+ memcpy (&src->vmgm_attr, src->vmg_file->vmgi_mat, sizeof (vmgi_mat_t));
+
+ GST_DEBUG ("Reading IFO info for %d VTSs", n_vts);
+ src->vts_attrs =
+ g_array_sized_new (FALSE, TRUE, sizeof (vtsi_mat_t), n_vts + 1);
+ if (!src->vts_attrs)
+ return FALSE;
+ g_array_set_size (src->vts_attrs, n_vts + 1);
+
+ for (i = 1; i <= n_vts; i++) {
+ ifo_handle_t *ifo = ifoOpen (src->dvdread, i);
+ if (!ifo) {
+ GST_ERROR ("Can't open VTS %d", i);
+ return FALSE;
+ }
+
+ GST_DEBUG ("VTS %d, Menu has %d audio %d subpictures. "
+ "Title has %d and %d", i,
+ ifo->vtsi_mat->nr_of_vtsm_audio_streams,
+ ifo->vtsi_mat->nr_of_vtsm_subp_streams,
+ ifo->vtsi_mat->nr_of_vts_audio_streams,
+ ifo->vtsi_mat->nr_of_vts_subp_streams);
+
+ memcpy (&g_array_index (src->vts_attrs, vtsi_mat_t, i),
+ ifo->vtsi_mat, sizeof (vtsi_mat_t));
+
+ ifoClose (ifo);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+rsn_dvdsrc_stop (RsnBaseSrc * bsrc)
+{
+ resinDvdSrc *src = RESINDVDSRC (bsrc);
+ gboolean ret = TRUE;
+
+ g_mutex_lock (src->dvd_lock);
+
+ /* Clear any allocated output buffer */
+ gst_buffer_replace (&src->alloc_buf, NULL);
+ gst_buffer_replace (&src->next_buf, NULL);
+ src->running = FALSE;
+
+ if (src->streams_event) {
+ gst_event_unref (src->streams_event);
+ src->streams_event = NULL;
+ }
+ if (src->clut_event) {
+ gst_event_unref (src->clut_event);
+ src->clut_event = NULL;
+ }
+ if (src->spu_select_event) {
+ gst_event_unref (src->spu_select_event);
+ src->spu_select_event = NULL;
+ }
+ if (src->audio_select_event) {
+ gst_event_unref (src->audio_select_event);
+ src->audio_select_event = NULL;
+ }
+ if (src->highlight_event) {
+ gst_event_unref (src->highlight_event);
+ src->highlight_event = NULL;
+ }
+
+ if (src->dvdnav) {
+ if (dvdnav_close (src->dvdnav) != DVDNAV_STATUS_OK) {
+ GST_ELEMENT_ERROR (src, RESOURCE, CLOSE, (NULL),
+ ("dvdnav_close failed: %s", dvdnav_err_to_string (src->dvdnav)));
+ ret = FALSE;
+ }
+ src->dvdnav = NULL;
+ }
+
+ if (src->vmg_file) {
+ ifoClose (src->vmg_file);
+ src->vmg_file = NULL;
+ }
+ if (src->vts_file) {
+ ifoClose (src->vts_file);
+ src->vts_file = NULL;
+ }
+ if (src->dvdread) {
+ DVDClose (src->dvdread);
+ src->dvdread = NULL;
+ }
+
+ g_mutex_unlock (src->dvd_lock);
+
+ return ret;
+}
+
+/* handle still events. Call with dvd_lock */
+static gboolean
+rsn_dvdsrc_do_still (resinDvdSrc * src, int duration)
+{
+ GstEvent *still_event;
+ GstStructure *s;
+ GstEvent *seg_event;
+ GstSegment *segment = &(GST_BASE_SRC (src)->segment);
+
+ g_print ("**** STILL FRAME. Duration %d ****\n", duration);
+
+ /* Send a close-segment event, and a dvd-still start
+ * event, then sleep */
+ s = gst_structure_new ("application/x-gst-dvd",
+ "event", G_TYPE_STRING, "dvd-still",
+ "still-state", G_TYPE_BOOLEAN, TRUE, NULL);
+ still_event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
+
+ segment->last_stop = src->cur_end_ts;
+
+ seg_event = gst_event_new_new_segment_full (TRUE,
+ segment->rate, segment->applied_rate, segment->format,
+ segment->start, segment->last_stop, segment->time);
+
+ /* Now, send the events. We need to drop the dvd lock while doing so,
+ * and then check after if we got flushed
+ */
+ g_mutex_unlock (src->dvd_lock);
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), still_event);
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), seg_event);
+ g_mutex_lock (src->dvd_lock);
+
+ g_mutex_lock (src->branch_lock);
+ if (src->branching) {
+ g_mutex_unlock (src->branch_lock);
+ return TRUE;
+ }
+
+ if (duration == 255) {
+ /*
+ * The only way to get woken from this still is by a flushing
+ * seek or a user action. Either one will clear the still, so
+ * don't skip it
+ */
+ src->need_segment = TRUE;
+ g_mutex_unlock (src->dvd_lock);
+ g_cond_wait (src->still_cond, src->branch_lock);
+ if (src->branching) {
+ g_mutex_unlock (src->branch_lock);
+ g_mutex_lock (src->dvd_lock);
+ return TRUE;
+ }
+ g_mutex_unlock (src->branch_lock);
+ g_mutex_lock (src->dvd_lock);
+ } else {
+ /* FIXME: Implement timed stills by sleeping on the clock, possibly
+ * in multiple steps if we get paused/unpaused */
+ if (dvdnav_still_skip (src->dvdnav) != DVDNAV_STATUS_OK)
+ return FALSE;
+
+ /* Later: We'll only do this if the still isn't interrupted: */
+ s = gst_structure_new ("application/x-gst-dvd",
+ "event", G_TYPE_STRING, "dvd-still",
+ "still-state", G_TYPE_BOOLEAN, TRUE, NULL);
+ still_event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
+
+ g_mutex_unlock (src->branch_lock);
+
+ g_mutex_unlock (src->dvd_lock);
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), still_event);
+ g_mutex_lock (src->dvd_lock);
+ }
+
+ return TRUE;
+}
+
+static GstFlowReturn
+rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock, GstBuffer ** outbuf)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
+ dvdnav_status_t dvdnav_ret;
+ guint8 *data;
+ gint event, len;
+
+ /* Allocate an output buffer if there isn't a pending one */
+ if (src->alloc_buf == NULL)
+ src->alloc_buf = gst_buffer_new_and_alloc (DVD_VIDEO_LB_LEN);
+
+ data = GST_BUFFER_DATA (src->alloc_buf);
+ len = DVD_VIDEO_LB_LEN;
+
+ dvdnav_ret = dvdnav_get_next_block (src->dvdnav, data, &event, &len);
+ if (dvdnav_ret != DVDNAV_STATUS_OK)
+ goto read_error;
+ g_mutex_lock (src->branch_lock);
+ if (src->branching)
+ goto branching;
+ g_mutex_unlock (src->branch_lock);
+
+ switch (event) {
+ case DVDNAV_BLOCK_OK:
+ /* Data block that needs outputting */
+ *outbuf = src->alloc_buf;
+ src->alloc_buf = NULL;
+ break;
+ case DVDNAV_NAV_PACKET:{
+ pci_t *pci = dvdnav_get_current_nav_pci (src->dvdnav);
+
+ GST_LOG_OBJECT (src, "NAV packet start TS %" GST_TIME_FORMAT
+ " end TS %" GST_TIME_FORMAT " %s",
+ GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm)),
+ GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm)),
+ (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm) != src->cur_end_ts) ?
+ "discont" : "");
+#if 0
+ g_print ("NAV packet start TS %" GST_TIME_FORMAT
+ " end TS %" GST_TIME_FORMAT " %s\n",
+ GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm)),
+ GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm)),
+ (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm) != src->cur_end_ts) ?
+ "discont" : "");
+#endif
+
+ if (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm) != src->cur_end_ts) {
+ g_print ("NAV packet discont: cur_end_ts %" GST_TIME_FORMAT " != "
+ " vobu_s_ptm: %" GST_TIME_FORMAT "\n",
+ GST_TIME_ARGS (src->cur_end_ts),
+ GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm)));
+ src->need_segment = TRUE;
+ }
+
+ src->cur_start_ts = MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm);
+ src->cur_end_ts = MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm);
+
+ /* highlight might change, let's check */
+ rsn_dvdsrc_update_highlight (src);
+
+ /* NAV packet is also a data block that needs sending */
+ *outbuf = src->alloc_buf;
+ src->alloc_buf = NULL;
+ break;
+ }
+ case DVDNAV_STOP:
+ /* End of the disc. EOS */
+ g_print ("STOP found. End of disc\n");
+ ret = GST_FLOW_UNEXPECTED;
+ break;
+ case DVDNAV_STILL_FRAME:{
+ dvdnav_still_event_t *info = (dvdnav_still_event_t *) data;
+ g_print ("STILL frame duration %d\n", info->length);
+
+ if (!have_dvd_lock) {
+ /* At a still frame but can't block, handle it later */
+ return GST_FLOW_WOULD_BLOCK;
+ }
+
+ if (!rsn_dvdsrc_do_still (src, info->length))
+ goto internal_error;
+
+ g_mutex_lock (src->branch_lock);
+ if (src->branching)
+ goto branching;
+ g_mutex_unlock (src->branch_lock);
+ break;
+ }
+ case DVDNAV_WAIT:
+ /* Drain out the queues so that the info on the screen matches
+ * the VM state */
+ if (have_dvd_lock) {
+ /* FIXME: Drain out the queues */
+ g_print ("****** FIXME: WAIT *****\n");
+ }
+ if (dvdnav_wait_skip (src->dvdnav) != DVDNAV_STATUS_OK)
+ goto internal_error;
+ break;
+ case DVDNAV_CELL_CHANGE:{
+ dvdnav_cell_change_event_t *event = (dvdnav_cell_change_event_t *) data;
+
+ src->pgc_duration = MPEGTIME_TO_GSTTIME (event->pgc_length);
+ src->cur_position = MPEGTIME_TO_GSTTIME (event->cell_start);
+
+ GST_DEBUG_OBJECT (src,
+ "CELL change dur now %" GST_TIME_FORMAT " position now %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (src->pgc_duration),
+ GST_TIME_ARGS (src->cur_position));
+ break;
+ }
+ case DVDNAV_SPU_CLUT_CHANGE:
+ rsn_dvdsrc_prepare_clut_change_event (src, (const guint32 *) data);
+ break;
+ case DVDNAV_VTS_CHANGE:{
+ dvdnav_vts_change_event_t *event = (dvdnav_vts_change_event_t *) data;
+
+ g_print ("VTS change\n");
+ if (dvdnav_is_domain_vmgm (src->dvdnav))
+ src->vts_n = 0;
+ else
+ src->vts_n = event->new_vtsN;
+
+ src->in_menu = !dvdnav_is_domain_vtsm (src->dvdnav);
+
+ if (!dvdnav_is_domain_fp (src->dvdnav))
+ rsn_dvdsrc_prepare_streamsinfo_event (src);
+
+ break;
+ }
+ case DVDNAV_AUDIO_STREAM_CHANGE:{
+ dvdnav_audio_stream_change_event_t *event =
+ (dvdnav_audio_stream_change_event_t *) data;
+ g_print ("cur audio stream change\n");
+ GST_DEBUG_OBJECT (src, " physical: %d", event->physical);
+ GST_DEBUG_OBJECT (src, " logical: %d", event->logical);
+
+ rsn_dvdsrc_prepare_audio_stream_event (src, event->physical);
+ break;
+ }
+ case DVDNAV_SPU_STREAM_CHANGE:{
+ dvdnav_spu_stream_change_event_t *event =
+ (dvdnav_spu_stream_change_event_t *) data;
+
+ rsn_dvdsrc_prepare_spu_stream_event (src, event->physical_wide & 0x1f,
+ (event->physical_wide & 0x80) ? TRUE : FALSE);
+
+ GST_DEBUG_OBJECT (src, " physical_wide: %d", event->physical_wide);
+ GST_DEBUG_OBJECT (src, " physical_letterbox: %d",
+ event->physical_letterbox);
+ GST_DEBUG_OBJECT (src, " physical_pan_scan: %d",
+ event->physical_pan_scan);
+ GST_DEBUG_OBJECT (src, " logical: %d", event->logical);
+ break;
+ }
+ case DVDNAV_HIGHLIGHT:{
+ rsn_dvdsrc_update_highlight (src);
+ if (src->highlight_event && have_dvd_lock) {
+ GstEvent *hl_event = src->highlight_event;
+ src->highlight_event = NULL;
+ g_mutex_unlock (src->dvd_lock);
+ g_print ("Highlight change - button: %d\n", src->active_button);
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), hl_event);
+ g_mutex_lock (src->dvd_lock);
+ }
+ break;
+ }
+ case DVDNAV_HOP_CHANNEL:
+ g_print ("Channel hop - User action\n");
+ src->need_segment = TRUE;
+ break;
+ case DVDNAV_NOP:
+ break;
+ default:
+ GST_WARNING_OBJECT (src, "Unknown dvdnav event %d", event);
+ break;
+ }
+
+ return ret;
+read_error:
+ GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
+ ("Failed to read next DVD block. Error: %s",
+ dvdnav_err_to_string (src->dvdnav)));
+ return GST_FLOW_ERROR;
+internal_error:
+ GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
+ ("Internal error processing DVD commands. Error: %s",
+ dvdnav_err_to_string (src->dvdnav)));
+ return GST_FLOW_ERROR;
+branching:
+ g_mutex_unlock (src->branch_lock);
+ return GST_FLOW_WRONG_STATE;
+}
+
+static GstFlowReturn
+rsn_dvdsrc_prepare_next_block (resinDvdSrc * src, gboolean have_dvd_lock)
+{
+ GstFlowReturn ret;
+
+ /* If buffer already ready, return */
+ if (src->next_buf)
+ return GST_FLOW_OK;
+
+ do {
+ ret = rsn_dvdsrc_step (src, have_dvd_lock, &src->next_buf);
+ }
+ while (ret == GST_FLOW_OK && src->next_buf == NULL);
+
+ if (ret == GST_FLOW_WOULD_BLOCK)
+ ret = GST_FLOW_OK;
+
+ return ret;
+}
+
+static GstFlowReturn
+rsn_dvdsrc_create (RsnPushSrc * psrc, GstBuffer ** outbuf)
+{
+ resinDvdSrc *src = RESINDVDSRC (psrc);
+ GstSegment *segment = &(GST_BASE_SRC (src)->segment);
+ GstFlowReturn ret;
+ GstEvent *streams_event = NULL;
+ GstEvent *clut_event = NULL;
+ GstEvent *spu_select_event = NULL;
+ GstEvent *audio_select_event = NULL;
+ GstEvent *highlight_event = NULL;
+
+ *outbuf = NULL;
+
+ g_mutex_lock (src->dvd_lock);
+ ret = rsn_dvdsrc_prepare_next_block (src, TRUE);
+ if (ret != GST_FLOW_OK) {
+ g_mutex_unlock (src->dvd_lock);
+ return ret;
+ }
+
+ if (src->next_buf != NULL) {
+ *outbuf = src->next_buf;
+ src->next_buf = NULL;
+
+ if (src->discont) {
+ g_print ("Discont packet\n");
+ GST_BUFFER_FLAG_SET (*outbuf, GST_BUFFER_FLAG_DISCONT);
+ src->discont = FALSE;
+ }
+ }
+
+ streams_event = src->streams_event;
+ src->streams_event = NULL;
+
+ spu_select_event = src->spu_select_event;
+ src->spu_select_event = NULL;
+
+ audio_select_event = src->audio_select_event;
+ src->audio_select_event = NULL;
+
+ clut_event = src->clut_event;
+ src->clut_event = NULL;
+
+ highlight_event = src->highlight_event;
+ src->highlight_event = NULL;
+
+ g_mutex_unlock (src->dvd_lock);
+
+ /* Push in-band events now that we've dropped the dvd_lock */
+ if (streams_event) {
+ g_print ("Pushing stream event\n");
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), streams_event);
+ }
+ if (spu_select_event) {
+ g_print ("Pushing spu_select event\n");
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), spu_select_event);
+ }
+ if (audio_select_event) {
+ g_print ("Pushing audio_select event\n");
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), audio_select_event);
+ }
+ if (clut_event) {
+ g_print ("Pushing clut event\n");
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), clut_event);
+ }
+
+ if (highlight_event) {
+ g_print ("Pushing highlight event with TS %" GST_TIME_FORMAT "\n",
+ GST_TIME_ARGS (GST_EVENT_TIMESTAMP (highlight_event)));
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), highlight_event);
+ }
+
+ g_mutex_lock (src->dvd_lock);
+
+ if (src->need_segment) {
+ /* Seamless segment update */
+ GstEvent *seek;
+
+ seek = gst_event_new_seek (segment->rate, rsndvd_format,
+ GST_SEEK_FLAG_NONE, GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_NONE, -1);
+ gst_element_send_event (GST_ELEMENT (src), seek);
+ src->need_segment = FALSE;
+ }
+ if (src->cur_end_ts != GST_CLOCK_TIME_NONE)
+ segment->last_stop = src->cur_end_ts;
+ g_mutex_unlock (src->dvd_lock);
+
+ return ret;
+}
+
+static RsnNavResult
+rsn_dvdsrc_perform_button_action (resinDvdSrc * src, RsnNavAction action)
+{
+ pci_t *pci = dvdnav_get_current_nav_pci (src->dvdnav);
+ RsnNavResult result = RSN_NAV_RESULT_NONE;
+ int button = 0;
+ btni_t *btn_info;
+
+ if (pci == NULL)
+ return RSN_NAV_RESULT_NONE;
+
+ if (pci->hli.hl_gi.hli_ss == 0)
+ return RSN_NAV_RESULT_NONE; /* No buttons at the moment */
+
+ dvdnav_get_current_highlight (src->dvdnav, &button);
+
+ if (button > pci->hli.hl_gi.btn_ns || button < 1)
+ return RSN_NAV_RESULT_NONE; /* No valid button */
+
+ btn_info = pci->hli.btnit + button - 1;
+
+ switch (action) {
+ case RSN_NAV_ACTION_ACTIVATE:
+ if (dvdnav_button_activate (src->dvdnav, pci) == DVDNAV_STATUS_OK)
+ result = RSN_NAV_RESULT_BRANCH;
+ break;
+ case RSN_NAV_ACTION_LEFT:
+ if (dvdnav_left_button_select (src->dvdnav, pci) == DVDNAV_STATUS_OK) {
+ if (btn_info->left &&
+ pci->hli.btnit[btn_info->left - 1].auto_action_mode)
+ result = RSN_NAV_RESULT_BRANCH;
+ else
+ result = RSN_NAV_RESULT_HIGHLIGHT;
+ }
+ break;
+ case RSN_NAV_ACTION_RIGHT:
+ if (dvdnav_right_button_select (src->dvdnav, pci) == DVDNAV_STATUS_OK) {
+ if (btn_info->right &&
+ pci->hli.btnit[btn_info->right - 1].auto_action_mode)
+ result = RSN_NAV_RESULT_BRANCH;
+ else
+ result = RSN_NAV_RESULT_HIGHLIGHT;
+ }
+ break;
+ case RSN_NAV_ACTION_DOWN:
+ if (dvdnav_lower_button_select (src->dvdnav, pci) == DVDNAV_STATUS_OK) {
+ if (btn_info->down &&
+ pci->hli.btnit[btn_info->down - 1].auto_action_mode)
+ result = RSN_NAV_RESULT_BRANCH;
+ else
+ result = RSN_NAV_RESULT_HIGHLIGHT;
+ }
+ break;
+ case RSN_NAV_ACTION_UP:
+ if (dvdnav_upper_button_select (src->dvdnav, pci) == DVDNAV_STATUS_OK) {
+ if (btn_info->up && pci->hli.btnit[btn_info->up - 1].auto_action_mode)
+ result = RSN_NAV_RESULT_BRANCH;
+ else
+ result = RSN_NAV_RESULT_HIGHLIGHT;
+ }
+ break;
+ }
+
+ if (result == RSN_NAV_RESULT_HIGHLIGHT)
+ g_cond_signal (src->still_cond);
+
+ return result;
+}
+
+static gboolean
+rsn_dvdsrc_handle_navigation_event (resinDvdSrc * src, GstEvent * event)
+{
+ const GstStructure *s = gst_event_get_structure (event);
+ const gchar *event_type;
+ gboolean channel_hop = FALSE;
+ gboolean have_lock = FALSE;
+ GstEvent *hl_event = NULL;
+ RsnNavResult nav_res = RSN_NAV_RESULT_NONE;
+
+ if (s == NULL)
+ return FALSE;
+ event_type = gst_structure_get_string (s, "event");
+ if (event_type == NULL)
+ return FALSE;
+
+ if (strcmp (event_type, "key-press") == 0) {
+ const gchar *key = gst_structure_get_string (s, "key");
+ if (key == NULL)
+ return FALSE;
+
+ GST_DEBUG ("dvdnavsrc got a keypress: %s", key);
+
+ g_mutex_lock (src->dvd_lock);
+ have_lock = TRUE;
+ if (!src->running)
+ goto not_running;
+
+ if (g_str_equal (key, "Return")) {
+ nav_res = rsn_dvdsrc_perform_button_action (src, RSN_NAV_ACTION_ACTIVATE);
+ } else if (g_str_equal (key, "Left")) {
+ nav_res = rsn_dvdsrc_perform_button_action (src, RSN_NAV_ACTION_LEFT);
+ } else if (g_str_equal (key, "Right")) {
+ nav_res = rsn_dvdsrc_perform_button_action (src, RSN_NAV_ACTION_RIGHT);
+ } else if (g_str_equal (key, "Up")) {
+ nav_res = rsn_dvdsrc_perform_button_action (src, RSN_NAV_ACTION_UP);
+ } else if (g_str_equal (key, "Down")) {
+ nav_res = rsn_dvdsrc_perform_button_action (src, RSN_NAV_ACTION_DOWN);
+ } else if (g_str_equal (key, "m")) {
+ if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Escape) == DVDNAV_STATUS_OK)
+ channel_hop = TRUE;
+ } else if (g_str_equal (key, "t")) {
+ if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Title) == DVDNAV_STATUS_OK)
+ channel_hop = TRUE;
+ } else if (g_str_equal (key, "r")) {
+ if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Root) == DVDNAV_STATUS_OK)
+ channel_hop = TRUE;
+ } else if (g_str_equal (key, "comma")) {
+ gint title = 0;
+ gint part = 0;
+
+ if (dvdnav_current_title_info (src->dvdnav, &title, &part) && title > 0
+ && part > 1) {
+ if (dvdnav_part_play (src->dvdnav, title, part - 1) ==
+ DVDNAV_STATUS_ERR)
+ dvdnav_prev_pg_search (src->dvdnav);
+ channel_hop = TRUE;
+ }
+ } else if (g_str_equal (key, "period")) {
+ gint title = 0;
+ gint part = 0;
+
+ if (dvdnav_current_title_info (src->dvdnav, &title, &part) && title > 0) {
+ if (dvdnav_part_play (src->dvdnav, title, part + 1) ==
+ DVDNAV_STATUS_ERR)
+ dvdnav_next_pg_search (src->dvdnav);
+ channel_hop = TRUE;
+ }
+ } else {
+ g_print ("Unknown keypress: %s\n", key);
+ }
+
+ } else if (strcmp (event_type, "mouse-move") == 0) {
+ gdouble x, y;
+ pci_t *nav;
+
+ if (!gst_structure_get_double (s, "pointer_x", &x) ||
+ !gst_structure_get_double (s, "pointer_y", &y))
+ return FALSE;
+
+ g_mutex_lock (src->dvd_lock);
+ have_lock = TRUE;
+ if (!src->running)
+ goto not_running;
+
+ nav = dvdnav_get_current_nav_pci (src->dvdnav);
+ if (nav && dvdnav_mouse_select (src->dvdnav, nav, (int) x, (int) y) ==
+ DVDNAV_STATUS_OK) {
+ nav_res = RSN_NAV_RESULT_HIGHLIGHT;
+ }
+ } else if (strcmp (event_type, "mouse-button-release") == 0) {
+ gdouble x, y;
+ pci_t *nav;
+
+ if (!gst_structure_get_double (s, "pointer_x", &x) ||
+ !gst_structure_get_double (s, "pointer_y", &y))
+ return FALSE;
+
+ GST_DEBUG_OBJECT (src, "Got click at %g, %g", x, y);
+
+ g_mutex_lock (src->dvd_lock);
+ have_lock = TRUE;
+ if (!src->running)
+ goto not_running;
+
+ nav = dvdnav_get_current_nav_pci (src->dvdnav);
+ if (nav &&
+ dvdnav_mouse_activate (src->dvdnav, nav, (int) x, (int) y) ==
+ DVDNAV_STATUS_OK) {
+ nav_res = RSN_NAV_RESULT_BRANCH;
+ }
+ }
+
+ if (have_lock) {
+ if (nav_res != RSN_NAV_RESULT_NONE) {
+ if (nav_res == RSN_NAV_RESULT_BRANCH) {
+ src->active_highlight = TRUE;
+ channel_hop = TRUE;
+ }
+
+ rsn_dvdsrc_update_highlight (src);
+ }
+
+ if (channel_hop) {
+ GstEvent *seek;
+
+ g_print ("flush and jump\n");
+ g_mutex_lock (src->branch_lock);
+ src->branching = TRUE;
+ g_cond_signal (src->still_cond);
+ g_mutex_unlock (src->branch_lock);
+
+ hl_event = src->highlight_event;
+ src->highlight_event = NULL;
+ src->active_highlight = FALSE;
+
+ g_mutex_unlock (src->dvd_lock);
+
+ if (hl_event) {
+ g_print ("Highlight change - button: %d\n", src->active_button);
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), hl_event);
+ }
+
+ /* Send ourselves a seek event to wake everything up and flush */
+ seek = gst_event_new_seek (1.0, rsndvd_format, GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_NONE, -1);
+ gst_element_send_event (GST_ELEMENT (src), seek);
+
+ g_mutex_lock (src->dvd_lock);
+
+ rsn_dvdsrc_update_highlight (src);
+ }
+
+ hl_event = src->highlight_event;
+ src->highlight_event = NULL;
+
+ g_mutex_unlock (src->dvd_lock);
+
+ if (hl_event) {
+ g_print ("Highlight change - button: %d\n", src->active_button);
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), hl_event);
+ }
+ }
+
+ return TRUE;
+not_running:
+ g_mutex_unlock (src->dvd_lock);
+ GST_DEBUG_OBJECT (src, "Element not started. Ignoring navigation event");
+ return FALSE;
+}
+
+static void
+rsn_dvdsrc_prepare_audio_stream_event (resinDvdSrc * src, guint8 phys_stream)
+{
+ GstStructure *s;
+ GstEvent *e;
+
+ s = gst_structure_new ("application/x-gst-dvd",
+ "event", G_TYPE_STRING, "dvd-set-audio-track",
+ "physical-id", G_TYPE_INT, (gint) phys_stream, NULL);
+
+ e = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
+
+ if (src->audio_select_event)
+ gst_event_unref (src->audio_select_event);
+ src->audio_select_event = e;
+}
+
+static void
+rsn_dvdsrc_prepare_spu_stream_event (resinDvdSrc * src, guint8 phys_stream,
+ gboolean forced_only)
+{
+ GstStructure *s;
+ GstEvent *e;
+
+ s = gst_structure_new ("application/x-gst-dvd",
+ "event", G_TYPE_STRING, "dvd-set-subpicture-track",
+ "physical-id", G_TYPE_INT, (gint) phys_stream,
+ "forced-only", G_TYPE_BOOLEAN, forced_only, NULL);
+
+ e = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
+
+ if (src->spu_select_event)
+ gst_event_unref (src->spu_select_event);
+ src->spu_select_event = e;
+}
+
+static gboolean
+rsn_dvdsrc_prepare_streamsinfo_event (resinDvdSrc * src)
+{
+ vtsi_mat_t *vts_attr;
+ audio_attr_t *a_attrs;
+ subp_attr_t *s_attrs;
+ gint n_audio, n_subp;
+ GstStructure *s;
+ GstEvent *e;
+ gint i;
+ gchar lang_code[3] = { '\0', '\0', '\0' };
+ gchar *t;
+
+ if (src->vts_attrs == NULL || src->vts_n >= src->vts_attrs->len) {
+ if (src->vts_attrs)
+ GST_ERROR_OBJECT (src, "No stream info for VTS %d (have %d)", src->vts_n,
+ src->vts_attrs->len);
+ else
+ GST_ERROR_OBJECT (src, "No stream info");
+ return FALSE;
+ }
+
+ if (src->vts_n == 0) {
+ /* VMGM info */
+ vts_attr = NULL;
+ a_attrs = &src->vmgm_attr.vmgm_audio_attr;
+ n_audio = MIN (1, src->vmgm_attr.nr_of_vmgm_audio_streams);
+ s_attrs = &src->vmgm_attr.vmgm_subp_attr;
+ n_subp = MIN (1, src->vmgm_attr.nr_of_vmgm_subp_streams);
+ } else if (src->in_menu) {
+ /* VTSM attrs */
+ vts_attr = &g_array_index (src->vts_attrs, vtsi_mat_t, src->vts_n);
+ a_attrs = &vts_attr->vtsm_audio_attr;
+ n_audio = vts_attr->nr_of_vtsm_audio_streams;
+ s_attrs = &vts_attr->vtsm_subp_attr;
+ n_subp = vts_attr->nr_of_vtsm_subp_streams;
+ } else {
+ /* VTS domain */
+ vts_attr = &g_array_index (src->vts_attrs, vtsi_mat_t, src->vts_n);
+ a_attrs = vts_attr->vts_audio_attr;
+ n_audio = vts_attr->nr_of_vts_audio_streams;
+ s_attrs = vts_attr->vts_subp_attr;
+ n_subp = vts_attr->nr_of_vts_subp_streams;
+ }
+
+ /* build event */
+ s = gst_structure_new ("application/x-gst-dvd",
+ "event", G_TYPE_STRING, "dvd-lang-codes", NULL);
+ e = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
+
+ /* audio */
+ if (n_audio == 0) {
+ /* Always create at least one audio stream */
+ gst_structure_set (s, "audio-0-format", G_TYPE_INT, (int) 0, NULL);
+ }
+ for (i = 0; i < n_audio; i++) {
+ const audio_attr_t *a = a_attrs + i;
+
+ t = g_strdup_printf ("audio-%d-format", i);
+ gst_structure_set (s, t, G_TYPE_INT, (int) a->audio_format, NULL);
+ g_free (t);
+
+ GST_DEBUG_OBJECT (src, "Audio stream %d is format %d", i,
+ (int) a->audio_format);
+
+ if (a->lang_type) {
+ t = g_strdup_printf ("audio-%d-language", i);
+ lang_code[0] = (a->lang_code >> 8) & 0xff;
+ lang_code[1] = a->lang_code & 0xff;
+ gst_structure_set (s, t, G_TYPE_STRING, lang_code, NULL);
+ g_free (t);
+
+ GST_DEBUG_OBJECT (src, "Audio stream %d is language %s", i, lang_code);
+ } else
+ GST_DEBUG_OBJECT (src, "Audio stream %d - no language", i, lang_code);
+ }
+
+ /* subpictures */
+ if (n_subp == 0) {
+ /* Always create at least one subpicture stream */
+ gst_structure_set (s, "subpicture-0-format", G_TYPE_INT, (int) 0, NULL);
+ gst_structure_set (s, "subpicture-0-language", G_TYPE_STRING, "MENU", NULL);
+ }
+ for (i = 0; i < n_subp; i++) {
+ const subp_attr_t *u = s_attrs + i;
+
+ t = g_strdup_printf ("subpicture-%d-format", i);
+ gst_structure_set (s, t, G_TYPE_INT, (int) 0, NULL);
+ g_free (t);
+
+ t = g_strdup_printf ("subpicture-%d-language", i);
+ if (u->type) {
+ lang_code[0] = (u->lang_code >> 8) & 0xff;
+ lang_code[1] = u->lang_code & 0xff;
+ gst_structure_set (s, t, G_TYPE_STRING, lang_code, NULL);
+ } else {
+ gst_structure_set (s, t, G_TYPE_STRING, "MENU", NULL);
+ }
+ g_free (t);
+
+ GST_DEBUG_OBJECT (src, "Subpicture stream %d is language %s", i,
+ lang_code[0] ? lang_code : "NONE");
+ }
+
+ if (src->streams_event)
+ gst_event_unref (src->streams_event);
+ src->streams_event = e;
+
+ return TRUE;
+}
+
+static void
+rsn_dvdsrc_prepare_clut_change_event (resinDvdSrc * src, const guint32 * clut)
+{
+ GstEvent *event;
+ GstStructure *structure;
+ gchar name[16];
+ int i;
+
+ structure = gst_structure_new ("application/x-gst-dvd",
+ "event", G_TYPE_STRING, "dvd-spu-clut-change", NULL);
+
+ /* Create a separate field for each value in the table. */
+ for (i = 0; i < 16; i++) {
+ sprintf (name, "clut%02d", i);
+ gst_structure_set (structure, name, G_TYPE_INT, (int) clut[i], NULL);
+ }
+
+ /* Create the DVD event and put the structure into it. */
+ event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
+
+ GST_LOG_OBJECT (src, "pushing clut change event %" GST_PTR_FORMAT, event);
+
+ if (src->clut_event)
+ gst_event_unref (src->clut_event);
+ src->clut_event = event;
+}
+
+/*
+ * Check for a new highlighted area, and prepare an spu highlight event if
+ * necessary.
+ */
+static void
+rsn_dvdsrc_update_highlight (resinDvdSrc * src)
+{
+ int button = 0;
+ pci_t *pci;
+ dvdnav_highlight_area_t area;
+ int mode = 0;
+ GstEvent *event = NULL;
+ GstStructure *s;
+
+ if (dvdnav_get_current_highlight (src->dvdnav, &button) != DVDNAV_STATUS_OK) {
+ GST_ELEMENT_ERROR (src, LIBRARY, FAILED, (NULL),
+ ("dvdnav_get_current_highlight: %s",
+ dvdnav_err_to_string (src->dvdnav)));
+ return;
+ }
+
+ pci = dvdnav_get_current_nav_pci (src->dvdnav);
+ if ((button > pci->hli.hl_gi.btn_ns) || (button < 1)) {
+ /* button is out of the range of possible buttons. */
+ button = 0;
+ }
+
+ if (pci->hli.hl_gi.hli_ss == 0 || button == 0) {
+ /* No highlight available, or no button selected - clear the SPU */
+ if (src->active_button != 0) {
+ src->active_button = 0;
+
+ s = gst_structure_new ("application/x-gst-dvd", "event",
+ G_TYPE_STRING, "dvd-spu-reset-highlight", NULL);
+ event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, s);
+ if (src->highlight_event)
+ gst_event_unref (src->highlight_event);
+ src->highlight_event = event;
+ }
+ return;
+ }
+
+ if (src->active_highlight)
+ mode = 1;
+
+ if (dvdnav_get_highlight_area (pci, button, mode, &area) != DVDNAV_STATUS_OK) {
+ GST_ELEMENT_ERROR (src, LIBRARY, FAILED, (NULL),
+ ("dvdnav_get_highlight_area: %s", dvdnav_err_to_string (src->dvdnav)));
+ return;
+ }
+
+ /* Check if we have a new button number, or a new highlight region. */
+ if (button != src->active_button ||
+ memcmp (&area, &(src->area), sizeof (dvdnav_highlight_area_t)) != 0) {
+ memcpy (&(src->area), &area, sizeof (dvdnav_highlight_area_t));
+
+ s = gst_structure_new ("application/x-gst-dvd", "event",
+ G_TYPE_STRING, "dvd-spu-highlight",
+ "button", G_TYPE_INT, (gint) button,
+ "palette", G_TYPE_INT, (gint) area.palette,
+ "sx", G_TYPE_INT, (gint) area.sx,
+ "sy", G_TYPE_INT, (gint) area.sy,
+ "ex", G_TYPE_INT, (gint) area.ex,
+ "ey", G_TYPE_INT, (gint) area.ey, NULL);
+
+ event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, s);
+
+ if (src->active_button == 0) {
+ /* When setting the button for the first time, take the
+ timestamp into account. */
+ GST_EVENT_TIMESTAMP (event) = MPEGTIME_TO_GSTTIME (area.pts);
+ }
+
+ src->active_button = button;
+
+ g_print ("Setting highlight. Button %d active %d TS %" GST_TIME_FORMAT
+ " palette 0x%x\n", button, mode,
+ GST_TIME_ARGS (GST_EVENT_TIMESTAMP (event)), area.palette);
+
+ if (src->highlight_event)
+ gst_event_unref (src->highlight_event);
+ src->highlight_event = event;
+ }
+}
+
+/* Use libdvdread to read and cache info from the IFO file about
+ * streams in each VTS */
+static gboolean
+rsn_dvdsrc_src_event (RsnBaseSrc * basesrc, GstEvent * event)
+{
+ resinDvdSrc *src = RESINDVDSRC (basesrc);
+ gboolean res;
+
+ GST_LOG_OBJECT (src, "handling %s event", GST_EVENT_TYPE_NAME (event));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_NAVIGATION:
+ res = rsn_dvdsrc_handle_navigation_event (src, event);
+ break;
+ default:
+ res = GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
+ break;
+ }
+
+ return res;
+}
+
+static gboolean
+rsn_dvdsrc_prepare_seek (RsnBaseSrc * bsrc, GstEvent * event,
+ GstSegment * segment)
+{
+ GstSeekType cur_type, stop_type;
+ gint64 cur, stop;
+ GstSeekFlags flags;
+ GstFormat seek_format;
+ gdouble rate;
+ gboolean update;
+
+ gst_event_parse_seek (event, &rate, &seek_format, &flags,
+ &cur_type, &cur, &stop_type, &stop);
+
+ if (seek_format == rsndvd_format) {
+ /* Seeks in our internal format are passed directly through to the do_seek
+ * method. */
+ gst_segment_init (segment, seek_format);
+ gst_segment_set_seek (segment, rate, seek_format, flags, cur_type, cur,
+ stop_type, stop, &update);
+ g_print ("Have internal seek event\n");
+
+ return TRUE;
+ }
+ /* Don't allow bytes seeks - angle, time, chapter, title only is the plan */
+ if (seek_format == GST_FORMAT_BYTES)
+ return FALSE;
+
+ /* Let basesrc handle other formats for now. FIXME: Implement angle,
+ * chapter etc */
+ return GST_BASE_SRC_CLASS (parent_class)->prepare_seek_segment (bsrc,
+ event, segment);
+}
+
+static gboolean
+rsn_dvdsrc_do_seek (RsnBaseSrc * bsrc, GstSegment * segment)
+{
+ resinDvdSrc *src = RESINDVDSRC (bsrc);
+ gboolean ret = FALSE;
+
+ if (segment->format == rsndvd_format) {
+ g_print ("Handling internal seek event\n");
+ ret = TRUE;
+ } else {
+ /* FIXME: Handle other formats: Time, title, chapter, angle */
+ /* HACK to make initial seek work: */
+ if (segment->format == GST_FORMAT_TIME) {
+ ret = TRUE;
+ src->discont = TRUE;
+ }
+ }
+
+ if (ret) {
+ /* The internal format has served its purpose of waking everything
+ * up and flushing, now step to the next data block so we know our
+ * position */
+ /* Force a highlight update */
+ src->active_button = -1;
+
+ g_print ("Entering prepare_next_block after seek\n");
+ if (rsn_dvdsrc_prepare_next_block (src, FALSE) != GST_FLOW_OK)
+ goto fail;
+ g_print ("prepare_next_block after seek done\n");
+
+ segment->format = GST_FORMAT_TIME;
+ /* The first TS output: */
+ segment->last_stop = segment->start = src->cur_start_ts;
+
+ /* time field = position is the 'logical' stream time here: */
+ segment->time = src->cur_position;
+
+ segment->stop = -1;
+ segment->duration = -1;
+
+ g_print ("seek completed. New start TS %" GST_TIME_FORMAT
+ " pos %" GST_TIME_FORMAT "\n", GST_TIME_ARGS (segment->start),
+ GST_TIME_ARGS (segment->time));
+
+ src->need_segment = FALSE;
+ }
+
+ return ret;
+fail:
+ g_print ("Seek in format %d failed\n", segment->format);
+ return FALSE;
+}
+
+gboolean
+rsndvdsrc_init (GstPlugin * plugin)
+{
+ gboolean res;
+
+ res = gst_element_register (plugin, "rsndvdsrc",
+ GST_RANK_NONE, RESIN_TYPE_DVDSRC);
+
+ return res;
+}
diff --git a/ext/resindvd/resindvdsrc.h b/ext/resindvd/resindvdsrc.h
new file mode 100644
index 00000000..8fe68100
--- /dev/null
+++ b/ext/resindvd/resindvdsrc.h
@@ -0,0 +1,113 @@
+/* GStreamer
+ * Copyright (C) 2008 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * 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 __RESINDVDSRC_H__
+#define __RESINDVDSRC_H__
+
+#include <gst/gst.h>
+
+#include "rsnpushsrc.h"
+
+#include "_stdint.h"
+
+#include <dvdnav/dvd_reader.h>
+#include <dvdnav/ifo_read.h>
+
+#include <dvdnav/dvdnav.h>
+#include <dvdnav/nav_print.h>
+
+G_BEGIN_DECLS
+
+#define RESIN_TYPE_DVDSRC (rsn_dvdsrc_get_type())
+#define RESINDVDSRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),RESIN_TYPE_DVDSRC,resinDvdSrc))
+#define RESINDVDSRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),RESIN_TYPE_DVDSRC,resinDvdSrcClass))
+#define IS_RESINDVDSRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),RESIN_TYPE_DVDSRC))
+#define IS_RESINDVDSRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),RESIN_TYPE_DVDSRC))
+
+typedef struct _resinDvdSrc resinDvdSrc;
+typedef struct _resinDvdSrcClass resinDvdSrcClass;
+
+struct _resinDvdSrc
+{
+ RsnPushSrc parent;
+
+ GMutex *dvd_lock;
+ GCond *still_cond;
+ GMutex *branch_lock;
+ gboolean branching;
+
+ gchar *device;
+ dvdnav_t *dvdnav;
+
+ /* dvd_reader instance is used to load and cache VTS/VMG ifo info */
+ dvd_reader_t *dvdread;
+
+ /* vmgi_mat_t from the VMG ifo: */
+ vmgi_mat_t vmgm_attr; /* VMGM domain info */
+ /* Array of cached vtsi_mat_t strctures from each IFO: */
+ GArray *vts_attrs;
+
+ ifo_handle_t *vmg_file;
+ ifo_handle_t *vts_file;
+
+ /* Current playback location: VTS 0 = VMG, plus in_menu or not */
+ gint vts_n;
+ gboolean in_menu;
+
+ gboolean running;
+ gboolean discont;
+ gboolean need_segment;
+ gboolean active_highlight;
+
+ GstBuffer *alloc_buf;
+ GstBuffer *next_buf;
+
+ /* Start timestamp of the previous NAV block */
+ GstClockTime cur_start_ts;
+ /* End timestamp of the previous NAV block */
+ GstClockTime cur_end_ts;
+ /* Position info of the previous NAV block */
+ GstClockTime cur_position;
+ /* Duration of the current PGC */
+ GstClockTime pgc_duration;
+
+ gint active_button;
+ dvdnav_highlight_area_t area;
+
+ /* Pending events to output */
+ GstEvent *streams_event;
+ GstEvent *clut_event;
+ GstEvent *spu_select_event;
+ GstEvent *audio_select_event;
+ GstEvent *highlight_event;
+};
+
+struct _resinDvdSrcClass
+{
+ RsnPushSrcClass parent_class;
+};
+
+GType rsn_dvdsrc_get_type (void);
+
+G_END_DECLS
+
+#endif /* __RESINDVDSRC_H__ */
diff --git a/ext/resindvd/rsnaudiomunge.c b/ext/resindvd/rsnaudiomunge.c
new file mode 100644
index 00000000..f0429ee9
--- /dev/null
+++ b/ext/resindvd/rsnaudiomunge.c
@@ -0,0 +1,378 @@
+/* GStreamer
+ * Copyright (C) 2008 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * 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 <string.h>
+
+#include <gst/gst.h>
+
+#include "rsnaudiomunge.h"
+
+GST_DEBUG_CATEGORY_STATIC (rsn_audiomunge_debug);
+#define GST_CAT_DEFAULT rsn_audiomunge_debug
+
+#define AUDIO_FILL_THRESHOLD (GST_SECOND/5)
+
+/* Filter signals and args */
+enum
+{
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_SILENT
+};
+
+/* the capabilities of the inputs and outputs.
+ *
+ * describe the real formats here.
+ */
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("ANY")
+ );
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("ANY")
+ );
+
+GST_BOILERPLATE (RsnAudioMunge, rsn_audiomunge, GstElement, GST_TYPE_ELEMENT);
+
+static void rsn_audiomunge_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void rsn_audiomunge_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static gboolean rsn_audiomunge_set_caps (GstPad * pad, GstCaps * caps);
+static GstFlowReturn rsn_audiomunge_chain (GstPad * pad, GstBuffer * buf);
+static gboolean rsn_audiomunge_sink_event (GstPad * pad, GstEvent * event);
+
+static GstStateChangeReturn
+rsn_audiomunge_change_state (GstElement * element, GstStateChange transition);
+
+static void
+rsn_audiomunge_base_init (gpointer gclass)
+{
+ static GstElementDetails element_details = {
+ "RsnAudioMunge",
+ "Audio/Filter",
+ "Resin DVD audio stream regulator",
+ "Jan Schmidt <thaytan@noraisin.net>"
+ };
+ GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+
+ GST_DEBUG_CATEGORY_INIT (rsn_audiomunge_debug, "rsn_audiomunge",
+ 0, "Resin audio stream regulator");
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&src_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&sink_template));
+
+ gst_element_class_set_details (element_class, &element_details);
+}
+
+static void
+rsn_audiomunge_class_init (RsnAudioMungeClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ gobject_class->set_property = rsn_audiomunge_set_property;
+ gobject_class->get_property = rsn_audiomunge_get_property;
+
+ gstelement_class->change_state = rsn_audiomunge_change_state;
+}
+
+static void
+rsn_audiomunge_init (RsnAudioMunge * munge, RsnAudioMungeClass * gclass)
+{
+ munge->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
+ gst_pad_set_setcaps_function (munge->sinkpad,
+ GST_DEBUG_FUNCPTR (rsn_audiomunge_set_caps));
+ gst_pad_set_getcaps_function (munge->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
+ gst_pad_set_chain_function (munge->sinkpad,
+ GST_DEBUG_FUNCPTR (rsn_audiomunge_chain));
+ gst_pad_set_event_function (munge->sinkpad,
+ GST_DEBUG_FUNCPTR (rsn_audiomunge_sink_event));
+ gst_element_add_pad (GST_ELEMENT (munge), munge->sinkpad);
+
+ munge->srcpad = gst_pad_new_from_static_template (&src_template, "src");
+ gst_pad_set_getcaps_function (munge->srcpad,
+ GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
+ gst_element_add_pad (GST_ELEMENT (munge), munge->srcpad);
+}
+
+static void
+rsn_audiomunge_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ //RsnAudioMunge *munge = RSN_AUDIOMUNGE (object);
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+rsn_audiomunge_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ //RsnAudioMunge *munge = RSN_AUDIOMUNGE (object);
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+rsn_audiomunge_set_caps (GstPad * pad, GstCaps * caps)
+{
+ RsnAudioMunge *munge = RSN_AUDIOMUNGE (gst_pad_get_parent (pad));
+ GstPad *otherpad;
+ gboolean ret;
+
+ g_return_val_if_fail (munge != NULL, FALSE);
+
+ otherpad = (pad == munge->srcpad) ? munge->sinkpad : munge->srcpad;
+ gst_object_unref (munge);
+
+ ret = gst_pad_set_caps (otherpad, caps);
+ return ret;
+}
+
+static void
+rsn_audiomunge_reset (RsnAudioMunge * munge)
+{
+ munge->have_audio = FALSE;
+ munge->in_still = FALSE;
+ gst_segment_init (&munge->sink_segment, GST_FORMAT_TIME);
+}
+
+static GstFlowReturn
+rsn_audiomunge_chain (GstPad * pad, GstBuffer * buf)
+{
+ RsnAudioMunge *munge = RSN_AUDIOMUNGE (GST_OBJECT_PARENT (pad));
+
+ if (!munge->have_audio) {
+ g_print ("First audio after flush has TS %" GST_TIME_FORMAT "\n",
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
+ }
+
+ munge->have_audio = TRUE;
+
+ /* just push out the incoming buffer without touching it */
+ return gst_pad_push (munge->srcpad, buf);
+}
+
+/* Create and send a silence buffer downstream */
+static GstFlowReturn
+rsn_audiomunge_make_audio (RsnAudioMunge * munge,
+ GstClockTime start, GstClockTime fill_time)
+{
+ GstFlowReturn ret;
+ GstBuffer *audio_buf;
+ GstCaps *caps;
+ guint buf_size;
+
+ /* Just generate a 48khz stereo buffer for now */
+#if 0
+ caps =
+ gst_caps_from_string
+ ("audio/x-raw-int,rate=48000,channels=2,width=16,depth=16,signed=(boolean)true,endianness=1234");
+ buf_size = 4 * (48000 * fill_time / GST_SECOND);
+#else
+ caps = gst_caps_from_string ("audio/x-raw-float, endianness=(int)1234,"
+ "width=(int)32, channels=(int)2, rate=(int)48000");
+ buf_size = 2 * 4 * (48000 * fill_time / GST_SECOND);
+#endif
+
+ audio_buf = gst_buffer_new_and_alloc (buf_size);
+
+ gst_buffer_set_caps (audio_buf, caps);
+ gst_caps_unref (caps);
+
+ GST_BUFFER_TIMESTAMP (audio_buf) = start;
+ GST_BUFFER_DURATION (audio_buf) = fill_time;
+ GST_BUFFER_FLAG_SET (audio_buf, GST_BUFFER_FLAG_DISCONT);
+
+ memset (GST_BUFFER_DATA (audio_buf), 0, buf_size);
+
+ g_print ("Sending %u bytes (%" GST_TIME_FORMAT ") of audio data "
+ "with TS %" GST_TIME_FORMAT "\n",
+ buf_size, GST_TIME_ARGS (fill_time), GST_TIME_ARGS (start));
+
+ ret = gst_pad_push (munge->srcpad, audio_buf);
+
+ return ret;
+}
+
+static void
+rsn_audiomunge_handle_dvd_event (RsnAudioMunge * munge, GstEvent * event)
+{
+ const GstStructure *s;
+ const gchar *event_type;
+
+ s = gst_event_get_structure (event);
+ event_type = gst_structure_get_string (s, "event");
+ if (event_type == NULL)
+ return;
+
+ if (strcmp (event_type, "dvd-still") == 0) {
+ gboolean in_still;
+
+ if (!gst_structure_get_boolean (s, "still-state", &in_still))
+ return;
+
+ /* Remember the still-frame state, so we can generate a pre-roll buffer
+ * when a new-segment arrives */
+ munge->in_still = in_still;
+
+ g_print ("**** AUDIO MUNGE: still-state now %d\n", munge->in_still);
+ }
+}
+
+static gboolean
+rsn_audiomunge_sink_event (GstPad * pad, GstEvent * event)
+{
+ gboolean ret = FALSE;
+ RsnAudioMunge *munge = RSN_AUDIOMUNGE (gst_pad_get_parent (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_FLUSH_STOP:
+ rsn_audiomunge_reset (munge);
+
+ g_print ("*********** AUDIO MUNGE: FLUSH\n");
+ ret = gst_pad_push_event (munge->srcpad, event);
+ break;
+ case GST_EVENT_NEWSEGMENT:
+ {
+ GstSegment *segment;
+ gboolean update;
+ GstFormat format;
+ gdouble rate, arate;
+ gint64 start, stop, time;
+
+ gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
+ &start, &stop, &time);
+
+ /* we need TIME format */
+ if (format != GST_FORMAT_TIME)
+ goto newseg_wrong_format;
+
+ /* now configure the values */
+ segment = &munge->sink_segment;
+
+ gst_segment_set_newsegment_full (segment, update,
+ rate, arate, format, start, stop, time);
+
+ if (munge->have_audio) {
+ g_print ("*********** AUDIO MUNGE NEWSEG: start %" GST_TIME_FORMAT
+ " stop %" GST_TIME_FORMAT " accum now %" GST_TIME_FORMAT
+ "\n", GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
+ GST_TIME_ARGS (segment->accum));
+
+ ret = gst_pad_push_event (munge->srcpad, event);
+ break;
+ }
+
+ /*
+ * FIXME:
+ * If the accum >= threshold or we're in a still frame and there's been
+ * no audio received, then we need to generate some audio data.
+ * If caused by a segment start update (time advancing in a gap) adjust
+ * the new-segment and send the buffer.
+ *
+ * Otherwise, send the buffer before the newsegment, so that it appears
+ * in the closing segment.
+ */
+ if (segment->accum >= AUDIO_FILL_THRESHOLD || munge->in_still) {
+ g_print ("*********** Send audio mebbe: accum = %" GST_TIME_FORMAT
+ " still-state=%d\n", GST_TIME_ARGS (segment->accum),
+ munge->in_still);
+ /* Just generate a 100ms silence buffer for now. FIXME: Fill the gap */
+ if (rsn_audiomunge_make_audio (munge, segment->start,
+ GST_SECOND / 10) == GST_FLOW_OK)
+ munge->have_audio = TRUE;
+ } else {
+ g_print ("*********** below thresh: accum = %" GST_TIME_FORMAT
+ "\n", GST_TIME_ARGS (segment->accum));
+ }
+
+ g_print ("*********** AUDIO MUNGE NEWSEG: start %" GST_TIME_FORMAT
+ " stop %" GST_TIME_FORMAT " accum now %" GST_TIME_FORMAT
+ "\n", GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
+ GST_TIME_ARGS (segment->accum));
+
+ ret = gst_pad_push_event (munge->srcpad, event);
+ break;
+ }
+ case GST_EVENT_CUSTOM_DOWNSTREAM:
+ {
+ const GstStructure *s = gst_event_get_structure (event);
+ if (s && gst_structure_has_name (s, "application/x-gst-dvd"))
+ rsn_audiomunge_handle_dvd_event (munge, event);
+
+ ret = gst_pad_push_event (munge->srcpad, event);
+ break;
+ }
+ default:
+ ret = gst_pad_push_event (munge->srcpad, event);
+ break;
+ }
+
+ return ret;
+
+newseg_wrong_format:
+
+ GST_DEBUG_OBJECT (munge, "received non TIME newsegment");
+ gst_event_unref (event);
+ gst_object_unref (munge);
+ return FALSE;
+}
+
+static GstStateChangeReturn
+rsn_audiomunge_change_state (GstElement * element, GstStateChange transition)
+{
+ RsnAudioMunge *munge = RSN_AUDIOMUNGE (element);
+ GstStateChangeReturn ret;
+
+ if (transition == GST_STATE_CHANGE_READY_TO_PAUSED)
+ rsn_audiomunge_reset (munge);
+
+ ret = parent_class->change_state (element, transition);
+
+ return ret;
+}
diff --git a/ext/resindvd/rsnaudiomunge.h b/ext/resindvd/rsnaudiomunge.h
new file mode 100644
index 00000000..f5ada285
--- /dev/null
+++ b/ext/resindvd/rsnaudiomunge.h
@@ -0,0 +1,61 @@
+/* GStreamer
+ * Copyright (C) 2008 Jan Schmidt <thaytan@noraisin.net>
+ *
+ * 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 __RSNAUDIOMUNGE_H__
+#define __RSNAUDIOMUNGE_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+/* #defines don't like whitespacey bits */
+#define RSN_TYPE_AUDIOMUNGE (rsn_audiomunge_get_type())
+#define RSN_AUDIOMUNGE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),RSN_TYPE_AUDIOMUNGE,RsnAudioMunge))
+#define RSN_AUDIOMUNGE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),RSN_TYPE_AUDIOMUNGE,RsnAudioMungeClass))
+#define RSN_IS_AUDIOMUNGE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),RSN_TYPE_AUDIOMUNGE))
+#define RSN_IS_AUDIOMUNGE_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),RSN_TYPE_AUDIOMUNGE))
+
+typedef struct _RsnAudioMunge RsnAudioMunge;
+typedef struct _RsnAudioMungeClass RsnAudioMungeClass;
+
+struct _RsnAudioMunge
+{
+ GstElement element;
+
+ GstPad *sinkpad, *srcpad;
+
+ GstSegment sink_segment;
+ gboolean have_audio;
+ gboolean in_still;
+};
+
+struct _RsnAudioMungeClass
+{
+ GstElementClass parent_class;
+};
+
+GType rsn_audiomunge_get_type (void);
+
+G_END_DECLS
+
+#endif /* __RSNAUDIOMUNGE_H__ */
diff --git a/ext/resindvd/rsnbasesrc.c b/ext/resindvd/rsnbasesrc.c
new file mode 100644
index 00000000..c4d7f24c
--- /dev/null
+++ b/ext/resindvd/rsnbasesrc.c
@@ -0,0 +1,2423 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000,2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstbasesrc.c:
+ *
+ * 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.
+ */
+
+/*
+ *
+ * This is a temporary copy of GstBaseSrc/GstPushSrc for the resin
+ * DVD components, to work around a deadlock with source elements that
+ * send seeks to themselves.
+ *
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "rsnbasesrc.h"
+#include <gst/base/gsttypefindhelper.h>
+#include <gst/gstmarshal.h>
+#include <gst/gst-i18n-lib.h>
+
+GST_DEBUG_CATEGORY_STATIC (rsn_base_src_debug);
+#define GST_CAT_DEFAULT rsn_base_src_debug
+
+#define GST_LIVE_GET_LOCK(elem) (GST_BASE_SRC_CAST(elem)->live_lock)
+#define GST_LIVE_LOCK(elem) g_mutex_lock(GST_LIVE_GET_LOCK(elem))
+#define GST_LIVE_TRYLOCK(elem) g_mutex_trylock(GST_LIVE_GET_LOCK(elem))
+#define GST_LIVE_UNLOCK(elem) g_mutex_unlock(GST_LIVE_GET_LOCK(elem))
+#define GST_LIVE_GET_COND(elem) (GST_BASE_SRC_CAST(elem)->live_cond)
+#define GST_LIVE_WAIT(elem) g_cond_wait (GST_LIVE_GET_COND (elem), GST_LIVE_GET_LOCK (elem))
+#define GST_LIVE_TIMED_WAIT(elem, timeval) g_cond_timed_wait (GST_LIVE_GET_COND (elem), GST_LIVE_GET_LOCK (elem),\
+ timeval)
+#define GST_LIVE_SIGNAL(elem) g_cond_signal (GST_LIVE_GET_COND (elem));
+#define GST_LIVE_BROADCAST(elem) g_cond_broadcast (GST_LIVE_GET_COND (elem));
+
+/* BaseSrc signals and args */
+enum
+{
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+#define DEFAULT_BLOCKSIZE 4096
+#define DEFAULT_NUM_BUFFERS -1
+#define DEFAULT_TYPEFIND FALSE
+#define DEFAULT_DO_TIMESTAMP FALSE
+
+enum
+{
+ PROP_0,
+ PROP_BLOCKSIZE,
+ PROP_NUM_BUFFERS,
+ PROP_TYPEFIND,
+ PROP_DO_TIMESTAMP
+};
+
+#define GST_BASE_SRC_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), RSN_TYPE_BASE_SRC, RsnBaseSrcPrivate))
+
+struct _RsnBaseSrcPrivate
+{
+ gboolean last_sent_eos; /* last thing we did was send an EOS (we set this
+ * to avoid the sending of two EOS in some cases) */
+ gboolean discont;
+
+ /* two segments to be sent in the streaming thread with STREAM_LOCK */
+ GstEvent *close_segment;
+ GstEvent *start_segment;
+
+ /* startup latency is the time it takes between going to PLAYING and producing
+ * the first BUFFER with running_time 0. This value is included in the latency
+ * reporting. */
+ GstClockTime latency;
+ /* timestamp offset, this is the offset add to the values of gst_times for
+ * pseudo live sources */
+ GstClockTimeDiff ts_offset;
+
+ gboolean do_timestamp;
+};
+
+static GstElementClass *parent_class = NULL;
+
+static void rsn_base_src_base_init (gpointer g_class);
+static void rsn_base_src_class_init (RsnBaseSrcClass * klass);
+static void rsn_base_src_init (RsnBaseSrc * src, gpointer g_class);
+static void rsn_base_src_finalize (GObject * object);
+
+
+GType
+rsn_base_src_get_type (void)
+{
+ static GType base_src_type = 0;
+
+ if (G_UNLIKELY (base_src_type == 0)) {
+ static const GTypeInfo base_src_info = {
+ sizeof (RsnBaseSrcClass),
+ (GBaseInitFunc) rsn_base_src_base_init,
+ NULL,
+ (GClassInitFunc) rsn_base_src_class_init,
+ NULL,
+ NULL,
+ sizeof (RsnBaseSrc),
+ 0,
+ (GInstanceInitFunc) rsn_base_src_init,
+ };
+
+ base_src_type = g_type_register_static (GST_TYPE_ELEMENT,
+ "RsnBaseSrc", &base_src_info, G_TYPE_FLAG_ABSTRACT);
+ }
+ return base_src_type;
+}
+static GstCaps *rsn_base_src_getcaps (GstPad * pad);
+static gboolean rsn_base_src_setcaps (GstPad * pad, GstCaps * caps);
+static void rsn_base_src_fixate (GstPad * pad, GstCaps * caps);
+
+static gboolean rsn_base_src_activate_push (GstPad * pad, gboolean active);
+static gboolean rsn_base_src_activate_pull (GstPad * pad, gboolean active);
+static void rsn_base_src_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void rsn_base_src_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static gboolean rsn_base_src_event_handler (GstPad * pad, GstEvent * event);
+static gboolean rsn_base_src_send_event (GstElement * elem, GstEvent * event);
+static gboolean rsn_base_src_default_event (RsnBaseSrc * src, GstEvent * event);
+static const GstQueryType *rsn_base_src_get_query_types (GstElement * element);
+
+static gboolean rsn_base_src_query (GstPad * pad, GstQuery * query);
+
+static gboolean rsn_base_src_default_negotiate (RsnBaseSrc * basesrc);
+static gboolean rsn_base_src_default_do_seek (RsnBaseSrc * src,
+ GstSegment * segment);
+static gboolean rsn_base_src_default_query (RsnBaseSrc * src, GstQuery * query);
+static gboolean rsn_base_src_default_prepare_seek_segment (RsnBaseSrc * src,
+ GstEvent * event, GstSegment * segment);
+
+static gboolean rsn_base_src_unlock (RsnBaseSrc * basesrc);
+static gboolean rsn_base_src_unlock_stop (RsnBaseSrc * basesrc);
+static gboolean rsn_base_src_start (RsnBaseSrc * basesrc);
+static gboolean rsn_base_src_stop (RsnBaseSrc * basesrc);
+
+static GstStateChangeReturn rsn_base_src_change_state (GstElement * element,
+ GstStateChange transition);
+
+static void rsn_base_src_loop (GstPad * pad);
+static gboolean rsn_base_src_pad_check_get_range (GstPad * pad);
+static gboolean rsn_base_src_default_check_get_range (RsnBaseSrc * bsrc);
+static GstFlowReturn rsn_base_src_pad_get_range (GstPad * pad, guint64 offset,
+ guint length, GstBuffer ** buf);
+static GstFlowReturn rsn_base_src_get_range (RsnBaseSrc * src, guint64 offset,
+ guint length, GstBuffer ** buf);
+
+static void
+rsn_base_src_base_init (gpointer g_class)
+{
+ GST_DEBUG_CATEGORY_INIT (rsn_base_src_debug, "basesrc", 0, "basesrc element");
+}
+
+static void
+rsn_base_src_class_init (RsnBaseSrcClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gstelement_class = GST_ELEMENT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (RsnBaseSrcPrivate));
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->finalize = GST_DEBUG_FUNCPTR (rsn_base_src_finalize);
+ gobject_class->set_property = GST_DEBUG_FUNCPTR (rsn_base_src_set_property);
+ gobject_class->get_property = GST_DEBUG_FUNCPTR (rsn_base_src_get_property);
+
+ g_object_class_install_property (gobject_class, PROP_BLOCKSIZE,
+ g_param_spec_ulong ("blocksize", "Block size",
+ "Size in bytes to read per buffer (0 = default)", 0, G_MAXULONG,
+ DEFAULT_BLOCKSIZE, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_NUM_BUFFERS,
+ g_param_spec_int ("num-buffers", "num-buffers",
+ "Number of buffers to output before sending EOS", -1, G_MAXINT,
+ DEFAULT_NUM_BUFFERS, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_TYPEFIND,
+ g_param_spec_boolean ("typefind", "Typefind",
+ "Run typefind before negotiating", DEFAULT_TYPEFIND,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_DO_TIMESTAMP,
+ g_param_spec_boolean ("do-timestamp", "Do timestamp",
+ "Apply current stream time to buffers", DEFAULT_DO_TIMESTAMP,
+ G_PARAM_READWRITE));
+
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (rsn_base_src_change_state);
+ gstelement_class->send_event = GST_DEBUG_FUNCPTR (rsn_base_src_send_event);
+ gstelement_class->get_query_types =
+ GST_DEBUG_FUNCPTR (rsn_base_src_get_query_types);
+
+ klass->negotiate = GST_DEBUG_FUNCPTR (rsn_base_src_default_negotiate);
+ klass->event = GST_DEBUG_FUNCPTR (rsn_base_src_default_event);
+ klass->do_seek = GST_DEBUG_FUNCPTR (rsn_base_src_default_do_seek);
+ klass->query = GST_DEBUG_FUNCPTR (rsn_base_src_default_query);
+ klass->check_get_range =
+ GST_DEBUG_FUNCPTR (rsn_base_src_default_check_get_range);
+ klass->prepare_seek_segment =
+ GST_DEBUG_FUNCPTR (rsn_base_src_default_prepare_seek_segment);
+}
+
+static void
+rsn_base_src_init (RsnBaseSrc * basesrc, gpointer g_class)
+{
+ GstPad *pad;
+ GstPadTemplate *pad_template;
+
+ basesrc->priv = GST_BASE_SRC_GET_PRIVATE (basesrc);
+
+ basesrc->is_live = FALSE;
+ basesrc->live_lock = g_mutex_new ();
+ basesrc->live_cond = g_cond_new ();
+ basesrc->num_buffers = DEFAULT_NUM_BUFFERS;
+ basesrc->num_buffers_left = -1;
+
+ basesrc->can_activate_push = TRUE;
+ basesrc->pad_mode = GST_ACTIVATE_NONE;
+
+ pad_template =
+ gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
+ g_return_if_fail (pad_template != NULL);
+
+ GST_DEBUG_OBJECT (basesrc, "creating src pad");
+ pad = gst_pad_new_from_template (pad_template, "src");
+
+ GST_DEBUG_OBJECT (basesrc, "setting functions on src pad");
+ gst_pad_set_activatepush_function (pad,
+ GST_DEBUG_FUNCPTR (rsn_base_src_activate_push));
+ gst_pad_set_activatepull_function (pad,
+ GST_DEBUG_FUNCPTR (rsn_base_src_activate_pull));
+ gst_pad_set_event_function (pad,
+ GST_DEBUG_FUNCPTR (rsn_base_src_event_handler));
+ gst_pad_set_query_function (pad, GST_DEBUG_FUNCPTR (rsn_base_src_query));
+ gst_pad_set_checkgetrange_function (pad,
+ GST_DEBUG_FUNCPTR (rsn_base_src_pad_check_get_range));
+ gst_pad_set_getrange_function (pad,
+ GST_DEBUG_FUNCPTR (rsn_base_src_pad_get_range));
+ gst_pad_set_getcaps_function (pad, GST_DEBUG_FUNCPTR (rsn_base_src_getcaps));
+ gst_pad_set_setcaps_function (pad, GST_DEBUG_FUNCPTR (rsn_base_src_setcaps));
+ gst_pad_set_fixatecaps_function (pad,
+ GST_DEBUG_FUNCPTR (rsn_base_src_fixate));
+
+ /* hold pointer to pad */
+ basesrc->srcpad = pad;
+ GST_DEBUG_OBJECT (basesrc, "adding src pad");
+ gst_element_add_pad (GST_ELEMENT (basesrc), pad);
+
+ basesrc->blocksize = DEFAULT_BLOCKSIZE;
+ basesrc->clock_id = NULL;
+ /* we operate in BYTES by default */
+ rsn_base_src_set_format (basesrc, GST_FORMAT_BYTES);
+ basesrc->data.ABI.typefind = DEFAULT_TYPEFIND;
+ basesrc->priv->do_timestamp = DEFAULT_DO_TIMESTAMP;
+
+ GST_OBJECT_FLAG_UNSET (basesrc, GST_BASE_SRC_STARTED);
+
+ GST_DEBUG_OBJECT (basesrc, "init done");
+}
+
+static void
+rsn_base_src_finalize (GObject * object)
+{
+ RsnBaseSrc *basesrc;
+ GstEvent **event_p;
+
+ basesrc = GST_BASE_SRC (object);
+
+ g_mutex_free (basesrc->live_lock);
+ g_cond_free (basesrc->live_cond);
+
+ event_p = &basesrc->data.ABI.pending_seek;
+ gst_event_replace ((GstEvent **) event_p, NULL);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/**
+ * rsn_base_src_wait_playing:
+ * @src: the src
+ *
+ * If the #RsnBaseSrcClass::create method performs its own synchronisation against
+ * the clock it must unblock when going from PLAYING to the PAUSED state and call
+ * this method before continuing to produce the remaining data.
+ *
+ * This function will block until a state change to PLAYING happens (in which
+ * case this function returns #GST_FLOW_OK) or the processing must be stopped due
+ * to a state change to READY or a FLUSH event (in which case this function
+ * returns #GST_FLOW_WRONG_STATE).
+ *
+ * Since: 0.10.12
+ *
+ * Returns: #GST_FLOW_OK if @src is PLAYING and processing can
+ * continue. Any other return value should be returned from the create vmethod.
+ */
+GstFlowReturn
+rsn_base_src_wait_playing (RsnBaseSrc * src)
+{
+ /* block until the state changes, or we get a flush, or something */
+ GST_LIVE_LOCK (src);
+ if (src->is_live) {
+ while (G_UNLIKELY (!src->live_running)) {
+ GST_DEBUG ("live source signal waiting");
+ GST_LIVE_SIGNAL (src);
+ GST_DEBUG ("live source waiting for running state");
+ GST_LIVE_WAIT (src);
+ GST_DEBUG ("live source unlocked");
+ }
+ /* FIXME, use another variable to signal stopping so that we don't
+ * have to grab another lock. */
+ GST_OBJECT_LOCK (src->srcpad);
+ if (G_UNLIKELY (GST_PAD_IS_FLUSHING (src->srcpad)))
+ goto flushing;
+ GST_OBJECT_UNLOCK (src->srcpad);
+ }
+ GST_LIVE_UNLOCK (src);
+
+ return GST_FLOW_OK;
+
+ /* ERRORS */
+flushing:
+ {
+ GST_DEBUG_OBJECT (src, "pad is flushing");
+ GST_OBJECT_UNLOCK (src->srcpad);
+ GST_LIVE_UNLOCK (src);
+ return GST_FLOW_WRONG_STATE;
+ }
+}
+
+/**
+ * rsn_base_src_set_live:
+ * @src: base source instance
+ * @live: new live-mode
+ *
+ * If the element listens to a live source, @live should
+ * be set to %TRUE.
+ *
+ * A live source will not produce data in the PAUSED state and
+ * will therefore not be able to participate in the PREROLL phase
+ * of a pipeline. To signal this fact to the application and the
+ * pipeline, the state change return value of the live source will
+ * be GST_STATE_CHANGE_NO_PREROLL.
+ */
+void
+rsn_base_src_set_live (RsnBaseSrc * src, gboolean live)
+{
+ GST_LIVE_LOCK (src);
+ src->is_live = live;
+ GST_LIVE_UNLOCK (src);
+}
+
+/**
+ * rsn_base_src_is_live:
+ * @src: base source instance
+ *
+ * Check if an element is in live mode.
+ *
+ * Returns: %TRUE if element is in live mode.
+ */
+gboolean
+rsn_base_src_is_live (RsnBaseSrc * src)
+{
+ gboolean result;
+
+ GST_LIVE_LOCK (src);
+ result = src->is_live;
+ GST_LIVE_UNLOCK (src);
+
+ return result;
+}
+
+/**
+ * rsn_base_src_set_format:
+ * @src: base source instance
+ * @format: the format to use
+ *
+ * Sets the default format of the source. This will be the format used
+ * for sending NEW_SEGMENT events and for performing seeks.
+ *
+ * If a format of GST_FORMAT_BYTES is set, the element will be able to
+ * operate in pull mode if the #RsnBaseSrc::is_seekable returns TRUE.
+ *
+ * @Since: 0.10.1
+ */
+void
+rsn_base_src_set_format (RsnBaseSrc * src, GstFormat format)
+{
+ gst_segment_init (&src->segment, format);
+}
+
+/**
+ * rsn_base_src_query_latency:
+ * @src: the source
+ * @live: if the source is live
+ * @min_latency: the min latency of the source
+ * @max_latency: the max latency of the source
+ *
+ * Query the source for the latency parameters. @live will be TRUE when @src is
+ * configured as a live source. @min_latency will be set to the difference
+ * between the running time and the timestamp of the first buffer.
+ * @max_latency is always the undefined value of -1.
+ *
+ * This function is mostly used by subclasses.
+ *
+ * Returns: TRUE if the query succeeded.
+ *
+ * Since: 0.10.13
+ */
+gboolean
+rsn_base_src_query_latency (RsnBaseSrc * src, gboolean * live,
+ GstClockTime * min_latency, GstClockTime * max_latency)
+{
+ GstClockTime min;
+
+ GST_LIVE_LOCK (src);
+ if (live)
+ *live = src->is_live;
+
+ /* if we have a startup latency, report this one, else report 0. Subclasses
+ * are supposed to override the query function if they want something
+ * else. */
+ if (src->priv->latency != -1)
+ min = src->priv->latency;
+ else
+ min = 0;
+
+ if (min_latency)
+ *min_latency = min;
+ if (max_latency)
+ *max_latency = -1;
+
+ GST_LOG_OBJECT (src, "latency: live %d, min %" GST_TIME_FORMAT
+ ", max %" GST_TIME_FORMAT, src->is_live, GST_TIME_ARGS (min),
+ GST_TIME_ARGS (-1));
+ GST_LIVE_UNLOCK (src);
+
+ return TRUE;
+}
+
+/**
+ * rsn_base_src_set_do_timestamp:
+ * @src: the source
+ * @timestamp: enable or disable timestamping
+ *
+ * Configure @src to automatically timestamp outgoing buffers based on the
+ * current running_time of the pipeline. This property is mostly useful for live
+ * sources.
+ *
+ * Since: 0.10.15
+ */
+void
+rsn_base_src_set_do_timestamp (RsnBaseSrc * src, gboolean timestamp)
+{
+ GST_OBJECT_LOCK (src);
+ src->priv->do_timestamp = timestamp;
+ GST_OBJECT_UNLOCK (src);
+}
+
+/**
+ * rsn_base_src_get_do_timestamp:
+ * @src: the source
+ *
+ * Query if @src timestamps outgoing buffers based on the current running_time.
+ *
+ * Returns: %TRUE if the base class will automatically timestamp outgoing buffers.
+ *
+ * Since: 0.10.15
+ */
+gboolean
+rsn_base_src_get_do_timestamp (RsnBaseSrc * src)
+{
+ gboolean res;
+
+ GST_OBJECT_LOCK (src);
+ res = src->priv->do_timestamp;
+ GST_OBJECT_UNLOCK (src);
+
+ return res;
+}
+
+static gboolean
+rsn_base_src_setcaps (GstPad * pad, GstCaps * caps)
+{
+ RsnBaseSrcClass *bclass;
+ RsnBaseSrc *bsrc;
+ gboolean res = TRUE;
+
+ bsrc = GST_BASE_SRC (GST_PAD_PARENT (pad));
+ bclass = GST_BASE_SRC_GET_CLASS (bsrc);
+
+ if (bclass->set_caps)
+ res = bclass->set_caps (bsrc, caps);
+
+ return res;
+}
+
+static GstCaps *
+rsn_base_src_getcaps (GstPad * pad)
+{
+ RsnBaseSrcClass *bclass;
+ RsnBaseSrc *bsrc;
+ GstCaps *caps = NULL;
+
+ bsrc = GST_BASE_SRC (GST_PAD_PARENT (pad));
+ bclass = GST_BASE_SRC_GET_CLASS (bsrc);
+ if (bclass->get_caps)
+ caps = bclass->get_caps (bsrc);
+
+ if (caps == NULL) {
+ GstPadTemplate *pad_template;
+
+ pad_template =
+ gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "src");
+ if (pad_template != NULL) {
+ caps = gst_caps_ref (gst_pad_template_get_caps (pad_template));
+ }
+ }
+ return caps;
+}
+
+static void
+rsn_base_src_fixate (GstPad * pad, GstCaps * caps)
+{
+ RsnBaseSrcClass *bclass;
+ RsnBaseSrc *bsrc;
+
+ bsrc = GST_BASE_SRC (gst_pad_get_parent (pad));
+ bclass = GST_BASE_SRC_GET_CLASS (bsrc);
+
+ if (bclass->fixate)
+ bclass->fixate (bsrc, caps);
+
+ gst_object_unref (bsrc);
+}
+
+static gboolean
+rsn_base_src_default_query (RsnBaseSrc * src, GstQuery * query)
+{
+ gboolean res;
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_POSITION:
+ {
+ GstFormat format;
+
+ gst_query_parse_position (query, &format, NULL);
+ switch (format) {
+ case GST_FORMAT_PERCENT:
+ {
+ gint64 percent;
+ gint64 position;
+ gint64 duration;
+
+ position = src->segment.last_stop;
+ duration = src->segment.duration;
+
+ if (position != -1 && duration != -1) {
+ if (position < duration)
+ percent = gst_util_uint64_scale (GST_FORMAT_PERCENT_MAX, position,
+ duration);
+ else
+ percent = GST_FORMAT_PERCENT_MAX;
+ } else
+ percent = -1;
+
+ gst_query_set_position (query, GST_FORMAT_PERCENT, percent);
+ res = TRUE;
+ break;
+ }
+ default:
+ {
+ gint64 position;
+
+ position = src->segment.last_stop;
+
+ if (position != -1) {
+ /* convert to requested format */
+ res =
+ gst_pad_query_convert (src->srcpad, src->segment.format,
+ position, &format, &position);
+ } else
+ res = TRUE;
+
+ gst_query_set_position (query, format, position);
+ break;
+ }
+ }
+ break;
+ }
+ case GST_QUERY_DURATION:
+ {
+ GstFormat format;
+
+ gst_query_parse_duration (query, &format, NULL);
+
+ GST_DEBUG_OBJECT (src, "duration query in format %s",
+ gst_format_get_name (format));
+ switch (format) {
+ case GST_FORMAT_PERCENT:
+ gst_query_set_duration (query, GST_FORMAT_PERCENT,
+ GST_FORMAT_PERCENT_MAX);
+ res = TRUE;
+ break;
+ default:
+ {
+ gint64 duration;
+
+ duration = src->segment.duration;
+
+ if (duration != -1) {
+ /* convert to requested format */
+ res =
+ gst_pad_query_convert (src->srcpad, src->segment.format,
+ duration, &format, &duration);
+ } else {
+ res = TRUE;
+ }
+ gst_query_set_duration (query, format, duration);
+ break;
+ }
+ }
+ break;
+ }
+
+ case GST_QUERY_SEEKING:
+ {
+ gst_query_set_seeking (query, src->segment.format,
+ src->seekable, 0, src->segment.duration);
+ res = TRUE;
+ break;
+ }
+ case GST_QUERY_SEGMENT:
+ {
+ gint64 start, stop;
+
+ /* no end segment configured, current duration then */
+ if ((stop = src->segment.stop) == -1)
+ stop = src->segment.duration;
+ start = src->segment.start;
+
+ /* adjust to stream time */
+ if (src->segment.time != -1) {
+ start -= src->segment.time;
+ if (stop != -1)
+ stop -= src->segment.time;
+ }
+ gst_query_set_segment (query, src->segment.rate, src->segment.format,
+ start, stop);
+ res = TRUE;
+ break;
+ }
+
+ case GST_QUERY_FORMATS:
+ {
+ gst_query_set_formats (query, 3, GST_FORMAT_DEFAULT,
+ GST_FORMAT_BYTES, GST_FORMAT_PERCENT);
+ res = TRUE;
+ break;
+ }
+ case GST_QUERY_CONVERT:
+ {
+ GstFormat src_fmt, dest_fmt;
+ gint64 src_val, dest_val;
+
+ gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
+
+ /* we can only convert between equal formats... */
+ if (src_fmt == dest_fmt) {
+ dest_val = src_val;
+ res = TRUE;
+ } else
+ res = FALSE;
+
+ gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
+ break;
+ }
+ case GST_QUERY_LATENCY:
+ {
+ GstClockTime min, max;
+ gboolean live;
+
+ /* Subclasses should override and implement something usefull */
+ res = rsn_base_src_query_latency (src, &live, &min, &max);
+
+ GST_LOG_OBJECT (src, "report latency: live %d, min %" GST_TIME_FORMAT
+ ", max %" GST_TIME_FORMAT, live, GST_TIME_ARGS (min),
+ GST_TIME_ARGS (max));
+
+ gst_query_set_latency (query, live, min, max);
+ break;
+ }
+ case GST_QUERY_JITTER:
+ case GST_QUERY_RATE:
+ default:
+ res = FALSE;
+ break;
+ }
+ GST_DEBUG_OBJECT (src, "query %s returns %d", GST_QUERY_TYPE_NAME (query),
+ res);
+ return res;
+}
+
+static gboolean
+rsn_base_src_query (GstPad * pad, GstQuery * query)
+{
+ RsnBaseSrc *src;
+ RsnBaseSrcClass *bclass;
+ gboolean result = FALSE;
+
+ src = GST_BASE_SRC (gst_pad_get_parent (pad));
+
+ bclass = GST_BASE_SRC_GET_CLASS (src);
+
+ if (bclass->query)
+ result = bclass->query (src, query);
+ else
+ result = gst_pad_query_default (pad, query);
+
+ gst_object_unref (src);
+
+ return result;
+}
+
+static gboolean
+rsn_base_src_default_do_seek (RsnBaseSrc * src, GstSegment * segment)
+{
+ gboolean res = TRUE;
+
+ /* update our offset if the start/stop position was updated */
+ if (segment->format == GST_FORMAT_BYTES) {
+ segment->last_stop = segment->start;
+ segment->time = segment->start;
+ } else if (segment->start == 0) {
+ /* seek to start, we can implement a default for this. */
+ segment->last_stop = 0;
+ segment->time = 0;
+ res = TRUE;
+ } else
+ res = FALSE;
+
+ return res;
+}
+
+static gboolean
+rsn_base_src_do_seek (RsnBaseSrc * src, GstSegment * segment)
+{
+ RsnBaseSrcClass *bclass;
+ gboolean result = FALSE;
+
+ bclass = GST_BASE_SRC_GET_CLASS (src);
+
+ if (bclass->do_seek)
+ result = bclass->do_seek (src, segment);
+
+ return result;
+}
+
+#define SEEK_TYPE_IS_RELATIVE(t) (((t) != GST_SEEK_TYPE_NONE) && ((t) != GST_SEEK_TYPE_SET))
+
+static gboolean
+rsn_base_src_default_prepare_seek_segment (RsnBaseSrc * src, GstEvent * event,
+ GstSegment * segment)
+{
+ /* By default, we try one of 2 things:
+ * - For absolute seek positions, convert the requested position to our
+ * configured processing format and place it in the output segment \
+ * - For relative seek positions, convert our current (input) values to the
+ * seek format, adjust by the relative seek offset and then convert back to
+ * the processing format
+ */
+ GstSeekType cur_type, stop_type;
+ gint64 cur, stop;
+ GstSeekFlags flags;
+ GstFormat seek_format, dest_format;
+ gdouble rate;
+ gboolean update;
+ gboolean res = TRUE;
+
+ gst_event_parse_seek (event, &rate, &seek_format, &flags,
+ &cur_type, &cur, &stop_type, &stop);
+ dest_format = segment->format;
+
+ if (seek_format == dest_format) {
+ gst_segment_set_seek (segment, rate, seek_format, flags,
+ cur_type, cur, stop_type, stop, &update);
+ return TRUE;
+ }
+
+ if (cur_type != GST_SEEK_TYPE_NONE) {
+ /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */
+ res =
+ gst_pad_query_convert (src->srcpad, seek_format, cur, &dest_format,
+ &cur);
+ cur_type = GST_SEEK_TYPE_SET;
+ }
+
+ if (res && stop_type != GST_SEEK_TYPE_NONE) {
+ /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */
+ res =
+ gst_pad_query_convert (src->srcpad, seek_format, stop, &dest_format,
+ &stop);
+ stop_type = GST_SEEK_TYPE_SET;
+ }
+
+ /* And finally, configure our output segment in the desired format */
+ gst_segment_set_seek (segment, rate, dest_format, flags, cur_type, cur,
+ stop_type, stop, &update);
+
+ if (!res)
+ goto no_format;
+
+ return res;
+
+no_format:
+ {
+ GST_DEBUG_OBJECT (src, "undefined format given, seek aborted.");
+ return FALSE;
+ }
+}
+
+static gboolean
+rsn_base_src_prepare_seek_segment (RsnBaseSrc * src, GstEvent * event,
+ GstSegment * seeksegment)
+{
+ RsnBaseSrcClass *bclass;
+ gboolean result = FALSE;
+
+ bclass = GST_BASE_SRC_GET_CLASS (src);
+
+ if (bclass->prepare_seek_segment)
+ result = bclass->prepare_seek_segment (src, event, seeksegment);
+
+ return result;
+}
+
+/* this code implements the seeking. It is a good example
+ * handling all cases.
+ *
+ * A seek updates the currently configured segment.start
+ * and segment.stop values based on the SEEK_TYPE. If the
+ * segment.start value is updated, a seek to this new position
+ * should be performed.
+ *
+ * The seek can only be executed when we are not currently
+ * streaming any data, to make sure that this is the case, we
+ * acquire the STREAM_LOCK which is taken when we are in the
+ * _loop() function or when a getrange() is called. Normally
+ * we will not receive a seek if we are operating in pull mode
+ * though.
+ *
+ * When we are in the loop() function, we might be in the middle
+ * of pushing a buffer, which might block in a sink. To make sure
+ * that the push gets unblocked we push out a FLUSH_START event.
+ * Our loop function will get a WRONG_STATE return value from
+ * the push and will pause, effectively releasing the STREAM_LOCK.
+ *
+ * For a non-flushing seek, we pause the task, which might eventually
+ * release the STREAM_LOCK. We say eventually because when the sink
+ * blocks on the sample we might wait a very long time until the sink
+ * unblocks the sample. In any case we acquire the STREAM_LOCK and
+ * can continue the seek. A non-flushing seek is normally done in a
+ * running pipeline to perform seamless playback.
+ * In the case of a non-flushing seek we need to make sure that the
+ * data we output after the seek is continuous with the previous data,
+ * this is because a non-flushing seek does not reset the stream-time
+ * to 0. We do this by closing the currently running segment, ie. sending
+ * a new_segment event with the stop position set to the last processed
+ * position.
+ *
+ * After updating the segment.start/stop values, we prepare for
+ * streaming again. We push out a FLUSH_STOP to make the peer pad
+ * accept data again and we start our task again.
+ *
+ * A segment seek posts a message on the bus saying that the playback
+ * of the segment started. We store the segment flag internally because
+ * when we reach the segment.stop we have to post a segment.done
+ * instead of EOS when doing a segment seek.
+ */
+/* FIXME (0.11), we have the unlock gboolean here because most current
+ * implementations (fdsrc, -base/gst/tcp/, ...) unconditionally unlock, even when
+ * the streaming thread isn't running, resulting in bogus unlocks later when it
+ * starts. This is fixed by adding unlock_stop, but we should still avoid unlocking
+ * unnecessarily for backwards compatibility. Ergo, the unlock variable stays
+ * until 0.11
+ */
+static gboolean
+rsn_base_src_perform_seek (RsnBaseSrc * src, GstEvent * event, gboolean unlock)
+{
+ gboolean res = TRUE;
+ gdouble rate;
+ GstFormat seek_format, dest_format;
+ GstSeekFlags flags;
+ GstSeekType cur_type, stop_type;
+ gint64 cur, stop;
+ gboolean flush;
+ gboolean update;
+ gboolean relative_seek = FALSE;
+ gboolean seekseg_configured = FALSE;
+ GstSegment seeksegment;
+
+ GST_DEBUG_OBJECT (src, "doing seek");
+
+ dest_format = src->segment.format;
+
+ if (event) {
+ gst_event_parse_seek (event, &rate, &seek_format, &flags,
+ &cur_type, &cur, &stop_type, &stop);
+
+ relative_seek = SEEK_TYPE_IS_RELATIVE (cur_type) ||
+ SEEK_TYPE_IS_RELATIVE (stop_type);
+
+ if (dest_format != seek_format && !relative_seek) {
+ /* If we have an ABSOLUTE position (SEEK_SET only), we can convert it
+ * here before taking the stream lock, otherwise we must convert it later,
+ * once we have the stream lock and can read the current position */
+ gst_segment_init (&seeksegment, dest_format);
+
+ if (!rsn_base_src_prepare_seek_segment (src, event, &seeksegment))
+ goto prepare_failed;
+
+ seekseg_configured = TRUE;
+ }
+
+ flush = flags & GST_SEEK_FLAG_FLUSH;
+ } else {
+ flush = FALSE;
+ }
+
+ /* send flush start */
+ if (flush)
+ gst_pad_push_event (src->srcpad, gst_event_new_flush_start ());
+ else
+ gst_pad_pause_task (src->srcpad);
+
+ /* unblock streaming thread */
+ if (unlock)
+ rsn_base_src_unlock (src);
+
+ /* grab streaming lock, this should eventually be possible, either
+ * because the task is paused or our streaming thread stopped
+ * because our peer is flushing. */
+ GST_PAD_STREAM_LOCK (src->srcpad);
+
+ if (unlock)
+ rsn_base_src_unlock_stop (src);
+
+ /* If we configured the seeksegment above, don't overwrite it now. Otherwise
+ * copy the current segment info into the temp segment that we can actually
+ * attempt the seek with. We only update the real segment if the seek suceeds. */
+ if (!seekseg_configured) {
+ memcpy (&seeksegment, &src->segment, sizeof (GstSegment));
+
+ /* now configure the final seek segment */
+ if (event) {
+ if (src->segment.format != seek_format) {
+ /* OK, here's where we give the subclass a chance to convert the relative
+ * seek into an absolute one in the processing format. We set up any
+ * absolute seek above, before taking the stream lock. */
+ if (!rsn_base_src_prepare_seek_segment (src, event, &seeksegment)) {
+ GST_DEBUG_OBJECT (src, "Preparing the seek failed after flushing. "
+ "Aborting seek");
+ res = FALSE;
+ }
+ } else {
+ /* The seek format matches our processing format, no need to ask the
+ * the subclass to configure the segment. */
+ gst_segment_set_seek (&seeksegment, rate, seek_format, flags,
+ cur_type, cur, stop_type, stop, &update);
+ }
+ }
+ /* Else, no seek event passed, so we're just (re)starting the
+ current segment. */
+ }
+
+ if (res) {
+ GST_DEBUG_OBJECT (src, "segment configured from %" G_GINT64_FORMAT
+ " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT,
+ seeksegment.start, seeksegment.stop, seeksegment.last_stop);
+
+ /* do the seek, segment.last_stop contains the new position. */
+ res = rsn_base_src_do_seek (src, &seeksegment);
+ }
+
+ /* and prepare to continue streaming */
+ if (flush) {
+ /* send flush stop, peer will accept data and events again. We
+ * are not yet providing data as we still have the STREAM_LOCK. */
+ gst_pad_push_event (src->srcpad, gst_event_new_flush_stop ());
+ } else if (res && src->data.ABI.running) {
+ /* we are running the current segment and doing a non-flushing seek,
+ * close the segment first based on the last_stop. */
+ GST_DEBUG_OBJECT (src, "closing running segment %" G_GINT64_FORMAT
+ " to %" G_GINT64_FORMAT, src->segment.start, src->segment.last_stop);
+
+ /* queue the segment for sending in the stream thread */
+ if (src->priv->close_segment)
+ gst_event_unref (src->priv->close_segment);
+ src->priv->close_segment =
+ gst_event_new_new_segment_full (TRUE,
+ src->segment.rate, src->segment.applied_rate, src->segment.format,
+ src->segment.start, src->segment.last_stop, src->segment.time);
+ }
+
+ /* The subclass must have converted the segment to the processing format
+ * by now */
+ if (res && seeksegment.format != dest_format) {
+ GST_DEBUG_OBJECT (src, "Subclass failed to prepare a seek segment "
+ "in the correct format. Aborting seek.");
+ res = FALSE;
+ }
+
+ /* if successfull seek, we update our real segment and push
+ * out the new segment. */
+ if (res) {
+ memcpy (&src->segment, &seeksegment, sizeof (GstSegment));
+
+ if (src->segment.flags & GST_SEEK_FLAG_SEGMENT) {
+ gst_element_post_message (GST_ELEMENT (src),
+ gst_message_new_segment_start (GST_OBJECT (src),
+ src->segment.format, src->segment.last_stop));
+ }
+
+ /* for deriving a stop position for the playback segment form the seek
+ * segment, we must take the duration when the stop is not set */
+ if ((stop = src->segment.stop) == -1)
+ stop = src->segment.duration;
+
+ GST_DEBUG_OBJECT (src, "Sending newsegment from %" G_GINT64_FORMAT
+ " to %" G_GINT64_FORMAT, src->segment.start, stop);
+
+ /* now replace the old segment so that we send it in the stream thread the
+ * next time it is scheduled. */
+ if (src->priv->start_segment)
+ gst_event_unref (src->priv->start_segment);
+ src->priv->start_segment =
+ gst_event_new_new_segment_full (FALSE,
+ src->segment.rate, src->segment.applied_rate, src->segment.format,
+ src->segment.last_stop, stop, src->segment.time);
+ }
+
+ src->priv->discont = TRUE;
+ src->data.ABI.running = TRUE;
+ /* and restart the task in case it got paused explicitely or by
+ * the FLUSH_START event we pushed out. */
+ gst_pad_start_task (src->srcpad, (GstTaskFunction) rsn_base_src_loop,
+ src->srcpad);
+
+ /* and release the lock again so we can continue streaming */
+ GST_PAD_STREAM_UNLOCK (src->srcpad);
+
+ return res;
+
+ /* ERROR */
+prepare_failed:
+ GST_DEBUG_OBJECT (src, "Preparing the seek failed before flushing. "
+ "Aborting seek");
+ return FALSE;
+}
+
+static const GstQueryType *
+rsn_base_src_get_query_types (GstElement * element)
+{
+ static const GstQueryType query_types[] = {
+ GST_QUERY_DURATION,
+ GST_QUERY_POSITION,
+ GST_QUERY_SEEKING,
+ GST_QUERY_SEGMENT,
+ GST_QUERY_FORMATS,
+ GST_QUERY_LATENCY,
+ GST_QUERY_JITTER,
+ GST_QUERY_RATE,
+ GST_QUERY_CONVERT,
+ 0
+ };
+
+ return query_types;
+}
+
+/* all events send to this element directly. This is mainly done from the
+ * application.
+ */
+static gboolean
+rsn_base_src_send_event (GstElement * element, GstEvent * event)
+{
+ RsnBaseSrc *src;
+ gboolean result = FALSE;
+
+ src = GST_BASE_SRC (element);
+
+ switch (GST_EVENT_TYPE (event)) {
+ /* bidirectional events */
+ case GST_EVENT_FLUSH_START:
+ case GST_EVENT_FLUSH_STOP:
+ /* sending random flushes downstream can break stuff,
+ * especially sync since all segment info will get flushed */
+ break;
+
+ /* downstream serialized events */
+ case GST_EVENT_EOS:
+ /* FIXME, queue EOS and make sure the task or pull function
+ * perform the EOS actions. */
+ break;
+ case GST_EVENT_NEWSEGMENT:
+ /* sending random NEWSEGMENT downstream can break sync. */
+ break;
+ case GST_EVENT_TAG:
+ /* sending tags could be useful, FIXME insert in dataflow */
+ break;
+ case GST_EVENT_BUFFERSIZE:
+ /* does not seem to make much sense currently */
+ break;
+
+ /* upstream events */
+ case GST_EVENT_QOS:
+ /* elements should override send_event and do something */
+ break;
+ case GST_EVENT_SEEK:
+ {
+ gboolean started;
+
+ GST_OBJECT_LOCK (src->srcpad);
+ if (GST_PAD_ACTIVATE_MODE (src->srcpad) == GST_ACTIVATE_PULL)
+ goto wrong_mode;
+ started = GST_PAD_ACTIVATE_MODE (src->srcpad) == GST_ACTIVATE_PUSH;
+ GST_OBJECT_UNLOCK (src->srcpad);
+
+ if (started) {
+ /* when we are running in push mode, we can execute the
+ * seek right now, we need to unlock. */
+ result = rsn_base_src_perform_seek (src, event, TRUE);
+ } else {
+ GstEvent **event_p;
+
+ /* else we store the event and execute the seek when we
+ * get activated */
+ GST_OBJECT_LOCK (src);
+ event_p = &src->data.ABI.pending_seek;
+ gst_event_replace ((GstEvent **) event_p, event);
+ GST_OBJECT_UNLOCK (src);
+ /* assume the seek will work */
+ result = TRUE;
+ }
+ break;
+ }
+ case GST_EVENT_NAVIGATION:
+ /* could make sense for elements that do something with navigation events
+ * but then they would need to override the send_event function */
+ break;
+ case GST_EVENT_LATENCY:
+ /* does not seem to make sense currently */
+ break;
+
+ /* custom events */
+ case GST_EVENT_CUSTOM_UPSTREAM:
+ /* override send_event if you want this */
+ break;
+ case GST_EVENT_CUSTOM_DOWNSTREAM:
+ case GST_EVENT_CUSTOM_BOTH:
+ /* FIXME, insert event in the dataflow */
+ break;
+ case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:
+ case GST_EVENT_CUSTOM_BOTH_OOB:
+ /* insert a random custom event into the pipeline */
+ GST_DEBUG_OBJECT (src, "pushing custom OOB event downstream");
+ result = gst_pad_push_event (src->srcpad, event);
+ /* we gave away the ref to the event in the push */
+ event = NULL;
+ break;
+ default:
+ break;
+ }
+done:
+ /* if we still have a ref to the event, unref it now */
+ if (event)
+ gst_event_unref (event);
+
+ return result;
+
+ /* ERRORS */
+wrong_mode:
+ {
+ GST_DEBUG_OBJECT (src, "cannot perform seek when operating in pull mode");
+ GST_OBJECT_UNLOCK (src->srcpad);
+ result = FALSE;
+ goto done;
+ }
+}
+
+static gboolean
+rsn_base_src_default_event (RsnBaseSrc * src, GstEvent * event)
+{
+ gboolean result;
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ /* is normally called when in push mode */
+ if (!src->seekable)
+ goto not_seekable;
+
+ result = rsn_base_src_perform_seek (src, event, TRUE);
+ break;
+ case GST_EVENT_FLUSH_START:
+ /* cancel any blocking getrange, is normally called
+ * when in pull mode. */
+ result = rsn_base_src_unlock (src);
+ break;
+ case GST_EVENT_FLUSH_STOP:
+ result = rsn_base_src_unlock_stop (src);
+ break;
+ default:
+ result = TRUE;
+ break;
+ }
+ return result;
+
+ /* ERRORS */
+not_seekable:
+ {
+ GST_DEBUG_OBJECT (src, "is not seekable");
+ return FALSE;
+ }
+}
+
+static gboolean
+rsn_base_src_event_handler (GstPad * pad, GstEvent * event)
+{
+ RsnBaseSrc *src;
+ RsnBaseSrcClass *bclass;
+ gboolean result = FALSE;
+
+ src = GST_BASE_SRC (gst_pad_get_parent (pad));
+ bclass = GST_BASE_SRC_GET_CLASS (src);
+
+ if (bclass->event) {
+ if (!(result = bclass->event (src, event)))
+ goto subclass_failed;
+ }
+
+done:
+ gst_event_unref (event);
+ gst_object_unref (src);
+
+ return result;
+
+ /* ERRORS */
+subclass_failed:
+ {
+ GST_DEBUG_OBJECT (src, "subclass refused event");
+ goto done;
+ }
+}
+
+static void
+rsn_base_src_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ RsnBaseSrc *src;
+
+ src = GST_BASE_SRC (object);
+
+ switch (prop_id) {
+ case PROP_BLOCKSIZE:
+ src->blocksize = g_value_get_ulong (value);
+ break;
+ case PROP_NUM_BUFFERS:
+ src->num_buffers = g_value_get_int (value);
+ break;
+ case PROP_TYPEFIND:
+ src->data.ABI.typefind = g_value_get_boolean (value);
+ break;
+ case PROP_DO_TIMESTAMP:
+ src->priv->do_timestamp = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+rsn_base_src_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ RsnBaseSrc *src;
+
+ src = GST_BASE_SRC (object);
+
+ switch (prop_id) {
+ case PROP_BLOCKSIZE:
+ g_value_set_ulong (value, src->blocksize);
+ break;
+ case PROP_NUM_BUFFERS:
+ g_value_set_int (value, src->num_buffers);
+ break;
+ case PROP_TYPEFIND:
+ g_value_set_boolean (value, src->data.ABI.typefind);
+ break;
+ case PROP_DO_TIMESTAMP:
+ g_value_set_boolean (value, src->priv->do_timestamp);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/* with STREAM_LOCK and LOCK */
+static GstClockReturn
+rsn_base_src_wait (RsnBaseSrc * basesrc, GstClock * clock, GstClockTime time)
+{
+ GstClockReturn ret;
+ GstClockID id;
+
+ id = gst_clock_new_single_shot_id (clock, time);
+
+ basesrc->clock_id = id;
+ /* release the object lock while waiting */
+ GST_OBJECT_UNLOCK (basesrc);
+
+ ret = gst_clock_id_wait (id, NULL);
+
+ GST_OBJECT_LOCK (basesrc);
+ gst_clock_id_unref (id);
+ basesrc->clock_id = NULL;
+
+ return ret;
+}
+
+/* perform synchronisation on a buffer.
+ * with STREAM_LOCK.
+ */
+static GstClockReturn
+rsn_base_src_do_sync (RsnBaseSrc * basesrc, GstBuffer * buffer)
+{
+ GstClockReturn result;
+ GstClockTime start, end;
+ RsnBaseSrcClass *bclass;
+ GstClockTime base_time;
+ GstClock *clock;
+ GstClockTime now = GST_CLOCK_TIME_NONE, timestamp;
+ gboolean do_timestamp, first, pseudo_live;
+
+ bclass = GST_BASE_SRC_GET_CLASS (basesrc);
+
+ start = end = -1;
+ if (bclass->get_times)
+ bclass->get_times (basesrc, buffer, &start, &end);
+
+ /* get buffer timestamp */
+ timestamp = GST_BUFFER_TIMESTAMP (buffer);
+
+ /* grab the lock to prepare for clocking and calculate the startup
+ * latency. */
+ GST_OBJECT_LOCK (basesrc);
+
+ /* if we are asked to sync against the clock we are a pseudo live element */
+ pseudo_live = (start != -1 && basesrc->is_live);
+ /* check for the first buffer */
+ first = (basesrc->priv->latency == -1);
+
+ if (timestamp != -1 && pseudo_live) {
+ GstClockTime latency;
+
+ /* we have a timestamp and a sync time, latency is the diff */
+ if (timestamp <= start)
+ latency = start - timestamp;
+ else
+ latency = 0;
+
+ if (first) {
+ GST_DEBUG_OBJECT (basesrc, "pseudo_live with latency %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (latency));
+ /* first time we calculate latency, just configure */
+ basesrc->priv->latency = latency;
+ } else {
+ if (basesrc->priv->latency != latency) {
+ /* we have a new latency, FIXME post latency message */
+ basesrc->priv->latency = latency;
+ GST_DEBUG_OBJECT (basesrc, "latency changed to %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (latency));
+ }
+ }
+ } else if (first) {
+ GST_DEBUG_OBJECT (basesrc, "no latency needed, live %d, sync %d",
+ basesrc->is_live, start != -1);
+ basesrc->priv->latency = 0;
+ }
+
+ /* get clock, if no clock, we can't sync or do timestamps */
+ if ((clock = GST_ELEMENT_CLOCK (basesrc)) == NULL)
+ goto no_clock;
+
+ base_time = GST_ELEMENT_CAST (basesrc)->base_time;
+
+ do_timestamp = basesrc->priv->do_timestamp;
+
+ /* first buffer, calculate the timestamp offset */
+ if (first) {
+ GstClockTime running_time;
+
+ now = gst_clock_get_time (clock);
+ running_time = now - base_time;
+
+ GST_LOG_OBJECT (basesrc,
+ "startup timestamp: %" GST_TIME_FORMAT ", running_time %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (timestamp),
+ GST_TIME_ARGS (running_time));
+
+ if (pseudo_live && timestamp != -1) {
+ /* live source and we need to sync, add startup latency to all timestamps
+ * to get the real running_time. Live sources should always timestamp
+ * according to the current running time. */
+ basesrc->priv->ts_offset = GST_CLOCK_DIFF (timestamp, running_time);
+
+ GST_LOG_OBJECT (basesrc, "live with sync, ts_offset %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (basesrc->priv->ts_offset));
+ } else {
+ basesrc->priv->ts_offset = 0;
+ GST_LOG_OBJECT (basesrc, "no timestamp offset needed");
+ }
+
+ if (!GST_CLOCK_TIME_IS_VALID (timestamp)) {
+ if (do_timestamp)
+ timestamp = running_time;
+ else
+ timestamp = 0;
+
+ GST_BUFFER_TIMESTAMP (buffer) = timestamp;
+
+ GST_LOG_OBJECT (basesrc, "created timestamp: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (timestamp));
+ }
+
+ /* add the timestamp offset we need for sync */
+ timestamp += basesrc->priv->ts_offset;
+ } else {
+ /* not the first buffer, the timestamp is the diff between the clock and
+ * base_time */
+ if (do_timestamp && !GST_CLOCK_TIME_IS_VALID (timestamp)) {
+ now = gst_clock_get_time (clock);
+
+ GST_BUFFER_TIMESTAMP (buffer) = now - base_time;
+
+ GST_LOG_OBJECT (basesrc, "created timestamp: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (now - base_time));
+ }
+ }
+
+ /* if we don't have a buffer timestamp, we don't sync */
+ if (!GST_CLOCK_TIME_IS_VALID (start))
+ goto no_sync;
+
+ if (basesrc->is_live && GST_CLOCK_TIME_IS_VALID (timestamp)) {
+ /* for pseudo live sources, add our ts_offset to the timestamp */
+ GST_BUFFER_TIMESTAMP (buffer) += basesrc->priv->ts_offset;
+ start += basesrc->priv->ts_offset;
+ }
+
+ GST_LOG_OBJECT (basesrc,
+ "waiting for clock, base time %" GST_TIME_FORMAT
+ ", stream_start %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (base_time), GST_TIME_ARGS (start));
+
+ result = rsn_base_src_wait (basesrc, clock, start + base_time);
+ GST_OBJECT_UNLOCK (basesrc);
+
+ GST_LOG_OBJECT (basesrc, "clock entry done: %d", result);
+
+ return result;
+
+ /* special cases */
+no_clock:
+ {
+ GST_DEBUG_OBJECT (basesrc, "we have no clock");
+ GST_OBJECT_UNLOCK (basesrc);
+ return GST_CLOCK_OK;
+ }
+no_sync:
+ {
+ GST_DEBUG_OBJECT (basesrc, "no sync needed");
+ GST_OBJECT_UNLOCK (basesrc);
+ return GST_CLOCK_OK;
+ }
+}
+
+static gboolean
+rsn_base_src_update_length (RsnBaseSrc * src, guint64 offset, guint * length)
+{
+ guint64 size, maxsize;
+ RsnBaseSrcClass *bclass;
+
+ bclass = GST_BASE_SRC_GET_CLASS (src);
+
+ /* only operate if we are working with bytes */
+ if (src->segment.format != GST_FORMAT_BYTES)
+ return TRUE;
+
+ /* get total file size */
+ size = (guint64) src->segment.duration;
+
+ /* the max amount of bytes to read is the total size or
+ * up to the segment.stop if present. */
+ if (src->segment.stop != -1)
+ maxsize = MIN (size, src->segment.stop);
+ else
+ maxsize = size;
+
+ GST_DEBUG_OBJECT (src,
+ "reading offset %" G_GUINT64_FORMAT ", length %u, size %" G_GINT64_FORMAT
+ ", segment.stop %" G_GINT64_FORMAT ", maxsize %" G_GINT64_FORMAT, offset,
+ *length, size, src->segment.stop, maxsize);
+
+ /* check size if we have one */
+ if (maxsize != -1) {
+ /* if we run past the end, check if the file became bigger and
+ * retry. */
+ if (G_UNLIKELY (offset + *length >= maxsize)) {
+ /* see if length of the file changed */
+ if (bclass->get_size)
+ if (!bclass->get_size (src, &size))
+ size = -1;
+
+ gst_segment_set_duration (&src->segment, GST_FORMAT_BYTES, size);
+
+ /* make sure we don't exceed the configured segment stop
+ * if it was set */
+ if (src->segment.stop != -1)
+ maxsize = MIN (size, src->segment.stop);
+ else
+ maxsize = size;
+
+ /* if we are at or past the end, EOS */
+ if (G_UNLIKELY (offset >= maxsize))
+ goto unexpected_length;
+
+ /* else we can clip to the end */
+ if (G_UNLIKELY (offset + *length >= maxsize))
+ *length = maxsize - offset;
+
+ }
+ }
+
+ /* keep track of current position. segment is in bytes, we checked
+ * that above. */
+ gst_segment_set_last_stop (&src->segment, GST_FORMAT_BYTES, offset);
+
+ return TRUE;
+
+ /* ERRORS */
+unexpected_length:
+ {
+ return FALSE;
+ }
+}
+
+static GstFlowReturn
+rsn_base_src_get_range (RsnBaseSrc * src, guint64 offset, guint length,
+ GstBuffer ** buf)
+{
+ GstFlowReturn ret;
+ RsnBaseSrcClass *bclass;
+ GstClockReturn status;
+
+ bclass = GST_BASE_SRC_GET_CLASS (src);
+
+ ret = rsn_base_src_wait_playing (src);
+ if (ret != GST_FLOW_OK)
+ goto stopped;
+
+ if (G_UNLIKELY (!GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED)))
+ goto not_started;
+
+ if (G_UNLIKELY (!bclass->create))
+ goto no_function;
+
+ if (G_UNLIKELY (!rsn_base_src_update_length (src, offset, &length)))
+ goto unexpected_length;
+
+ /* normally we don't count buffers */
+ if (G_UNLIKELY (src->num_buffers_left >= 0)) {
+ if (src->num_buffers_left == 0)
+ goto reached_num_buffers;
+ else
+ src->num_buffers_left--;
+ }
+
+ GST_DEBUG_OBJECT (src,
+ "calling create offset %" G_GUINT64_FORMAT " length %u, time %"
+ G_GINT64_FORMAT, offset, length, src->segment.time);
+
+ ret = bclass->create (src, offset, length, buf);
+ if (G_UNLIKELY (ret != GST_FLOW_OK))
+ goto not_ok;
+
+ /* no timestamp set and we are at offset 0, we can timestamp with 0 */
+ if (offset == 0 && src->segment.time == 0
+ && GST_BUFFER_TIMESTAMP (*buf) == -1)
+ GST_BUFFER_TIMESTAMP (*buf) = 0;
+
+ /* now sync before pushing the buffer */
+ status = rsn_base_src_do_sync (src, *buf);
+ switch (status) {
+ case GST_CLOCK_EARLY:
+ /* the buffer is too late. We currently don't drop the buffer. */
+ GST_DEBUG_OBJECT (src, "buffer too late!, returning anyway");
+ break;
+ case GST_CLOCK_OK:
+ /* buffer synchronised properly */
+ GST_DEBUG_OBJECT (src, "buffer ok");
+ break;
+ case GST_CLOCK_UNSCHEDULED:
+ /* this case is triggered when we were waiting for the clock and
+ * it got unlocked because we did a state change. We return
+ * WRONG_STATE in this case to stop the dataflow also get rid of the
+ * produced buffer. */
+ GST_DEBUG_OBJECT (src,
+ "clock was unscheduled (%d), returning WRONG_STATE", status);
+ gst_buffer_unref (*buf);
+ *buf = NULL;
+ ret = GST_FLOW_WRONG_STATE;
+ break;
+ default:
+ /* all other result values are unexpected and errors */
+ GST_ELEMENT_ERROR (src, CORE, CLOCK,
+ (_("Internal clock error.")),
+ ("clock returned unexpected return value %d", status));
+ gst_buffer_unref (*buf);
+ *buf = NULL;
+ ret = GST_FLOW_ERROR;
+ break;
+ }
+ return ret;
+
+ /* ERROR */
+stopped:
+ {
+ GST_DEBUG_OBJECT (src, "wait_playing returned %d (%s)", ret,
+ gst_flow_get_name (ret));
+ return ret;
+ }
+not_ok:
+ {
+ GST_DEBUG_OBJECT (src, "create returned %d (%s)", ret,
+ gst_flow_get_name (ret));
+ return ret;
+ }
+not_started:
+ {
+ GST_DEBUG_OBJECT (src, "getrange but not started");
+ return GST_FLOW_WRONG_STATE;
+ }
+no_function:
+ {
+ GST_DEBUG_OBJECT (src, "no create function");
+ return GST_FLOW_ERROR;
+ }
+unexpected_length:
+ {
+ GST_DEBUG_OBJECT (src, "unexpected length %u (offset=%" G_GUINT64_FORMAT
+ ", size=%" G_GINT64_FORMAT ")", length, offset, src->segment.duration);
+ return GST_FLOW_UNEXPECTED;
+ }
+reached_num_buffers:
+ {
+ GST_DEBUG_OBJECT (src, "sent all buffers");
+ return GST_FLOW_UNEXPECTED;
+ }
+}
+
+static GstFlowReturn
+rsn_base_src_pad_get_range (GstPad * pad, guint64 offset, guint length,
+ GstBuffer ** buf)
+{
+ RsnBaseSrc *src;
+ GstFlowReturn res;
+
+ src = GST_BASE_SRC (gst_pad_get_parent (pad));
+
+ res = rsn_base_src_get_range (src, offset, length, buf);
+
+ gst_object_unref (src);
+
+ return res;
+}
+
+static gboolean
+rsn_base_src_default_check_get_range (RsnBaseSrc * src)
+{
+ gboolean res;
+
+ if (!GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED)) {
+ GST_LOG_OBJECT (src, "doing start/stop to check get_range support");
+ if (G_LIKELY (rsn_base_src_start (src)))
+ rsn_base_src_stop (src);
+ }
+
+ /* we can operate in getrange mode if the native format is bytes
+ * and we are seekable, this condition is set in the random_access
+ * flag and is set in the _start() method. */
+ res = src->random_access;
+
+ return res;
+}
+
+static gboolean
+rsn_base_src_check_get_range (RsnBaseSrc * src)
+{
+ RsnBaseSrcClass *bclass;
+ gboolean res;
+
+ bclass = GST_BASE_SRC_GET_CLASS (src);
+
+ if (bclass->check_get_range == NULL)
+ goto no_function;
+
+ res = bclass->check_get_range (src);
+ GST_LOG_OBJECT (src, "%s() returned %d",
+ GST_DEBUG_FUNCPTR_NAME (bclass->check_get_range), (gint) res);
+
+ return res;
+
+ /* ERRORS */
+no_function:
+ {
+ GST_WARNING_OBJECT (src, "no check_get_range function set");
+ return FALSE;
+ }
+}
+
+static gboolean
+rsn_base_src_pad_check_get_range (GstPad * pad)
+{
+ RsnBaseSrc *src;
+ gboolean res;
+
+ src = GST_BASE_SRC (gst_pad_get_parent (pad));
+
+ res = rsn_base_src_check_get_range (src);
+
+ gst_object_unref (src);
+
+ return res;
+}
+
+static void
+rsn_base_src_loop (GstPad * pad)
+{
+ RsnBaseSrc *src;
+ GstBuffer *buf = NULL;
+ GstFlowReturn ret;
+ gint64 position;
+ gboolean eos;
+
+ eos = FALSE;
+
+ src = GST_BASE_SRC (gst_pad_get_parent (pad));
+
+ src->priv->last_sent_eos = FALSE;
+
+ /* if we operate in bytes, we can calculate an offset */
+ if (src->segment.format == GST_FORMAT_BYTES)
+ position = src->segment.last_stop;
+ else
+ position = -1;
+
+ ret = rsn_base_src_get_range (src, position, src->blocksize, &buf);
+ if (G_UNLIKELY (ret != GST_FLOW_OK)) {
+ GST_INFO_OBJECT (src, "pausing after rsn_base_src_get_range() = %s",
+ gst_flow_get_name (ret));
+ goto pause;
+ }
+ /* this should not happen */
+ if (G_UNLIKELY (buf == NULL))
+ goto null_buffer;
+
+ /* push events to close/start our segment before we push the buffer. */
+ if (src->priv->close_segment) {
+ gst_pad_push_event (pad, src->priv->close_segment);
+ src->priv->close_segment = NULL;
+ }
+ if (src->priv->start_segment) {
+ gst_pad_push_event (pad, src->priv->start_segment);
+ src->priv->start_segment = NULL;
+ }
+
+ /* figure out the new position */
+ switch (src->segment.format) {
+ case GST_FORMAT_BYTES:
+ position += GST_BUFFER_SIZE (buf);
+ break;
+ case GST_FORMAT_TIME:
+ {
+ GstClockTime start, duration;
+
+ start = GST_BUFFER_TIMESTAMP (buf);
+ duration = GST_BUFFER_DURATION (buf);
+
+ if (GST_CLOCK_TIME_IS_VALID (start))
+ position = start;
+ else
+ position = src->segment.last_stop;
+
+ if (GST_CLOCK_TIME_IS_VALID (duration))
+ position += duration;
+ break;
+ }
+ case GST_FORMAT_DEFAULT:
+ position = GST_BUFFER_OFFSET_END (buf);
+ break;
+ default:
+ position = -1;
+ break;
+ }
+ if (position != -1) {
+ if (src->segment.stop != -1) {
+ if (position >= src->segment.stop) {
+ eos = TRUE;
+ position = src->segment.stop;
+ }
+ }
+ gst_segment_set_last_stop (&src->segment, src->segment.format, position);
+ }
+
+ if (G_UNLIKELY (src->priv->discont)) {
+ buf = gst_buffer_make_metadata_writable (buf);
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
+ src->priv->discont = FALSE;
+ }
+
+ ret = gst_pad_push (pad, buf);
+ if (G_UNLIKELY (ret != GST_FLOW_OK)) {
+ GST_INFO_OBJECT (src, "pausing after gst_pad_push() = %s",
+ gst_flow_get_name (ret));
+ goto pause;
+ }
+
+ if (eos) {
+ GST_INFO_OBJECT (src, "pausing after EOS");
+ ret = GST_FLOW_UNEXPECTED;
+ goto pause;
+ }
+
+done:
+ gst_object_unref (src);
+ return;
+
+ /* special cases */
+pause:
+ {
+ const gchar *reason = gst_flow_get_name (ret);
+
+ GST_DEBUG_OBJECT (src, "pausing task, reason %s", reason);
+ src->data.ABI.running = FALSE;
+ gst_pad_pause_task (pad);
+ if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
+ if (ret == GST_FLOW_UNEXPECTED) {
+ /* perform EOS logic */
+ if (src->segment.flags & GST_SEEK_FLAG_SEGMENT) {
+ gst_element_post_message (GST_ELEMENT_CAST (src),
+ gst_message_new_segment_done (GST_OBJECT_CAST (src),
+ src->segment.format, src->segment.last_stop));
+ } else {
+ gst_pad_push_event (pad, gst_event_new_eos ());
+ src->priv->last_sent_eos = TRUE;
+ }
+ } else {
+ /* for fatal errors we post an error message, post the error
+ * first so the app knows about the error first. */
+ GST_ELEMENT_ERROR (src, STREAM, FAILED,
+ (_("Internal data flow error.")),
+ ("streaming task paused, reason %s (%d)", reason, ret));
+ gst_pad_push_event (pad, gst_event_new_eos ());
+ src->priv->last_sent_eos = TRUE;
+ }
+ }
+ goto done;
+ }
+null_buffer:
+ {
+ GST_ELEMENT_ERROR (src, STREAM, FAILED,
+ (_("Internal data flow error.")), ("element returned NULL buffer"));
+ /* we finished the segment on error */
+ src->data.ABI.running = FALSE;
+ gst_pad_pause_task (pad);
+ gst_pad_push_event (pad, gst_event_new_eos ());
+ src->priv->last_sent_eos = TRUE;
+ goto done;
+ }
+}
+
+/* this will always be called between start() and stop(). So you can rely on
+ * resources allocated by start() and freed from stop(). This needs to be added
+ * to the docs at some point. */
+static gboolean
+rsn_base_src_unlock (RsnBaseSrc * basesrc)
+{
+ RsnBaseSrcClass *bclass;
+ gboolean result = TRUE;
+
+ GST_DEBUG ("unlock");
+ /* unblock whatever the subclass is doing */
+ bclass = GST_BASE_SRC_GET_CLASS (basesrc);
+ if (bclass->unlock)
+ result = bclass->unlock (basesrc);
+
+ GST_DEBUG ("unschedule clock");
+ /* and unblock the clock as well, if any */
+ GST_OBJECT_LOCK (basesrc);
+ if (basesrc->clock_id) {
+ gst_clock_id_unschedule (basesrc->clock_id);
+ }
+ GST_OBJECT_UNLOCK (basesrc);
+
+ GST_DEBUG ("unlock done");
+
+ return result;
+}
+
+/* this will always be called between start() and stop(). So you can rely on
+ * resources allocated by start() and freed from stop(). This needs to be added
+ * to the docs at some point. */
+static gboolean
+rsn_base_src_unlock_stop (RsnBaseSrc * basesrc)
+{
+ RsnBaseSrcClass *bclass;
+ gboolean result = TRUE;
+
+ GST_DEBUG_OBJECT (basesrc, "unlock stop");
+
+ /* Finish a previous unblock request, allowing subclasses to flush command
+ * queues or whatever they need to do */
+ bclass = GST_BASE_SRC_GET_CLASS (basesrc);
+ if (bclass->unlock_stop)
+ result = bclass->unlock_stop (basesrc);
+
+ GST_DEBUG_OBJECT (basesrc, "unlock stop done");
+
+ return result;
+}
+
+/* default negotiation code.
+ *
+ * Take intersection between src and sink pads, take first
+ * caps and fixate.
+ */
+static gboolean
+rsn_base_src_default_negotiate (RsnBaseSrc * basesrc)
+{
+ GstCaps *thiscaps;
+ GstCaps *caps = NULL;
+ GstCaps *peercaps = NULL;
+ gboolean result = FALSE;
+
+ /* first see what is possible on our source pad */
+ thiscaps = gst_pad_get_caps (GST_BASE_SRC_PAD (basesrc));
+ GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps);
+ /* nothing or anything is allowed, we're done */
+ if (thiscaps == NULL || gst_caps_is_any (thiscaps))
+ goto no_nego_needed;
+
+ /* get the peer caps */
+ peercaps = gst_pad_peer_get_caps (GST_BASE_SRC_PAD (basesrc));
+ GST_DEBUG_OBJECT (basesrc, "caps of peer: %" GST_PTR_FORMAT, peercaps);
+ if (peercaps) {
+ GstCaps *icaps;
+
+ /* get intersection */
+ icaps = gst_caps_intersect (thiscaps, peercaps);
+ GST_DEBUG_OBJECT (basesrc, "intersect: %" GST_PTR_FORMAT, icaps);
+ gst_caps_unref (thiscaps);
+ gst_caps_unref (peercaps);
+ if (icaps) {
+ /* take first (and best, since they are sorted) possibility */
+ caps = gst_caps_copy_nth (icaps, 0);
+ gst_caps_unref (icaps);
+ }
+ } else {
+ /* no peer, work with our own caps then */
+ caps = thiscaps;
+ }
+ if (caps) {
+ caps = gst_caps_make_writable (caps);
+ gst_caps_truncate (caps);
+
+ /* now fixate */
+ if (!gst_caps_is_empty (caps)) {
+ gst_pad_fixate_caps (GST_BASE_SRC_PAD (basesrc), caps);
+ GST_DEBUG_OBJECT (basesrc, "fixated to: %" GST_PTR_FORMAT, caps);
+
+ if (gst_caps_is_any (caps)) {
+ /* hmm, still anything, so element can do anything and
+ * nego is not needed */
+ result = TRUE;
+ } else if (gst_caps_is_fixed (caps)) {
+ /* yay, fixed caps, use those then */
+ gst_pad_set_caps (GST_BASE_SRC_PAD (basesrc), caps);
+ result = TRUE;
+ }
+ }
+ gst_caps_unref (caps);
+ }
+ return result;
+
+no_nego_needed:
+ {
+ GST_DEBUG_OBJECT (basesrc, "no negotiation needed");
+ if (thiscaps)
+ gst_caps_unref (thiscaps);
+ return TRUE;
+ }
+}
+
+static gboolean
+rsn_base_src_negotiate (RsnBaseSrc * basesrc)
+{
+ RsnBaseSrcClass *bclass;
+ gboolean result = TRUE;
+
+ bclass = GST_BASE_SRC_GET_CLASS (basesrc);
+
+ if (bclass->negotiate)
+ result = bclass->negotiate (basesrc);
+
+ return result;
+}
+
+static gboolean
+rsn_base_src_start (RsnBaseSrc * basesrc)
+{
+ RsnBaseSrcClass *bclass;
+ gboolean result;
+ guint64 size;
+
+ if (GST_OBJECT_FLAG_IS_SET (basesrc, GST_BASE_SRC_STARTED))
+ return TRUE;
+
+ GST_DEBUG_OBJECT (basesrc, "starting source");
+
+ basesrc->num_buffers_left = basesrc->num_buffers;
+
+ gst_segment_init (&basesrc->segment, basesrc->segment.format);
+ basesrc->data.ABI.running = FALSE;
+
+ bclass = GST_BASE_SRC_GET_CLASS (basesrc);
+ if (bclass->start)
+ result = bclass->start (basesrc);
+ else
+ result = TRUE;
+
+ if (!result)
+ goto could_not_start;
+
+ GST_OBJECT_FLAG_SET (basesrc, GST_BASE_SRC_STARTED);
+
+ /* figure out the size */
+ if (basesrc->segment.format == GST_FORMAT_BYTES) {
+ if (bclass->get_size) {
+ if (!(result = bclass->get_size (basesrc, &size)))
+ size = -1;
+ } else {
+ result = FALSE;
+ size = -1;
+ }
+ GST_DEBUG_OBJECT (basesrc, "setting size %" G_GUINT64_FORMAT, size);
+ /* only update the size when operating in bytes, subclass is supposed
+ * to set duration in the start method for other formats */
+ gst_segment_set_duration (&basesrc->segment, GST_FORMAT_BYTES, size);
+ } else {
+ size = -1;
+ }
+
+ GST_DEBUG_OBJECT (basesrc,
+ "format: %d, have size: %d, size: %" G_GUINT64_FORMAT ", duration: %"
+ G_GINT64_FORMAT, basesrc->segment.format, result, size,
+ basesrc->segment.duration);
+
+ /* check if we can seek */
+ if (bclass->is_seekable)
+ basesrc->seekable = bclass->is_seekable (basesrc);
+ else
+ basesrc->seekable = FALSE;
+
+ GST_DEBUG_OBJECT (basesrc, "is seekable: %d", basesrc->seekable);
+
+ /* update for random access flag */
+ basesrc->random_access = basesrc->seekable &&
+ basesrc->segment.format == GST_FORMAT_BYTES;
+
+ GST_DEBUG_OBJECT (basesrc, "is random_access: %d", basesrc->random_access);
+
+ /* run typefind if we are random_access and the typefinding is enabled. */
+ if (basesrc->random_access && basesrc->data.ABI.typefind && size != -1) {
+ GstCaps *caps;
+
+ caps = gst_type_find_helper (basesrc->srcpad, size);
+ gst_pad_set_caps (basesrc->srcpad, caps);
+ gst_caps_unref (caps);
+ } else {
+ /* use class or default negotiate function */
+ if (!rsn_base_src_negotiate (basesrc))
+ goto could_not_negotiate;
+ }
+
+ return TRUE;
+
+ /* ERROR */
+could_not_start:
+ {
+ GST_DEBUG_OBJECT (basesrc, "could not start");
+ /* subclass is supposed to post a message. We don't have to call _stop. */
+ return FALSE;
+ }
+could_not_negotiate:
+ {
+ GST_DEBUG_OBJECT (basesrc, "could not negotiate, stopping");
+ GST_ELEMENT_ERROR (basesrc, STREAM, FORMAT,
+ ("Could not negotiate format"), ("Check your filtered caps, if any"));
+ /* we must call stop */
+ rsn_base_src_stop (basesrc);
+ return FALSE;
+ }
+}
+
+static gboolean
+rsn_base_src_stop (RsnBaseSrc * basesrc)
+{
+ RsnBaseSrcClass *bclass;
+ gboolean result = TRUE;
+
+ if (!GST_OBJECT_FLAG_IS_SET (basesrc, GST_BASE_SRC_STARTED))
+ return TRUE;
+
+ GST_DEBUG_OBJECT (basesrc, "stopping source");
+
+ bclass = GST_BASE_SRC_GET_CLASS (basesrc);
+ if (bclass->stop)
+ result = bclass->stop (basesrc);
+
+ if (result)
+ GST_OBJECT_FLAG_UNSET (basesrc, GST_BASE_SRC_STARTED);
+
+ return result;
+}
+
+static gboolean
+rsn_base_src_deactivate (RsnBaseSrc * basesrc, GstPad * pad)
+{
+ gboolean result;
+
+ GST_LIVE_LOCK (basesrc);
+ basesrc->live_running = TRUE;
+ GST_LIVE_SIGNAL (basesrc);
+ GST_LIVE_UNLOCK (basesrc);
+
+ /* step 1, unblock clock sync (if any) */
+ result = rsn_base_src_unlock (basesrc);
+
+ /* step 2, make sure streaming finishes */
+ result &= gst_pad_stop_task (pad);
+
+ /* step 3, clear the unblock condition */
+ result &= rsn_base_src_unlock_stop (basesrc);
+
+ return result;
+}
+
+static gboolean
+rsn_base_src_activate_push (GstPad * pad, gboolean active)
+{
+ RsnBaseSrc *basesrc;
+ GstEvent *event;
+
+ basesrc = GST_BASE_SRC (GST_OBJECT_PARENT (pad));
+
+ /* prepare subclass first */
+ if (active) {
+ GST_DEBUG_OBJECT (basesrc, "Activating in push mode");
+
+ if (G_UNLIKELY (!basesrc->can_activate_push))
+ goto no_push_activation;
+
+ if (G_UNLIKELY (!rsn_base_src_start (basesrc)))
+ goto error_start;
+
+ basesrc->priv->last_sent_eos = FALSE;
+
+ /* do initial seek, which will start the task */
+ GST_OBJECT_LOCK (basesrc);
+ event = basesrc->data.ABI.pending_seek;
+ basesrc->data.ABI.pending_seek = NULL;
+ GST_OBJECT_UNLOCK (basesrc);
+
+ /* no need to unlock anything, the task is certainly
+ * not running here. The perform seek code will start the task when
+ * finished. */
+ if (G_UNLIKELY (!rsn_base_src_perform_seek (basesrc, event, FALSE)))
+ goto seek_failed;
+
+ if (event)
+ gst_event_unref (event);
+ } else {
+ GST_DEBUG_OBJECT (basesrc, "Deactivating in push mode");
+ /* call the unlock function and stop the task */
+ if (G_UNLIKELY (!rsn_base_src_deactivate (basesrc, pad)))
+ goto deactivate_failed;
+
+ /* now we can stop the source */
+ if (G_UNLIKELY (!rsn_base_src_stop (basesrc)))
+ goto error_stop;
+ }
+ return TRUE;
+
+ /* ERRORS */
+no_push_activation:
+ {
+ GST_WARNING_OBJECT (basesrc, "Subclass disabled push-mode activation");
+ return FALSE;
+ }
+error_start:
+ {
+ GST_WARNING_OBJECT (basesrc, "Failed to start in push mode");
+ return FALSE;
+ }
+seek_failed:
+ {
+ GST_ERROR_OBJECT (basesrc, "Failed to perform initial seek");
+ rsn_base_src_stop (basesrc);
+ if (event)
+ gst_event_unref (event);
+ return FALSE;
+ }
+deactivate_failed:
+ {
+ GST_ERROR_OBJECT (basesrc, "Failed to deactivate in push mode");
+ return FALSE;
+ }
+error_stop:
+ {
+ GST_DEBUG_OBJECT (basesrc, "Failed to stop in push mode");
+ return FALSE;
+ }
+}
+
+static gboolean
+rsn_base_src_activate_pull (GstPad * pad, gboolean active)
+{
+ RsnBaseSrc *basesrc;
+
+ basesrc = GST_BASE_SRC (GST_OBJECT_PARENT (pad));
+
+ /* prepare subclass first */
+ if (active) {
+ GST_DEBUG_OBJECT (basesrc, "Activating in pull mode");
+ if (G_UNLIKELY (!rsn_base_src_start (basesrc)))
+ goto error_start;
+
+ /* if not random_access, we cannot operate in pull mode for now */
+ if (G_UNLIKELY (!rsn_base_src_check_get_range (basesrc)))
+ goto no_get_range;
+ } else {
+ GST_DEBUG_OBJECT (basesrc, "Deactivating in pull mode");
+ /* call the unlock function. We have no task to stop. */
+ if (G_UNLIKELY (!rsn_base_src_deactivate (basesrc, pad)))
+ goto deactivate_failed;
+
+ /* don't send EOS when going from PAUSED => READY when in pull mode */
+ basesrc->priv->last_sent_eos = TRUE;
+
+ if (G_UNLIKELY (!rsn_base_src_stop (basesrc)))
+ goto error_stop;
+ }
+ return TRUE;
+
+ /* ERRORS */
+error_start:
+ {
+ GST_ERROR_OBJECT (basesrc, "Failed to start in pull mode");
+ return FALSE;
+ }
+no_get_range:
+ {
+ GST_ERROR_OBJECT (basesrc, "Cannot operate in pull mode, stopping");
+ rsn_base_src_stop (basesrc);
+ return FALSE;
+ }
+deactivate_failed:
+ {
+ GST_ERROR_OBJECT (basesrc, "Failed to deactivate in pull mode");
+ return FALSE;
+ }
+error_stop:
+ {
+ GST_ERROR_OBJECT (basesrc, "Failed to stop in pull mode");
+ return FALSE;
+ }
+}
+
+static GstStateChangeReturn
+rsn_base_src_change_state (GstElement * element, GstStateChange transition)
+{
+ RsnBaseSrc *basesrc;
+ GstStateChangeReturn result;
+ gboolean no_preroll = FALSE;
+
+ basesrc = GST_BASE_SRC (element);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ GST_LIVE_LOCK (element);
+ basesrc->priv->latency = -1;
+ if (basesrc->is_live) {
+ no_preroll = TRUE;
+ basesrc->live_running = FALSE;
+ }
+ basesrc->priv->last_sent_eos = FALSE;
+ basesrc->priv->discont = TRUE;
+ GST_LIVE_UNLOCK (element);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ GST_LIVE_LOCK (element);
+ if (basesrc->is_live) {
+ gboolean start;
+
+ rsn_base_src_unlock_stop (basesrc);
+ /* for live sources we restart the timestamp correction */
+ basesrc->priv->latency = -1;
+ basesrc->live_running = TRUE;
+ GST_LIVE_SIGNAL (element);
+ /* have to restart the task in case it stopped because of the unlock when
+ * we went to PAUSED. Only do this if we operating in push mode. */
+ GST_OBJECT_LOCK (basesrc->srcpad);
+ start = (GST_PAD_ACTIVATE_MODE (basesrc->srcpad) == GST_ACTIVATE_PUSH);
+ GST_OBJECT_UNLOCK (basesrc->srcpad);
+ if (start)
+ gst_pad_start_task (basesrc->srcpad,
+ (GstTaskFunction) rsn_base_src_loop, basesrc->srcpad);
+ }
+ GST_LIVE_UNLOCK (element);
+ break;
+ default:
+ break;
+ }
+
+ if ((result =
+ GST_ELEMENT_CLASS (parent_class)->change_state (element,
+ transition)) == GST_STATE_CHANGE_FAILURE)
+ goto failure;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ GST_LIVE_LOCK (element);
+ if (basesrc->is_live) {
+ rsn_base_src_unlock (basesrc);
+ no_preroll = TRUE;
+ basesrc->live_running = FALSE;
+ }
+ GST_LIVE_UNLOCK (element);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ {
+ GstEvent **event_p;
+
+ /* FIXME, deprecate this behaviour, it is very dangerous.
+ * the prefered way of sending EOS downstream is by sending
+ * the EOS event to the element */
+ if (!basesrc->priv->last_sent_eos) {
+ GST_DEBUG_OBJECT (basesrc, "Sending EOS event");
+ gst_pad_push_event (basesrc->srcpad, gst_event_new_eos ());
+ basesrc->priv->last_sent_eos = TRUE;
+ }
+ event_p = &basesrc->data.ABI.pending_seek;
+ gst_event_replace (event_p, NULL);
+ event_p = &basesrc->priv->close_segment;
+ gst_event_replace (event_p, NULL);
+ event_p = &basesrc->priv->start_segment;
+ gst_event_replace (event_p, NULL);
+ break;
+ }
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ break;
+ default:
+ break;
+ }
+
+ if (no_preroll && result == GST_STATE_CHANGE_SUCCESS)
+ result = GST_STATE_CHANGE_NO_PREROLL;
+
+ return result;
+
+ /* ERRORS */
+failure:
+ {
+ GST_DEBUG_OBJECT (basesrc, "parent failed state change");
+ return result;
+ }
+}
diff --git a/ext/resindvd/rsnbasesrc.h b/ext/resindvd/rsnbasesrc.h
new file mode 100644
index 00000000..9365c20b
--- /dev/null
+++ b/ext/resindvd/rsnbasesrc.h
@@ -0,0 +1,257 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstbasesrc.h:
+ *
+ * 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.
+ */
+
+/*
+ *
+ * This is a temporary copy of GstBaseSrc/GstPushSrc for the resin
+ * DVD components, to work around a deadlock with source elements that
+ * send seeks to themselves.
+ *
+ */
+
+#ifndef __GST_BASE_SRC_H__
+#define __GST_BASE_SRC_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define RSN_TYPE_BASE_SRC (rsn_base_src_get_type())
+#define GST_BASE_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),RSN_TYPE_BASE_SRC,RsnBaseSrc))
+#define GST_BASE_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RSN_TYPE_BASE_SRC,RsnBaseSrcClass))
+#define GST_BASE_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), RSN_TYPE_BASE_SRC, RsnBaseSrcClass))
+#define GST_IS_BASE_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),RSN_TYPE_BASE_SRC))
+#define GST_IS_BASE_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RSN_TYPE_BASE_SRC))
+#define GST_BASE_SRC_CAST(obj) ((RsnBaseSrc *)(obj))
+
+/**
+ * RsnBaseSrcFlags:
+ * @GST_BASE_SRC_STARTED: has source been started
+ * @GST_BASE_SRC_FLAG_LAST: offset to define more flags
+ *
+ * The #GstElement flags that a basesrc element may have.
+ */
+typedef enum {
+ GST_BASE_SRC_STARTED = (GST_ELEMENT_FLAG_LAST << 0),
+ /* padding */
+ GST_BASE_SRC_FLAG_LAST = (GST_ELEMENT_FLAG_LAST << 2)
+} RsnBaseSrcFlags;
+
+typedef struct _RsnBaseSrc RsnBaseSrc;
+typedef struct _RsnBaseSrcClass RsnBaseSrcClass;
+typedef struct _RsnBaseSrcPrivate RsnBaseSrcPrivate;
+
+/**
+ * GST_BASE_SRC_PAD:
+ * @obj: base source instance
+ *
+ * Gives the pointer to the #GstPad object of the element.
+ */
+#define GST_BASE_SRC_PAD(obj) (GST_BASE_SRC_CAST (obj)->srcpad)
+
+
+/**
+ * RsnBaseSrc:
+ * @element: the parent element.
+ *
+ * The opaque #RsnBaseSrc data structure.
+ */
+struct _RsnBaseSrc {
+ GstElement element;
+
+ /*< protected >*/
+ GstPad *srcpad;
+
+ /* available to subclass implementations */
+ /* MT-protected (with LIVE_LOCK) */
+ GMutex *live_lock;
+ GCond *live_cond;
+ gboolean is_live;
+ gboolean live_running;
+
+ /* MT-protected (with LOCK) */
+ gint blocksize; /* size of buffers when operating push based */
+ gboolean can_activate_push; /* some scheduling properties */
+ GstActivateMode pad_mode;
+ gboolean seekable;
+ gboolean random_access;
+
+ GstClockID clock_id; /* for syncing */
+ GstClockTime end_time;
+
+ /* MT-protected (with STREAM_LOCK) */
+ GstSegment segment;
+ gboolean need_newsegment;
+
+ guint64 offset; /* current offset in the resource, unused */
+ guint64 size; /* total size of the resource, unused */
+
+ gint num_buffers;
+ gint num_buffers_left;
+
+ /*< private >*/
+ union {
+ struct {
+ /* FIXME: those fields should be moved into the private struct */
+ gboolean typefind;
+ gboolean running;
+ GstEvent *pending_seek;
+ } ABI;
+ gpointer _gst_reserved[GST_PADDING_LARGE-1];
+ } data;
+
+ RsnBaseSrcPrivate *priv;
+};
+
+/**
+ * RsnBaseSrcClass:
+ * @parent_class: Element parent class
+ * @get_caps: Called to get the caps to report
+ * @set_caps: Notify subclass of changed output caps
+ * @negotiate: Negotiated the caps with the peer.
+ * @newsegment: Generate and send a new_segment event (UNUSED)
+ * @start: Start processing. Subclasses should open resources and prepare
+ * to produce data.
+ * @stop: Stop processing. Subclasses should use this to close resources.
+ * @get_times: Given a buffer, return the start and stop time when it
+ * should be pushed out. The base class will sync on the clock using
+ * these times.
+ * @get_size: Return the total size of the resource, in the configured format.
+ * @is_seekable: Check if the source can seek
+ * @unlock: Unlock any pending access to the resource. Subclasses should
+ * unblock any blocked function ASAP
+ * @unlock_stop: Clear the previous unlock request. Subclasses should clear
+ * any state they set during unlock(), such as clearing command queues.
+ * @event: Override this to implement custom event handling.
+ * @create: Ask the subclass to create a buffer with offset and size.
+ * @do_seek: Perform seeking on the resource to the indicated segment.
+ * @prepare_seek_segment: Prepare the GstSegment that will be passed to the
+ * do_seek vmethod for executing a seek request. Sub-classes should override
+ * this if they support seeking in formats other than the configured native
+ * format. By default, it tries to convert the seek arguments to the
+ * configured native format and prepare a segment in that format.
+ * Since: 0.10.13
+ * @query: Handle a requested query.
+ * @check_get_range: Check whether the source would support pull-based
+ * operation if it were to be opened now. This vfunc is optional, but
+ * should be implemented if possible to avoid unnecessary start/stop
+ * cycles. The default implementation will open and close the resource
+ * to find out whether get_range is supported, and that is usually
+ * undesirable.
+ * @fixate: Called during negotation if caps need fixating. Implement instead of
+ * setting a fixate function on the source pad.
+ *
+ * Subclasses can override any of the available virtual methods or not, as
+ * needed. At the minimum, the @create method should be overridden to produce
+ * buffers.
+ */
+struct _RsnBaseSrcClass {
+ GstElementClass parent_class;
+
+ /*< public >*/
+ /* virtual methods for subclasses */
+
+ /* get caps from subclass */
+ GstCaps* (*get_caps) (RsnBaseSrc *src);
+ /* notify the subclass of new caps */
+ gboolean (*set_caps) (RsnBaseSrc *src, GstCaps *caps);
+
+ /* decide on caps */
+ gboolean (*negotiate) (RsnBaseSrc *src);
+
+ /* generate and send a newsegment (UNUSED) */
+ gboolean (*newsegment) (RsnBaseSrc *src);
+
+ /* start and stop processing, ideal for opening/closing the resource */
+ gboolean (*start) (RsnBaseSrc *src);
+ gboolean (*stop) (RsnBaseSrc *src);
+
+ /* given a buffer, return start and stop time when it should be pushed
+ * out. The base class will sync on the clock using these times. */
+ void (*get_times) (RsnBaseSrc *src, GstBuffer *buffer,
+ GstClockTime *start, GstClockTime *end);
+
+ /* get the total size of the resource in bytes */
+ gboolean (*get_size) (RsnBaseSrc *src, guint64 *size);
+
+ /* check if the resource is seekable */
+ gboolean (*is_seekable) (RsnBaseSrc *src);
+ /* unlock any pending access to the resource. subclasses should unlock
+ * any function ASAP. */
+ gboolean (*unlock) (RsnBaseSrc *src);
+
+ /* notify subclasses of an event */
+ gboolean (*event) (RsnBaseSrc *src, GstEvent *event);
+
+ /* ask the subclass to create a buffer with offset and size */
+ GstFlowReturn (*create) (RsnBaseSrc *src, guint64 offset, guint size,
+ GstBuffer **buf);
+
+ /* additions that change padding... */
+ /* notify subclasses of a seek */
+ gboolean (*do_seek) (RsnBaseSrc *src, GstSegment *segment);
+ /* notify subclasses of a query */
+ gboolean (*query) (RsnBaseSrc *src, GstQuery *query);
+
+ /* check whether the source would support pull-based operation if
+ * it were to be opened now. This vfunc is optional, but should be
+ * implemented if possible to avoid unnecessary start/stop cycles.
+ * The default implementation will open and close the resource to
+ * find out whether get_range is supported and that is usually
+ * undesirable. */
+ gboolean (*check_get_range) (RsnBaseSrc *src);
+
+ /* called if, in negotation, caps need fixating */
+ void (*fixate) (RsnBaseSrc *src, GstCaps *caps);
+
+ /* Clear any pending unlock request, as we succeeded in unlocking */
+ gboolean (*unlock_stop) (RsnBaseSrc *src);
+
+ /* Prepare the segment on which to perform do_seek(), converting to the
+ * current basesrc format. */
+ gboolean (*prepare_seek_segment) (RsnBaseSrc *src, GstEvent *seek,
+ GstSegment *segment);
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING_LARGE - 6];
+};
+
+GType rsn_base_src_get_type (void);
+
+GstFlowReturn rsn_base_src_wait_playing (RsnBaseSrc *src);
+
+void rsn_base_src_set_live (RsnBaseSrc *src, gboolean live);
+gboolean rsn_base_src_is_live (RsnBaseSrc *src);
+
+void rsn_base_src_set_format (RsnBaseSrc *src, GstFormat format);
+
+gboolean rsn_base_src_query_latency (RsnBaseSrc *src, gboolean * live,
+ GstClockTime * min_latency,
+ GstClockTime * max_latency);
+
+void rsn_base_src_set_do_timestamp (RsnBaseSrc *src, gboolean live);
+gboolean rsn_base_src_get_do_timestamp (RsnBaseSrc *src);
+
+G_END_DECLS
+
+#endif /* __GST_BASE_SRC_H__ */
diff --git a/ext/resindvd/rsnpushsrc.c b/ext/resindvd/rsnpushsrc.c
new file mode 100644
index 00000000..363b41d6
--- /dev/null
+++ b/ext/resindvd/rsnpushsrc.c
@@ -0,0 +1,101 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000,2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstpushsrc.c:
+ *
+ * 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.
+ */
+
+/*
+ *
+ * This is a temporary copy of GstBaseSrc/GstPushSrc for the resin
+ * DVD components, to work around a deadlock with source elements that
+ * send seeks to themselves.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "rsnpushsrc.h"
+#include <gst/gstmarshal.h>
+
+GST_DEBUG_CATEGORY_STATIC (rsn_push_src_debug);
+#define GST_CAT_DEFAULT rsn_push_src_debug
+
+#define _do_init(type) \
+ GST_DEBUG_CATEGORY_INIT (rsn_push_src_debug, "pushsrc", 0, \
+ "pushsrc element");
+
+GST_BOILERPLATE_FULL (RsnPushSrc, rsn_push_src, RsnBaseSrc, RSN_TYPE_BASE_SRC,
+ _do_init);
+
+static gboolean rsn_push_src_check_get_range (RsnBaseSrc * src);
+static GstFlowReturn rsn_push_src_create (RsnBaseSrc * bsrc, guint64 offset,
+ guint length, GstBuffer ** ret);
+
+static void
+rsn_push_src_base_init (gpointer g_class)
+{
+ /* nop */
+}
+
+static void
+rsn_push_src_class_init (RsnPushSrcClass * klass)
+{
+ RsnBaseSrcClass *gstbasesrc_class = (RsnBaseSrcClass *) klass;
+
+ gstbasesrc_class->create = GST_DEBUG_FUNCPTR (rsn_push_src_create);
+ gstbasesrc_class->check_get_range =
+ GST_DEBUG_FUNCPTR (rsn_push_src_check_get_range);
+}
+
+static void
+rsn_push_src_init (RsnPushSrc * pushsrc, RsnPushSrcClass * klass)
+{
+ /* nop */
+}
+
+static gboolean
+rsn_push_src_check_get_range (RsnBaseSrc * src)
+{
+ /* a pushsrc can by default never operate in pull mode override
+ * if you want something different. */
+ return FALSE;
+}
+
+static GstFlowReturn
+rsn_push_src_create (RsnBaseSrc * bsrc, guint64 offset, guint length,
+ GstBuffer ** ret)
+{
+ GstFlowReturn fret;
+ RsnPushSrc *src;
+ RsnPushSrcClass *pclass;
+
+ src = GST_PUSH_SRC (bsrc);
+ pclass = GST_PUSH_SRC_GET_CLASS (src);
+ if (pclass->create)
+ fret = pclass->create (src, ret);
+ else
+ fret = GST_FLOW_ERROR;
+
+ return fret;
+}
diff --git a/ext/resindvd/rsnpushsrc.h b/ext/resindvd/rsnpushsrc.h
new file mode 100644
index 00000000..eb50f031
--- /dev/null
+++ b/ext/resindvd/rsnpushsrc.h
@@ -0,0 +1,76 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstpushsrc.h:
+ *
+ * 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.
+ */
+
+/*
+ *
+ * This is a temporary copy of GstBaseSrc/GstPushSrc for the resin
+ * DVD components, to work around a deadlock with source elements that
+ * send seeks to themselves.
+ *
+ */
+#ifndef __GST_PUSH_SRC_H__
+#define __GST_PUSH_SRC_H__
+
+#include <gst/gst.h>
+#include "rsnbasesrc.h"
+
+G_BEGIN_DECLS
+
+#define RSN_TYPE_PUSH_SRC (rsn_push_src_get_type())
+#define GST_PUSH_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),RSN_TYPE_PUSH_SRC,RsnPushSrc))
+#define GST_PUSH_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RSN_TYPE_PUSH_SRC,RsnPushSrcClass))
+#define GST_PUSH_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), RSN_TYPE_PUSH_SRC, RsnPushSrcClass))
+#define GST_IS_PUSH_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),RSN_TYPE_PUSH_SRC))
+#define GST_IS_PUSH_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RSN_TYPE_PUSH_SRC))
+
+typedef struct _RsnPushSrc RsnPushSrc;
+typedef struct _RsnPushSrcClass RsnPushSrcClass;
+
+/**
+ * RsnPushSrc:
+ * @parent: the parent base source object.
+ *
+ * The opaque #RsnPushSrc data structure.
+ */
+struct _RsnPushSrc {
+ RsnBaseSrc parent;
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+struct _RsnPushSrcClass {
+ RsnBaseSrcClass parent_class;
+
+ /* ask the subclass to create a buffer */
+ GstFlowReturn (*create) (RsnPushSrc *src, GstBuffer **buf);
+
+ /*< private >*/
+ gpointer _gst_reserved[GST_PADDING];
+};
+
+GType rsn_push_src_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GST_PUSH_SRC_H__ */
diff --git a/ext/resindvd/rsnstreamselector.c b/ext/resindvd/rsnstreamselector.c
new file mode 100644
index 00000000..b422ac38
--- /dev/null
+++ b/ext/resindvd/rsnstreamselector.c
@@ -0,0 +1,784 @@
+/* GStreamer
+ * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com>
+ * Copyright (C) 2007 Wim Taymans <wim.taymans@gmail.com>
+ *
+ * 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 <string.h>
+
+#include "rsnstreamselector.h"
+
+GST_DEBUG_CATEGORY_STATIC (stream_selector_debug);
+#define GST_CAT_DEFAULT stream_selector_debug
+
+static const GstElementDetails rsn_stream_selector_details =
+GST_ELEMENT_DETAILS ("StreamSelector",
+ "Generic",
+ "N-to-1 input stream_selectoring",
+ "Julien Moutte <julien@moutte.net>\n"
+ "Ronald S. Bultje <rbultje@ronald.bitfreak.net>\n"
+ "Jan Schmidt <thaytan@mad.scientist.com>\n"
+ "Wim Taymans <wim.taymans@gmail.com>");
+
+static GstStaticPadTemplate rsn_stream_selector_sink_factory =
+GST_STATIC_PAD_TEMPLATE ("sink%d",
+ GST_PAD_SINK,
+ GST_PAD_REQUEST,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate rsn_stream_selector_src_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+enum
+{
+ PROP_0,
+ PROP_N_PADS,
+ PROP_ACTIVE_PAD,
+ PROP_LAST
+};
+
+static gboolean rsn_stream_selector_is_active_sinkpad (RsnStreamSelector * sel,
+ GstPad * pad);
+static GstPad *rsn_stream_selector_activate_sinkpad (RsnStreamSelector * sel,
+ GstPad * pad);
+static GstPad *rsn_stream_selector_get_linked_pad (GstPad * pad,
+ gboolean strict);
+
+#define RSN_TYPE_SELECTOR_PAD \
+ (gst_selector_pad_get_type())
+#define GST_SELECTOR_PAD(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), RSN_TYPE_SELECTOR_PAD, RsnSelectorPad))
+#define GST_SELECTOR_PAD_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), RSN_TYPE_SELECTOR_PAD, RsnSelectorPadClass))
+#define RSN_IS_SELECTOR_PAD(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RSN_TYPE_SELECTOR_PAD))
+#define RSN_IS_SELECTOR_PAD_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), RSN_TYPE_SELECTOR_PAD))
+#define GST_SELECTOR_PAD_CAST(obj) \
+ ((RsnSelectorPad *)(obj))
+
+typedef struct _RsnSelectorPad RsnSelectorPad;
+typedef struct _RsnSelectorPadClass RsnSelectorPadClass;
+
+struct _RsnSelectorPad
+{
+ GstPad parent;
+
+ gboolean active;
+ gboolean eos;
+ gboolean segment_pending;
+ GstSegment segment;
+ GstTagList *tags;
+};
+
+struct _RsnSelectorPadClass
+{
+ GstPadClass parent;
+};
+
+static void gst_selector_pad_class_init (RsnSelectorPadClass * klass);
+static void gst_selector_pad_init (RsnSelectorPad * pad);
+static void gst_selector_pad_finalize (GObject * object);
+
+static void gst_selector_pad_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec);
+
+static GstPadClass *selector_pad_parent_class = NULL;
+
+static void gst_selector_pad_reset (RsnSelectorPad * pad);
+static gboolean gst_selector_pad_event (GstPad * pad, GstEvent * event);
+static GstCaps *gst_selector_pad_getcaps (GstPad * pad);
+static GList *gst_selector_pad_get_linked_pads (GstPad * pad);
+static GstFlowReturn gst_selector_pad_chain (GstPad * pad, GstBuffer * buf);
+static GstFlowReturn gst_selector_pad_bufferalloc (GstPad * pad,
+ guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
+
+enum
+{
+ PROP_PAD_0,
+ PROP_PAD_TAGS,
+ PROP_PAD_ACTIVE,
+ PROP_PAD_LAST
+};
+
+static GType
+gst_selector_pad_get_type (void)
+{
+ static GType selector_pad_type = 0;
+
+ if (!selector_pad_type) {
+ static const GTypeInfo selector_pad_info = {
+ sizeof (RsnSelectorPadClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gst_selector_pad_class_init,
+ NULL,
+ NULL,
+ sizeof (RsnSelectorPad),
+ 0,
+ (GInstanceInitFunc) gst_selector_pad_init,
+ };
+
+ selector_pad_type =
+ g_type_register_static (GST_TYPE_PAD, "RsnSelectorPad",
+ &selector_pad_info, 0);
+ }
+ return selector_pad_type;
+}
+
+static void
+gst_selector_pad_class_init (RsnSelectorPadClass * klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = (GObjectClass *) klass;
+
+ selector_pad_parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->finalize = gst_selector_pad_finalize;
+ gobject_class->get_property =
+ GST_DEBUG_FUNCPTR (gst_selector_pad_get_property);
+
+ g_object_class_install_property (gobject_class, PROP_PAD_TAGS,
+ g_param_spec_boxed ("tags", "Tags",
+ "The currently active tags on the pad", GST_TYPE_TAG_LIST,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_PAD_ACTIVE,
+ g_param_spec_boolean ("active", "Active",
+ "If the pad is currently active", FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gst_selector_pad_init (RsnSelectorPad * pad)
+{
+ gst_selector_pad_reset (pad);
+}
+
+static void
+gst_selector_pad_finalize (GObject * object)
+{
+ RsnSelectorPad *pad;
+
+ pad = GST_SELECTOR_PAD_CAST (object);
+
+ if (pad->tags)
+ gst_tag_list_free (pad->tags);
+
+ G_OBJECT_CLASS (selector_pad_parent_class)->finalize (object);
+}
+
+static void
+gst_selector_pad_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec)
+{
+ RsnSelectorPad *pad;
+
+ pad = GST_SELECTOR_PAD (object);
+
+ switch (prop_id) {
+ case PROP_PAD_TAGS:
+ GST_OBJECT_LOCK (object);
+ g_value_set_boxed (value, pad->tags);
+ GST_OBJECT_UNLOCK (object);
+ break;
+ case PROP_PAD_ACTIVE:
+ {
+ RsnStreamSelector *sel;
+
+ sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad));
+ g_value_set_boolean (value, rsn_stream_selector_is_active_sinkpad (sel,
+ GST_PAD_CAST (pad)));
+ gst_object_unref (sel);
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_selector_pad_reset (RsnSelectorPad * pad)
+{
+ pad->active = FALSE;
+ pad->eos = FALSE;
+ gst_segment_init (&pad->segment, GST_FORMAT_UNDEFINED);
+}
+
+/* strictly get the linked pad from the sinkpad. If the pad is active we return
+ * the srcpad else we return NULL */
+static GList *
+gst_selector_pad_get_linked_pads (GstPad * pad)
+{
+ GstPad *otherpad;
+
+ otherpad = rsn_stream_selector_get_linked_pad (pad, TRUE);
+ if (!otherpad)
+ return NULL;
+
+ /* need to drop the ref, internal linked pads is not MT safe */
+ gst_object_unref (otherpad);
+
+ return g_list_append (NULL, otherpad);
+}
+
+static gboolean
+gst_selector_pad_event (GstPad * pad, GstEvent * event)
+{
+ gboolean res = TRUE;
+ gboolean forward = TRUE;
+ RsnStreamSelector *sel;
+ RsnSelectorPad *selpad;
+ GstPad *active_sinkpad;
+
+ sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad));
+ selpad = GST_SELECTOR_PAD_CAST (pad);
+
+ /* only forward if we are dealing with the active sinkpad */
+ active_sinkpad = rsn_stream_selector_activate_sinkpad (sel, pad);
+ forward = (active_sinkpad == pad);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_FLUSH_STOP:
+ gst_selector_pad_reset (selpad);
+ break;
+ case GST_EVENT_NEWSEGMENT:
+ {
+ gboolean update;
+ GstFormat format;
+ gdouble rate, arate;
+ gint64 start, stop, time;
+
+ gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
+ &start, &stop, &time);
+
+ GST_DEBUG_OBJECT (selpad,
+ "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
+ "format %d, "
+ "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
+ G_GINT64_FORMAT, update, rate, arate, format, start, stop, time);
+
+ gst_segment_set_newsegment_full (&selpad->segment, update,
+ rate, arate, format, start, stop, time);
+ /* if we are not going to forward the segment, mark the segment as
+ * pending */
+ if (!forward)
+ selpad->segment_pending = TRUE;
+ break;
+ }
+ case GST_EVENT_TAG:
+ {
+ GstTagList *tags;
+
+ GST_OBJECT_LOCK (selpad);
+ if (selpad->tags)
+ gst_tag_list_free (selpad->tags);
+ gst_event_parse_tag (event, &tags);
+ if (tags)
+ tags = gst_tag_list_copy (tags);
+ selpad->tags = tags;
+ GST_DEBUG_OBJECT (sel, "received tags %" GST_PTR_FORMAT, selpad->tags);
+ GST_OBJECT_UNLOCK (selpad);
+ break;
+ }
+ case GST_EVENT_EOS:
+ selpad->eos = TRUE;
+ break;
+ default:
+ break;
+ }
+ if (forward)
+ res = gst_pad_push_event (sel->srcpad, event);
+ else
+ gst_event_unref (event);
+
+ gst_object_unref (sel);
+
+ return res;
+}
+
+static GstCaps *
+gst_selector_pad_getcaps (GstPad * pad)
+{
+ RsnStreamSelector *sel;
+ GstCaps *caps;
+
+ sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad));
+
+ GST_DEBUG_OBJECT (sel, "Getting caps of srcpad peer");
+ caps = gst_pad_peer_get_caps (sel->srcpad);
+ if (caps == NULL)
+ caps = gst_caps_new_any ();
+
+ gst_object_unref (sel);
+
+ return caps;
+}
+
+static GstFlowReturn
+gst_selector_pad_bufferalloc (GstPad * pad, guint64 offset,
+ guint size, GstCaps * caps, GstBuffer ** buf)
+{
+ RsnStreamSelector *sel;
+ GstFlowReturn result;
+ GstPad *active_sinkpad;
+
+ sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad));
+
+ active_sinkpad = rsn_stream_selector_activate_sinkpad (sel, pad);
+
+ /* Fallback allocation for buffers from pads except the selected one */
+ if (pad != active_sinkpad) {
+ GST_DEBUG_OBJECT (sel,
+ "Pad %s:%s is not selected. Performing fallback allocation",
+ GST_DEBUG_PAD_NAME (pad));
+
+ *buf = NULL;
+ result = GST_FLOW_OK;
+ } else {
+ result = gst_pad_alloc_buffer (sel->srcpad, offset, size, caps, buf);
+
+ /* FIXME: HACK. If buffer alloc returns not-linked, perform a fallback
+ * allocation. This should NOT be necessary, because playbin should
+ * properly block the source pad from running until it's finished hooking
+ * everything up, but playbin needs refactoring first. */
+ if (result == GST_FLOW_NOT_LINKED) {
+ GST_DEBUG_OBJECT (sel,
+ "No peer pad yet - performing fallback allocation for pad %s:%s",
+ GST_DEBUG_PAD_NAME (pad));
+
+ *buf = NULL;
+ result = GST_FLOW_OK;
+ }
+ }
+
+ gst_object_unref (sel);
+
+ return result;
+}
+
+static GstFlowReturn
+gst_selector_pad_chain (GstPad * pad, GstBuffer * buf)
+{
+ RsnStreamSelector *sel;
+ GstFlowReturn res;
+ GstPad *active_sinkpad;
+ RsnSelectorPad *selpad;
+ GstClockTime timestamp;
+ GstSegment *seg;
+
+ sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad));
+ selpad = GST_SELECTOR_PAD_CAST (pad);
+ seg = &selpad->segment;
+
+ active_sinkpad = rsn_stream_selector_activate_sinkpad (sel, pad);
+
+ timestamp = GST_BUFFER_TIMESTAMP (buf);
+ if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
+ GST_DEBUG_OBJECT (sel, "received timestamp %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (timestamp));
+ gst_segment_set_last_stop (seg, seg->format, timestamp);
+ }
+
+ /* Ignore buffers from pads except the selected one */
+ if (pad != active_sinkpad)
+ goto ignore;
+
+ /* if we have a pending segment, push it out now */
+ if (selpad->segment_pending) {
+ gst_pad_push_event (sel->srcpad, gst_event_new_new_segment_full (FALSE,
+ seg->rate, seg->applied_rate, seg->format, seg->start, seg->stop,
+ seg->time));
+
+ selpad->segment_pending = FALSE;
+ }
+
+ /* forward */
+ GST_DEBUG_OBJECT (sel, "Forwarding buffer %p from pad %s:%s", buf,
+ GST_DEBUG_PAD_NAME (pad));
+ res = gst_pad_push (sel->srcpad, buf);
+done:
+ gst_object_unref (sel);
+ return res;
+ /* dropped buffers */
+ignore:
+ {
+ GST_DEBUG_OBJECT (sel, "Ignoring buffer %p from pad %s:%s",
+ buf, GST_DEBUG_PAD_NAME (pad));
+ gst_buffer_unref (buf);
+ res = GST_FLOW_NOT_LINKED;
+ goto done;
+ }
+}
+
+static void rsn_stream_selector_dispose (GObject * object);
+static void rsn_stream_selector_finalize (GObject * object);
+
+static void rsn_stream_selector_init (RsnStreamSelector * sel);
+static void rsn_stream_selector_base_init (RsnStreamSelectorClass * klass);
+static void rsn_stream_selector_class_init (RsnStreamSelectorClass * klass);
+
+static void rsn_stream_selector_set_property (GObject * object,
+ guint prop_id, const GValue * value, GParamSpec * pspec);
+static void rsn_stream_selector_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec);
+
+static GstPad *rsn_stream_selector_request_new_pad (GstElement * element,
+ GstPadTemplate * templ, const gchar * unused);
+static void rsn_stream_selector_release_pad (GstElement * element,
+ GstPad * pad);
+static GList *rsn_stream_selector_get_linked_pads (GstPad * pad);
+static GstCaps *rsn_stream_selector_getcaps (GstPad * pad);
+
+static GstElementClass *parent_class = NULL;
+
+GType
+rsn_stream_selector_get_type (void)
+{
+ static GType stream_selector_type = 0;
+
+ if (!stream_selector_type) {
+ static const GTypeInfo stream_selector_info = {
+ sizeof (RsnStreamSelectorClass),
+ (GBaseInitFunc) rsn_stream_selector_base_init,
+ NULL,
+ (GClassInitFunc) rsn_stream_selector_class_init,
+ NULL,
+ NULL,
+ sizeof (RsnStreamSelector),
+ 0,
+ (GInstanceInitFunc) rsn_stream_selector_init,
+ };
+ stream_selector_type =
+ g_type_register_static (GST_TYPE_ELEMENT,
+ "RsnStreamSelector", &stream_selector_info, 0);
+ GST_DEBUG_CATEGORY_INIT (stream_selector_debug,
+ "streamselector", 0, "A stream-selector element");
+ }
+
+ return stream_selector_type;
+}
+
+static void
+rsn_stream_selector_base_init (RsnStreamSelectorClass * klass)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+ gst_element_class_set_details (element_class, &rsn_stream_selector_details);
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&rsn_stream_selector_sink_factory));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&rsn_stream_selector_src_factory));
+}
+
+static void
+rsn_stream_selector_class_init (RsnStreamSelectorClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class->dispose = rsn_stream_selector_dispose;
+ gobject_class->finalize = rsn_stream_selector_finalize;
+
+ gobject_class->set_property =
+ GST_DEBUG_FUNCPTR (rsn_stream_selector_set_property);
+ gobject_class->get_property =
+ GST_DEBUG_FUNCPTR (rsn_stream_selector_get_property);
+
+ g_object_class_install_property (gobject_class, PROP_N_PADS,
+ g_param_spec_uint ("n-pads", "Number of Pads",
+ "The number of sink pads", 0, G_MAXUINT, 0,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD,
+ g_param_spec_object ("active-pad", "Active Pad",
+ "The currently active sink pad", GST_TYPE_PAD,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gstelement_class->request_new_pad = rsn_stream_selector_request_new_pad;
+ gstelement_class->release_pad = rsn_stream_selector_release_pad;
+}
+
+static void
+rsn_stream_selector_init (RsnStreamSelector * sel)
+{
+ sel->srcpad = gst_pad_new ("src", GST_PAD_SRC);
+ gst_pad_set_internal_link_function (sel->srcpad,
+ GST_DEBUG_FUNCPTR (rsn_stream_selector_get_linked_pads));
+ gst_pad_set_getcaps_function (sel->srcpad,
+ GST_DEBUG_FUNCPTR (rsn_stream_selector_getcaps));
+ gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad);
+ /* sinkpad management */
+ sel->padcount = 0;
+ sel->active_sinkpad = NULL;
+ gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED);
+}
+
+static void
+rsn_stream_selector_dispose (GObject * object)
+{
+ RsnStreamSelector *sel = RSN_STREAM_SELECTOR (object);
+
+ if (sel->active_sinkpad) {
+ gst_object_unref (sel->active_sinkpad);
+ sel->active_sinkpad = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+rsn_stream_selector_finalize (GObject * object)
+{
+ RsnStreamSelector *sel;
+
+ sel = RSN_STREAM_SELECTOR (object);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+rsn_stream_selector_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ RsnStreamSelector *sel = RSN_STREAM_SELECTOR (object);
+
+ switch (prop_id) {
+ case PROP_ACTIVE_PAD:
+ {
+ GstPad *pad = NULL;
+ GstPad **active_pad_p;
+
+ pad = g_value_get_object (value);
+
+ GST_OBJECT_LOCK (object);
+ if (pad != sel->active_sinkpad) {
+ RsnSelectorPad *selpad;
+
+ selpad = GST_SELECTOR_PAD_CAST (pad);
+ /* we can only activate pads that have data received */
+ if (selpad && !selpad->active) {
+ GST_DEBUG_OBJECT (sel, "No data received on pad %" GST_PTR_FORMAT,
+ pad);
+ } else {
+ active_pad_p = &sel->active_sinkpad;
+ gst_object_replace ((GstObject **) active_pad_p,
+ GST_OBJECT_CAST (pad));
+ GST_DEBUG_OBJECT (sel, "New active pad is %" GST_PTR_FORMAT,
+ sel->active_sinkpad);
+ }
+ }
+ GST_OBJECT_UNLOCK (object);
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+rsn_stream_selector_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ RsnStreamSelector *sel = RSN_STREAM_SELECTOR (object);
+
+ switch (prop_id) {
+ case PROP_N_PADS:
+ GST_OBJECT_LOCK (object);
+ g_value_set_uint (value, sel->n_pads);
+ GST_OBJECT_UNLOCK (object);
+ break;
+ case PROP_ACTIVE_PAD:{
+ GST_OBJECT_LOCK (object);
+ g_value_set_object (value, sel->active_sinkpad);
+ GST_OBJECT_UNLOCK (object);
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GstPad *
+rsn_stream_selector_get_linked_pad (GstPad * pad, gboolean strict)
+{
+ RsnStreamSelector *sel;
+ GstPad *otherpad = NULL;
+
+ sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad));
+
+ GST_OBJECT_LOCK (sel);
+ if (pad == sel->srcpad)
+ otherpad = sel->active_sinkpad;
+ else if (pad == sel->active_sinkpad || !strict)
+ otherpad = sel->srcpad;
+ if (otherpad)
+ gst_object_ref (otherpad);
+ GST_OBJECT_UNLOCK (sel);
+ gst_object_unref (sel);
+ return otherpad;
+}
+
+static GstCaps *
+rsn_stream_selector_getcaps (GstPad * pad)
+{
+ GstPad *otherpad;
+ GstObject *parent;
+ GstCaps *caps;
+
+ otherpad = rsn_stream_selector_get_linked_pad (pad, FALSE);
+ parent = gst_object_get_parent (GST_OBJECT (pad));
+ if (!otherpad) {
+ GST_DEBUG_OBJECT (parent,
+ "Pad %s:%s not linked, returning ANY", GST_DEBUG_PAD_NAME (pad));
+ caps = gst_caps_new_any ();
+ } else {
+ GST_DEBUG_OBJECT (parent,
+ "Pad %s:%s is linked (to %s:%s), returning peer caps",
+ GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (otherpad));
+ /* if the peer has caps, use those. If the pad is not linked, this function
+ * returns NULL and we return ANY */
+ if (!(caps = gst_pad_peer_get_caps (otherpad)))
+ caps = gst_caps_new_any ();
+ gst_object_unref (otherpad);
+ }
+
+ gst_object_unref (parent);
+ return caps;
+}
+
+/* check if the pad is the active sinkpad */
+static gboolean
+rsn_stream_selector_is_active_sinkpad (RsnStreamSelector * sel, GstPad * pad)
+{
+ RsnSelectorPad *selpad;
+ gboolean res;
+
+ selpad = GST_SELECTOR_PAD_CAST (pad);
+
+ GST_OBJECT_LOCK (sel);
+ res = (pad == sel->active_sinkpad);
+ GST_OBJECT_UNLOCK (sel);
+
+ return res;
+}
+
+/* Get or create the active sinkpad */
+static GstPad *
+rsn_stream_selector_activate_sinkpad (RsnStreamSelector * sel, GstPad * pad)
+{
+ GstPad *active_sinkpad;
+ RsnSelectorPad *selpad;
+
+ selpad = GST_SELECTOR_PAD_CAST (pad);
+
+ GST_OBJECT_LOCK (sel);
+ selpad->active = TRUE;
+ active_sinkpad = sel->active_sinkpad;
+ if (active_sinkpad == NULL) {
+ /* first pad we get an alloc on becomes the activated pad by default */
+ active_sinkpad = sel->active_sinkpad = gst_object_ref (pad);
+ GST_DEBUG_OBJECT (sel, "Activating pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+ }
+ GST_OBJECT_UNLOCK (sel);
+
+ return active_sinkpad;
+}
+
+static GList *
+rsn_stream_selector_get_linked_pads (GstPad * pad)
+{
+ GstPad *otherpad;
+
+ otherpad = rsn_stream_selector_get_linked_pad (pad, TRUE);
+ if (!otherpad)
+ return NULL;
+ /* need to drop the ref, internal linked pads is not MT safe */
+ gst_object_unref (otherpad);
+ return g_list_append (NULL, otherpad);
+}
+
+static GstPad *
+rsn_stream_selector_request_new_pad (GstElement * element,
+ GstPadTemplate * templ, const gchar * unused)
+{
+ RsnStreamSelector *sel;
+ gchar *name = NULL;
+ GstPad *sinkpad = NULL;
+
+ sel = RSN_STREAM_SELECTOR (element);
+ g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
+ GST_LOG_OBJECT (sel, "Creating new pad %d", sel->padcount);
+ GST_OBJECT_LOCK (sel);
+ name = g_strdup_printf ("sink%d", sel->padcount++);
+ sinkpad = g_object_new (RSN_TYPE_SELECTOR_PAD,
+ "name", name, "direction", templ->direction, "template", templ, NULL);
+ g_free (name);
+ sel->n_pads++;
+ GST_OBJECT_UNLOCK (sel);
+
+ gst_pad_set_event_function (sinkpad,
+ GST_DEBUG_FUNCPTR (gst_selector_pad_event));
+ gst_pad_set_getcaps_function (sinkpad,
+ GST_DEBUG_FUNCPTR (gst_selector_pad_getcaps));
+ gst_pad_set_chain_function (sinkpad,
+ GST_DEBUG_FUNCPTR (gst_selector_pad_chain));
+ gst_pad_set_internal_link_function (sinkpad,
+ GST_DEBUG_FUNCPTR (gst_selector_pad_get_linked_pads));
+ gst_pad_set_bufferalloc_function (sinkpad,
+ GST_DEBUG_FUNCPTR (gst_selector_pad_bufferalloc));
+
+ gst_pad_set_active (sinkpad, TRUE);
+ gst_element_add_pad (GST_ELEMENT (sel), sinkpad);
+ return sinkpad;
+}
+
+static void
+rsn_stream_selector_release_pad (GstElement * element, GstPad * pad)
+{
+ RsnStreamSelector *sel;
+
+ sel = RSN_STREAM_SELECTOR (element);
+ GST_LOG_OBJECT (sel, "Releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+ GST_OBJECT_LOCK (sel);
+ /* if the pad was the active pad, makes us select a new one */
+ if (sel->active_sinkpad == pad) {
+ GST_DEBUG_OBJECT (sel, "Deactivating pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+ sel->active_sinkpad = NULL;
+ }
+ sel->n_pads--;
+ GST_OBJECT_UNLOCK (sel);
+
+ gst_pad_set_active (pad, FALSE);
+ gst_element_remove_pad (GST_ELEMENT (sel), pad);
+}
diff --git a/ext/resindvd/rsnstreamselector.h b/ext/resindvd/rsnstreamselector.h
new file mode 100644
index 00000000..aa51699b
--- /dev/null
+++ b/ext/resindvd/rsnstreamselector.h
@@ -0,0 +1,62 @@
+/* GStreamer
+ * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * 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 __RSN_STREAM_SELECTOR_H__
+#define __RSN_STREAM_SELECTOR_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define RSN_TYPE_STREAM_SELECTOR \
+ (rsn_stream_selector_get_type())
+#define RSN_STREAM_SELECTOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), RSN_TYPE_STREAM_SELECTOR, RsnStreamSelector))
+#define RSN_STREAM_SELECTOR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), RSN_TYPE_STREAM_SELECTOR, RsnStreamSelectorClass))
+#define RSN_IS_STREAM_SELECTOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RSN_TYPE_STREAM_SELECTOR))
+#define RSN_IS_STREAM_SELECTOR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), RSN_TYPE_STREAM_SELECTOR))
+
+typedef struct _RsnStreamSelector RsnStreamSelector;
+typedef struct _RsnStreamSelectorClass RsnStreamSelectorClass;
+
+struct _RsnStreamSelector {
+ GstElement element;
+
+ GstPad *srcpad;
+
+ GstPad *active_sinkpad;
+ guint n_pads;
+ guint padcount;
+
+ GstSegment segment;
+};
+
+struct _RsnStreamSelectorClass {
+ GstElementClass parent_class;
+};
+
+GType rsn_stream_selector_get_type (void);
+
+G_END_DECLS
+
+#endif /* __RSN_STREAM_SELECTOR_H__ */
diff --git a/gst/dvdspu/gstdvdspu.c b/gst/dvdspu/gstdvdspu.c
index c16743bd..680d9101 100644
--- a/gst/dvdspu/gstdvdspu.c
+++ b/gst/dvdspu/gstdvdspu.c
@@ -395,12 +395,22 @@ gst_dvd_spu_video_event (GstPad * pad, GstEvent * event)
const GstStructure *structure = gst_event_get_structure (event);
const char *event_type;
+ if (structure == NULL) {
+ res = gst_pad_event_default (pad, event);
+ break;
+ }
+
if (!gst_structure_has_name (structure, "application/x-gst-dvd")) {
res = gst_pad_event_default (pad, event);
break;
}
event_type = gst_structure_get_string (structure, "event");
+ if (event_type == NULL) {
+ res = gst_pad_event_default (pad, event);
+ break;
+ }
+
GST_DEBUG_OBJECT (dvdspu,
"DVD event of type %s on video pad", event_type);