summaryrefslogtreecommitdiff
path: root/gst/isomp4
diff options
context:
space:
mode:
Diffstat (limited to 'gst/isomp4')
-rw-r--r--gst/isomp4/Makefile.am4
-rwxr-xr-x[-rw-r--r--]gst/isomp4/fourcc.h17
-rwxr-xr-x[-rw-r--r--]gst/isomp4/qtdemux.c3861
-rwxr-xr-x[-rw-r--r--]gst/isomp4/qtdemux.h189
-rw-r--r--gst/isomp4/qtdemux_dump.c64
-rw-r--r--gst/isomp4/qtdemux_dump.h8
-rwxr-xr-x[-rw-r--r--]gst/isomp4/qtdemux_fourcc.h29
-rwxr-xr-x[-rw-r--r--]gst/isomp4/qtdemux_types.c16
8 files changed, 3745 insertions, 443 deletions
diff --git a/gst/isomp4/Makefile.am b/gst/isomp4/Makefile.am
index 010e09c..d7be13e 100644
--- a/gst/isomp4/Makefile.am
+++ b/gst/isomp4/Makefile.am
@@ -1,7 +1,7 @@
plugin_LTLIBRARIES = libgstisomp4.la
-libgstisomp4_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS)
+libgstisomp4_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(DRM_CLIENT_CFLAGS) $(DRM_TRUSTED_CFLAGS)
libgstisomp4_la_LIBADD = \
$(GST_PLUGINS_BASE_LIBS) \
-lgstriff-@GST_MAJORMINOR@ \
@@ -9,7 +9,7 @@ libgstisomp4_la_LIBADD = \
-lgstrtp-@GST_MAJORMINOR@ \
-lgsttag-@GST_MAJORMINOR@ \
-lgstpbutils-@GST_MAJORMINOR@ \
- $(GST_BASE_LIBS) $(GST_LIBS) $(ZLIB_LIBS)
+ $(GST_BASE_LIBS) $(GST_LIBS) $(ZLIB_LIBS) $(DRM_CLIENT_LIBS) $(DRM_TRUSTED_LIBS)
libgstisomp4_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS}
libgstisomp4_la_SOURCES = isomp4-plugin.c gstrtpxqtdepay.c \
qtdemux.c qtdemux_types.c qtdemux_dump.c qtdemux_lang.c \
diff --git a/gst/isomp4/fourcc.h b/gst/isomp4/fourcc.h
index 295d17e..eb36cb5 100644..100755
--- a/gst/isomp4/fourcc.h
+++ b/gst/isomp4/fourcc.h
@@ -55,14 +55,10 @@ G_BEGIN_DECLS
#define FOURCC_clip GST_MAKE_FOURCC('c','l','i','p')
#define FOURCC_trak GST_MAKE_FOURCC('t','r','a','k')
#define FOURCC_udta GST_MAKE_FOURCC('u','d','t','a')
-#define FOURCC_ctab GST_MAKE_FOURCC('c','t','a','b')
#define FOURCC_tkhd GST_MAKE_FOURCC('t','k','h','d')
#define FOURCC_crgn GST_MAKE_FOURCC('c','r','g','n')
-#define FOURCC_matt GST_MAKE_FOURCC('m','a','t','t')
-#define FOURCC_kmat GST_MAKE_FOURCC('k','m','a','t')
#define FOURCC_edts GST_MAKE_FOURCC('e','d','t','s')
#define FOURCC_elst GST_MAKE_FOURCC('e','l','s','t')
-#define FOURCC_load GST_MAKE_FOURCC('l','o','a','d')
#define FOURCC_tref GST_MAKE_FOURCC('t','r','e','f')
#define FOURCC_imap GST_MAKE_FOURCC('i','m','a','p')
#define FOURCC___in GST_MAKE_FOURCC(' ',' ','i','n')
@@ -78,7 +74,6 @@ G_BEGIN_DECLS
#define FOURCC_smhd GST_MAKE_FOURCC('s','m','h','d')
#define FOURCC_gmhd GST_MAKE_FOURCC('g','m','h','d')
#define FOURCC_hmhd GST_MAKE_FOURCC('h','m','h','d')
-#define FOURCC_gmin GST_MAKE_FOURCC('g','m','i','n')
#define FOURCC_dinf GST_MAKE_FOURCC('d','i','n','f')
#define FOURCC_dref GST_MAKE_FOURCC('d','r','e','f')
#define FOURCC_stbl GST_MAKE_FOURCC('s','t','b','l')
@@ -93,7 +88,6 @@ G_BEGIN_DECLS
#define FOURCC_strm GST_MAKE_FOURCC('s','t','r','m')
#define FOURCC_rtsp GST_MAKE_FOURCC('r','t','s','p')
#define FOURCC_co64 GST_MAKE_FOURCC('c','o','6','4')
-#define FOURCC_cmov GST_MAKE_FOURCC('c','m','o','v')
#define FOURCC_dcom GST_MAKE_FOURCC('d','c','o','m')
#define FOURCC_cmvd GST_MAKE_FOURCC('c','m','v','d')
#define FOURCC_hint GST_MAKE_FOURCC('h','i','n','t')
@@ -134,13 +128,7 @@ G_BEGIN_DECLS
#define FOURCC_free GST_MAKE_FOURCC('f','r','e','e')
#define FOURCC_data GST_MAKE_FOURCC('d','a','t','a')
#define FOURCC_SVQ3 GST_MAKE_FOURCC('S','V','Q','3')
-#define FOURCC_rmra GST_MAKE_FOURCC('r','m','r','a')
-#define FOURCC_rmda GST_MAKE_FOURCC('r','m','d','a')
-#define FOURCC_rdrf GST_MAKE_FOURCC('r','d','r','f')
#define FOURCC__gen GST_MAKE_FOURCC(0xa9, 'g', 'e', 'n')
-#define FOURCC_rmdr GST_MAKE_FOURCC('r','m','d','r')
-#define FOURCC_rmvc GST_MAKE_FOURCC('r','m','v','c')
-#define FOURCC_qtim GST_MAKE_FOURCC('q','t','i','m')
#define FOURCC_drms GST_MAKE_FOURCC('d','r','m','s')
#define FOURCC_avc1 GST_MAKE_FOURCC('a','v','c','1')
#define FOURCC_h263 GST_MAKE_FOURCC('h','2','6','3')
@@ -188,6 +176,11 @@ G_BEGIN_DECLS
#define FOURCC_sosn GST_MAKE_FOURCC('s','o','s','n')
#define FOURCC_XMP_ GST_MAKE_FOURCC('X','M','P','_')
#define FOURCC_uuid GST_MAKE_FOURCC('u','u','i','d')
+#define FOURCC_pssh GST_MAKE_FOURCC('p','s','s','h')
+#define FOURCC_saiz GST_MAKE_FOURCC('s','a','i','z')
+#define FOURCC_saio GST_MAKE_FOURCC('s','a','i','o')
+#define FOURCC_senc GST_MAKE_FOURCC('s','e','n','c')
+#define FOURCC_mfhd GST_MAKE_FOURCC('m','f','h','d')
/* SVQ3 fourcc */
diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c
index 29d7ef5..cc15f89 100644..100755
--- a/gst/isomp4/qtdemux.c
+++ b/gst/isomp4/qtdemux.c
@@ -76,12 +76,80 @@
#ifdef HAVE_ZLIB
# include <zlib.h>
#endif
+#ifdef QTDEMUX_MODIFICATION
+#include <gst/base/gstbytereader.h>
+#define READ_UINT8(reader, val, nbits) G_STMT_START { \
+ if (!gst_bit_reader_get_bits_uint8 (reader, &val, nbits)) { \
+ GST_WARNING ("failed to read uint8, nbits: %d", nbits); \
+ goto failed; \
+ } \
+}G_STMT_END
+#define MARKER_UNCHECKED(br) G_STMT_START { \
+ if (!gst_bit_reader_get_bits_uint8_unchecked (br, 1)) { \
+ GST_WARNING ("Wrong marker bit"); \
+ goto failed; \
+ } \
+}G_STMT_END
+#define CHECK_REMAINING(br, needed) G_STMT_START { \
+ if (gst_bit_reader_get_remaining (br) < needed) \
+ goto failed; \
+} G_STMT_END
+#define READ_UINT16(reader, val, nbits) G_STMT_START { \
+ if (!gst_bit_reader_get_bits_uint16 (reader, &val, nbits)) { \
+ GST_WARNING ("failed to read uint16, nbits: %d", nbits); \
+ goto failed; \
+ } \
+}G_STMT_END
+#define SKIP(reader, nbits) G_STMT_START { \
+ if (!gst_bit_reader_skip (reader, nbits)) { \
+ GST_WARNING ("failed to skip nbits: %d", nbits); \
+ goto failed; \
+ } \
+} G_STMT_END
+#define CHECK_ALLOWED(val, min, max) G_STMT_START { \
+ if (val < min || val > max) { \
+ GST_WARNING ("value not in allowed range. value: %d, range %d-%d", \
+ val, min, max); \
+ goto failed; \
+ } \
+}G_STMT_END
+static const guint8 default_intra_quant_mat[64] = {
+ 8, 17, 18, 19, 21, 23, 25, 27,
+ 17, 18, 19, 21, 23, 25, 27, 28,
+ 20, 21, 22, 23, 24, 26, 28, 30,
+ 21, 22, 23, 24, 26, 28, 30, 32,
+ 22, 23, 24, 26, 28, 30, 32, 35,
+ 23, 24, 26, 28, 30, 32, 35, 38,
+ 25, 26, 28, 30, 32, 35, 38, 41,
+ 27, 28, 30, 32, 35, 38, 41, 45
+};
+static const guint8 default_non_intra_quant_mat[64] = {
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 18, 19, 20, 21, 22, 23, 24, 25,
+ 19, 20, 21, 22, 23, 24, 26, 27,
+ 20, 21, 22, 23, 25, 26, 27, 28,
+ 21, 22, 23, 24, 26, 27, 28, 30,
+ 22, 23, 24, 26, 27, 28, 30, 31,
+ 23, 24, 25, 27, 28, 30, 31, 33,
+};
+static const guint8 mpeg4_zigzag_8x8[64] = {
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63
+};
+#endif
/* max. size considered 'sane' for non-mdat atoms */
#define QTDEMUX_MAX_ATOM_SIZE (25*1024*1024)
/* if the sample index is larger than this, something is likely wrong */
-#define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (50*1024*1024)
+#define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (60*1024*1024)
/* For converting qt creation times to unix epoch times */
#define QTDEMUX_SECONDS_PER_DAY (60 * 60 * 24)
@@ -92,15 +160,20 @@
#ifdef QTDEMUX_MODIFICATION
/* default value of buffer-size property */
#define QTDEMUX_MAX_BUFFER_SIZE (100*1024*1024)
-
+#ifdef DRM_ENABLE
+#define QTDEMUX_DRM_WORD_SIZE 50
+#endif
+#define QTDEMUX_MAX_SAMPLE_DURATION 2*GST_SECOND
#define QTDEMUX_DEFAULT_FWD_TRICKPLAY_MODE 0 /* 0: sending P-frames also, 1: only key frames */
/* properties considering different cases of file formats */
+
enum
{
PROP_0,
PROP_MAX_BUFFER_SIZE,
PROP_TEMP_LOCATION,
PROP_FWDTRICK_MODE,
+ PROP_PLAYBACK_PROTECTION,
};
#endif
@@ -112,6 +185,11 @@ typedef struct _QtDemuxSample QtDemuxSample;
#ifdef QTDEMUX_MODIFICATION
typedef struct _TrickPlayInfo TrickPlayInfo;
+typedef struct _QtDemuxSubSampleEncryption QtDemuxSubSampleEncryption;
+typedef struct _QtDemuxSubSampleEntryInfo QtDemuxSubSampleEntryInfo;
+typedef struct _QtDemuxTfraTable QtDemuxTfraTable;
+typedef struct _QtDemuxTfraEntryInfo QtDemuxTfraEntryInfo;
+typedef struct _QtDemuxLanguageStruct QtDemuxLanguageStruct;
struct _TrickPlayInfo
{
@@ -121,6 +199,37 @@ struct _TrickPlayInfo
gint32 show_samples; /* samples to show between two consecutive key frames */
guint64 start_pos; /* trickplay start position */
};
+
+struct _QtDemuxSubSampleEntryInfo
+{
+ guint16 LenofClearData;
+ guint32 LenofEncryptData;
+};
+
+struct _QtDemuxSubSampleEncryption
+{
+ guint16 n_entries;
+ QtDemuxSubSampleEntryInfo *sub_entry;
+};
+
+struct _QtDemuxTfraEntryInfo
+{
+ guint64 time;
+ guint64 moof_offset;
+};
+
+struct _QtDemuxTfraTable
+{
+ guint32 n_entries;
+ GArray *tfra_entries;
+};
+
+struct _QtDemuxLanguageStruct
+{
+ gchar* language_code;
+ gchar* language_key;
+ gboolean active;
+};
#endif
@@ -136,9 +245,13 @@ struct _QtDemuxSample
guint32 size;
gint32 pts_offset; /* Add this value to timestamp to get the pts */
guint64 offset;
- guint64 timestamp; /* DTS In mov time */
+ guint64 timestamp; /* DTS In mov time */
guint32 duration; /* In mov time */
- gboolean keyframe; /* TRUE when this packet is a keyframe */
+ gboolean keyframe; /* TRUE when this packet is a keyframe */
+#ifdef QTDEMUX_MODIFICATION
+ guint8 *iv; /* initialization vector for decryption*/
+ QtDemuxSubSampleEncryption *sub_encry;
+#endif
};
/* timestamp is the DTS */
@@ -213,6 +326,70 @@ struct _QtDemuxSample
*
* This is a good usecase for the GStreamer accumulated SEGMENT events.
*/
+#ifdef QTDEMUX_MODIFICATION
+typedef char uuid_t[16];
+
+static const uuid_t tfxd_uuid =
+ {
+ 0x6d, 0x1d, 0x9b, 0x05,
+ 0x42, 0xd5, 0x44, 0xe6,
+ 0x80, 0xe2, 0x14, 0x1d,
+ 0xaf, 0xf7, 0x57, 0xb2
+ };
+
+static const uuid_t tfrf_uuid =
+ {
+ 0xd4, 0x80, 0x7e, 0xf2,
+ 0xca, 0x39, 0x46, 0x95,
+ 0x8e, 0x54, 0x26, 0xcb,
+ 0x9e, 0x46, 0xa7, 0x9f
+ };
+
+static const uuid_t encrypt_uuid =
+ {
+ 0xa2, 0x39, 0x4f, 0x52,
+ 0x5a, 0x9b, 0x4f, 0x14,
+ 0xa2, 0x44, 0x6c, 0x42,
+ 0x7c, 0x64, 0x8d, 0xf4
+ };
+
+static const uuid_t protection_uuid =
+ {
+ 0xd0, 0x8a, 0x4f, 0x18,
+ 0x10, 0xf3, 0x4a, 0x82,
+ 0xb6, 0xc8, 0x32, 0xd8,
+ 0xab, 0xa1, 0x83, 0xd3
+ };
+static const uuid_t playready_system_id =
+ {
+ 0x9a, 0x04, 0xf0, 0x79,
+ 0x98, 0x40, 0x42, 0x86,
+ 0xab, 0x92, 0xe6, 0x5b,
+ 0xe0, 0x88, 0x5f, 0x95
+ };
+
+static const uuid_t dash_playready_system_id =
+ {
+ 0x79, 0xf0, 0x04, 0x9a,
+ 0x40, 0x98, 0x86, 0x42,
+ 0xab, 0x92, 0xe6, 0x5b,
+ 0xe0, 0x88, 0x5f, 0x95
+ };
+
+#define SE_OVERRIDE_TE_FLAGS 0x000001
+#define SE_USE_SUBSAMPLE_ENCRYPTION 0x000002
+
+#ifdef DRM_ENABLE
+typedef enum
+{
+ UUID_UNKNOWN = -1,
+ UUID_TFXD,
+ UUID_TFRF,
+ UUID_SAMPLE_ENCRYPT,
+ UUID_PROTECTION_HEADER,
+}uuid_type_t;
+#endif // DRM_ENABLE
+#endif
struct _QtDemuxSegment
{
@@ -225,7 +402,20 @@ struct _QtDemuxSegment
guint64 media_stop;
gdouble rate;
};
-
+#ifdef QTDEMUX_MODIFICATION
+#ifdef DRM_ENABLE
+struct _QtDemuxDrm
+{
+ drm_trusted_open_decrypt_info_s open_input_data ;
+ drm_trusted_open_decrypt_resp_data_s open_output_data ;
+ drm_trusted_set_consumption_state_info_s state_input_data;
+ drm_permission_type_e status_perm_type;
+ drm_license_status_e license_status ;
+ gchar *license_file_path;
+ drm_bool_type_e is_drm_file;
+};
+#endif
+#endif
struct _QtDemuxStream
{
GstPad *pad;
@@ -253,7 +443,7 @@ struct _QtDemuxStream
QtDemuxSample *samples;
gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */
guint32 min_duration; /* duration in timescale of first sample, used for figuring out
- the framerate, in timescale units */
+ the framerate, in timescale units */
/* if we use chunks or samples */
gboolean sampled;
@@ -324,7 +514,6 @@ struct _QtDemuxStream
GstByteReader stsc;
GstByteReader stts;
GstByteReader stss;
- GstByteReader stps;
GstByteReader ctts;
gboolean chunks_are_chunks;
@@ -356,10 +545,7 @@ struct _QtDemuxStream
gboolean stss_present;
guint32 n_sample_syncs;
guint32 stss_index;
- /* stps */
- gboolean stps_present;
guint32 n_sample_partial_syncs;
- guint32 stps_index;
/* ctts */
gboolean ctts_present;
guint32 n_composition_times;
@@ -374,7 +560,14 @@ struct _QtDemuxStream
guint32 def_sample_size;
guint32 def_sample_flags;
#ifdef QTDEMUX_MODIFICATION
+ guint32 orientation;
TrickPlayInfo *trickplay_info; /* trickplay specific handle */
+ guint32 prev_n_samples;
+ GQueue *frag_queue; /* used for PIFF fragments in chain mode */
+ QtDemuxTfraTable *tfra_table;
+ /* dash specified */
+ guint64 dash_seek_offset;
+ guint64 moof_seeked_time;
#endif
};
@@ -462,8 +655,16 @@ static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux,
static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
QtDemuxStream * stream, guint32 n);
static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
+static GstFlowReturn qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
+ guint64 * length, guint32 fourcc);
#ifdef QTDEMUX_MODIFICATION
+static void gst_qtdemux_stream_clear (GstQTDemux *qtdemux, QtDemuxStream * stream);
+static void gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream);
+static gboolean gst_qtdemux_sink_setcaps (GstPad * pad, GstCaps *caps);
+static void gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
+ QtDemuxStream * stream);
+static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
static void gst_qtdemux_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_qtdemux_get_property (GObject * object, guint prop_id,
@@ -471,6 +672,14 @@ static void gst_qtdemux_get_property (GObject * object, guint prop_id,
static gint32 gst_qtdemux_find_next_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str, guint32 index);
static void gst_qtdemux_forward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, guint64 *timestamp);
static void gst_qtdemux_backward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, guint64 *timestamp);
+
+#ifdef DRM_ENABLE
+static gboolean qtdemux_get_playready_licence (GstQTDemux * qtdemux, guint8* data, guint size, char* query_file_path);
+void test_drm_trusted_operation_cb(drm_trusted_user_operation_info_s *operation_info, void *output_data);
+static gboolean qtdemux_parse_playready_system_id(GstQTDemux * qtdemux, GstByteReader *uuid_data);
+static uuid_type_t qtdemux_get_uuid_type(GstQTDemux * qtdemux, GstByteReader *uuid_data, gint64 *uuid_offset);
+static void qtdemux_post_drm_error (GstQTDemux * qtdemux, int drm_error);
+#endif // DRM_ENABLE
#endif
static void
@@ -523,7 +732,7 @@ gst_qtdemux_class_init (GstQTDemuxClass * klass)
"Maximum buffer size for mdat atom buffering in case it comes before the moov atom",
1, G_MAXUINT, QTDEMUX_MAX_BUFFER_SIZE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
+
g_object_class_install_property (gobject_class, PROP_TEMP_LOCATION,
g_param_spec_string ("temp-location", "temp-location",
"Location for the mdat atom buffering incase it comes before the moov atom",
@@ -536,6 +745,11 @@ gst_qtdemux_class_init (GstQTDemuxClass * klass)
"(0) Sending Non-Keyframes also in forward trickplay (1) Sending only keyframes in forward trickplay",
QTDEMUX_DEFAULT_FWD_TRICKPLAY_MODE,
G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_PLAYBACK_PROTECTION,
+ g_param_spec_boolean ("playback-protection", "whether file is playback protected or not",
+ "TRUE->playback protected, FALSE->not playback protected", FALSE,
+ G_PARAM_READWRITE));
#endif
@@ -554,9 +768,15 @@ gst_qtdemux_init (GstQTDemux * qtdemux, GstQTDemuxClass * klass)
qtdemux_sink_activate_push);
gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
- gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
qtdemux->state = QTDEMUX_STATE_INITIAL;
+#ifdef QTDEMUX_MODIFICATION
+ gst_pad_set_setcaps_function (qtdemux->sinkpad, gst_qtdemux_sink_setcaps);
+ gst_pad_set_element_private (qtdemux->sinkpad, qtdemux);
+ gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
+
+ qtdemux->protection_header_present = FALSE;
+#endif
qtdemux->pullbased = FALSE;
qtdemux->posted_redirect = FALSE;
qtdemux->neededbytes = 16;
@@ -568,7 +788,12 @@ gst_qtdemux_init (GstQTDemux * qtdemux, GstQTDemuxClass * klass)
qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
qtdemux->mdatbuffer = NULL;
gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
+#ifdef MULTI_AUDIO
+ qtdemux->Language_list = NULL;
+#endif
#ifdef QTDEMUX_MODIFICATION
+ qtdemux->is_dash = FALSE;
+ qtdemux->dash_init_offset = GST_CLOCK_TIME_NONE;
/* Initialization of properties with default values*/
qtdemux->filename = g_strdup("/tmp/qtdemux");
qtdemux->file = NULL;
@@ -576,6 +801,31 @@ gst_qtdemux_init (GstQTDemux * qtdemux, GstQTDemuxClass * klass)
qtdemux->filesize = 0;
qtdemux->maxbuffersize = QTDEMUX_MAX_BUFFER_SIZE;
qtdemux->fwdtrick_mode = QTDEMUX_DEFAULT_FWD_TRICKPLAY_MODE;
+#ifdef DRM_ENABLE
+ qtdemux->encrypt_content = FALSE;
+ qtdemux->pr_handle = NULL;
+#endif
+ qtdemux->piff_fragmented = FALSE;
+ qtdemux->max_pop_ts = 0;
+ qtdemux->playback_protected = FALSE;
+ qtdemux->need_parsing_moov = FALSE;
+ qtdemux->sample_count[0] = 0;
+ qtdemux->sample_count[1] = 0;
+ qtdemux->current_sample[0] = 0;
+ qtdemux->current_sample[1] = 0;
+ qtdemux->sequence_id = 0;
+ qtdemux->dash_content = FALSE;
+ qtdemux->sub_sample_count = NULL;
+ qtdemux->encrypted_data = NULL;
+ qtdemux->clear_data = NULL;
+ qtdemux->iv_data_audio = NULL;
+ qtdemux->iv_data_video = NULL;
+ qtdemux->no_of_audio_samples = 0;
+ qtdemux->no_of_video_samples = 0;
+ qtdemux->moof_offsets = NULL;
+ qtdemux->need_moof_parsing = FALSE;
+ qtdemux->mfra_offset = 0;
+ qtdemux->Subtitle_language_list = NULL;
#endif
}
@@ -610,13 +860,16 @@ gst_qtdemux_set_property (GObject * object, guint prop_id,
demux->maxbuffersize = g_value_get_uint (value);
break;
case PROP_TEMP_LOCATION:
- if (demux->filename)
+ if (demux->filename)
g_free (demux->filename);
demux->filename = g_strdup (g_value_get_string (value));
break;
case PROP_FWDTRICK_MODE:
demux->fwdtrick_mode = g_value_get_boolean(value);
break;
+ case PROP_PLAYBACK_PROTECTION:
+ demux->playback_protected = g_value_get_boolean(value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -624,7 +877,7 @@ gst_qtdemux_set_property (GObject * object, guint prop_id,
}
static void
-gst_qtdemux_get_property (GObject * object, guint prop_id,
+gst_qtdemux_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstQTDemux *demux;
@@ -640,6 +893,9 @@ gst_qtdemux_get_property (GObject * object, guint prop_id,
case PROP_FWDTRICK_MODE:
g_value_set_boolean(value, demux->fwdtrick_mode);
break;
+ case PROP_PLAYBACK_PROTECTION:
+ g_value_set_boolean(value, demux->playback_protected);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -661,7 +917,190 @@ gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
("no known streams found"));
}
}
+#ifdef QTDEMUX_MODIFICATION
+#ifdef DRM_ENABLE
+static gboolean
+qtdemux_playready_parse_uuid(GNode * uuid,GstQTDemux * qtdemux)
+{
+ QtDemuxDrm struct_drm = {0, };
+ GstByteReader uuid_data;
+ guint32 protection_header_size = 0;
+ guint8 *protection_header_data = NULL;
+ gint64 uuid_offset = 0;
+ int ret = -1;
+ int i = 0;
+ guint8 *buffer = (guint8 *) uuid->data;
+ gint len_protection_header = 0;
+ gboolean is_video = FALSE;
+ uuid_type_t uuid_type;
+ struct_drm.license_status = DRM_LICENSE_STATUS_UNDEFINED;
+ struct_drm.license_file_path = NULL;
+ struct_drm.is_drm_file = DRM_UNKNOWN;
+
+ gst_byte_reader_init (&uuid_data, buffer, QT_UINT32 (buffer));
+ uuid_type = qtdemux_get_uuid_type (qtdemux, &uuid_data, &uuid_offset);/*this function call is neccesary for changing uudi data offset*/
+ if(!qtdemux) {
+ goto fail;
+ }
+ if(qtdemux->n_streams) {
+ for(i = 0 ; i < qtdemux->n_streams;i++ ) {
+ if(qtdemux->streams[i]->subtype == FOURCC_vide) {
+ GST_INFO_OBJECT(qtdemux,"video stream is present in content");
+ is_video = TRUE;
+ break;
+ }
+ }
+ }
+ if (qtdemux->playback_protected && is_video ) {
+ GST_ERROR_OBJECT (qtdemux, "Video file is playback protected, so exiting now");
+ GST_ERROR_OBJECT (qtdemux, "Trusted OPL violation error");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT_NOKEY, ("Trusted opl violation error"), (NULL));
+ goto fail;
+ }
+
+ if (!qtdemux_parse_playready_system_id (qtdemux, &uuid_data)) {
+ GST_ERROR_OBJECT (qtdemux, "not a playready system id..");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), ("not a valid playready system ID"));
+ goto fail;
+ }
+
+/* Enters into if body when valid system ID is present.*/
+ gst_byte_reader_skip (&uuid_data, 12);
+
+ gst_byte_reader_get_uint16_le (&uuid_data, &protection_header_size);
+
+ GST_DEBUG_OBJECT(qtdemux,"protection header size: %d ", protection_header_size);
+
+ if (!gst_byte_reader_dup_data(&uuid_data, protection_header_size , &protection_header_data)) {
+ GST_ERROR_OBJECT (qtdemux, "failed to duplicate bytereader data...");
+ GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory"));
+ goto fail;
+ }
+/* for adding wchar null check */
+ protection_header_size = protection_header_size + 2;
+
+ protection_header_data = (guint8 *) realloc (protection_header_data,protection_header_size);
+ if (!protection_header_data) {
+ GST_ERROR_OBJECT (qtdemux, "failed to allocate memory...");
+ GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory"));
+ goto fail;
+ }
+
+/* adding wchar null character */
+ protection_header_data[protection_header_size-2] = '\0';
+ protection_header_data[protection_header_size-1] = '\0';
+
+ GST_DEBUG_OBJECT(qtdemux,"protection header size: %d ", protection_header_size);
+
+ struct_drm.license_file_path = (gchar*)calloc((QTDEMUX_DRM_WORD_SIZE + 32 + protection_header_size), sizeof(gchar));
+ if(struct_drm.license_file_path == NULL) {
+ GST_ERROR_OBJECT (qtdemux, "failed to allocate memory...");
+ GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory"));
+ goto fail;
+ }
+
+ strcpy(struct_drm.license_file_path, "ismv");
+
+ struct_drm.license_file_path[4] = '?';
+ for(i = 0; i < 16; i++) {
+ sprintf(struct_drm.license_file_path + 4 + 1 + (i*2), "%02x", qtdemux->uuid_protection[i]);
+ }
+
+ for(i = 0; i < 8; i++) {
+ struct_drm.license_file_path[(QTDEMUX_DRM_WORD_SIZE - 13 ) + i] = '0';
+ }
+
+ for(i = 0; i < 16; i++) {
+ sprintf(struct_drm.license_file_path + (QTDEMUX_DRM_WORD_SIZE -5 ) + (i*2), "%02x", qtdemux->uuid_playready[i]);
+ }
+ struct_drm.license_file_path[(QTDEMUX_DRM_WORD_SIZE -5 ) + 32] = '?';
+ len_protection_header = sprintf(struct_drm.license_file_path + (QTDEMUX_DRM_WORD_SIZE -5 ) + 32 + 1 , "%d", protection_header_size);
+ GST_DEBUG_OBJECT(qtdemux, "protection header size length %d", len_protection_header);
+
+ for(i = 0; i < protection_header_size; i++) {
+ struct_drm.license_file_path[(QTDEMUX_DRM_WORD_SIZE -5 ) + 32 + 1 + len_protection_header + i] = protection_header_data[i];
+ }
+
+ GST_DEBUG_OBJECT(qtdemux, "ismv header is %s", struct_drm.license_file_path);
+
+ ret = drm_is_drm_file(struct_drm.license_file_path, &struct_drm.is_drm_file);
+ if(DRM_RETURN_SUCCESS != ret) {
+ GST_ERROR_OBJECT (qtdemux, "Failed to read is DRM_FILE information");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("Failed to read is DRM_FILE information"), (NULL));
+ goto fail;
+ }
+
+ GST_DEBUG_OBJECT(qtdemux, "is drm is drm %d", struct_drm.is_drm_file);
+
+
+ struct_drm.status_perm_type = DRM_PERMISSION_TYPE_PLAY;
+ ret = drm_get_license_status (struct_drm.license_file_path, struct_drm.status_perm_type, &struct_drm.license_status);
+
+ if (DRM_RETURN_SUCCESS != ret) {
+ GST_ERROR_OBJECT (qtdemux, "failed to get license status : 0x%x", ret);
+ qtdemux_post_drm_error (qtdemux, DRM_LICENSE_STATUS_UNDEFINED);
+ goto fail;
+ }
+
+ if (DRM_LICENSE_STATUS_VALID != struct_drm.license_status) {
+ GST_ERROR ("DRM license status is not valid, license_status=%d", struct_drm.license_status);
+ qtdemux_post_drm_error (qtdemux, struct_drm.license_status);
+ goto fail;
+ }
+
+ GST_DEBUG_OBJECT (qtdemux, "successfully got the license status..");
+
+
+#ifdef GET_DRM_LICENSE
+ license_ret = qtdemux_get_playready_licence(qtdemux, protection_header_data, protection_header_size);
+ if(!license_ret) {
+ GST_ERROR_OBJECT (qtdemux, "failed to get playready license");
+ goto fail;
+ }
+#endif
+
+ struct_drm.open_input_data.file_type = DRM_TRUSTED_TYPE_PIFF;
+ struct_drm.open_input_data.permission = DRM_TRUSTED_PERMISSION_TYPE_PLAY;
+ struct_drm.open_input_data.operation_callback.callback = test_drm_trusted_operation_cb;
+ struct_drm.open_input_data.lic_header.header = (unsigned char *) protection_header_data;
+ struct_drm.open_input_data.lic_header.header_len = protection_header_size;
+
+/* Open Decrypt Session*/
+ ret = drm_trusted_open_decrypt_session(&struct_drm.open_input_data, &struct_drm.open_output_data, &(qtdemux->pr_handle));
+ if (DRM_TRUSTED_RETURN_SUCCESS != ret) {
+ GST_ERROR_OBJECT (qtdemux, "failed to open decrypt session");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("failed to open decrypt session"), (NULL));
+ goto fail;
+ }
+
+/* Before Read, Appropriate state MUST be SET */
+ struct_drm.state_input_data.state = DRM_CONSUMPTION_STARTED;
+ ret = drm_trusted_set_decrypt_state(qtdemux->pr_handle, &struct_drm.state_input_data);
+ if (DRM_TRUSTED_RETURN_SUCCESS != ret) {
+ GST_ERROR_OBJECT (qtdemux, "failed to set decrypt state...");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("failed to set decrypt state"), (NULL));
+ goto fail;
+ }
+ qtdemux->encrypt_content = TRUE;
+
+ free(struct_drm.license_file_path);
+ free (protection_header_data);
+ return TRUE;
+fail:
+ if (protection_header_data) {
+ free (protection_header_data);
+ }
+
+ if(struct_drm.license_file_path) {
+ free(struct_drm.license_file_path);
+ struct_drm.license_file_path = NULL;
+ }
+ GST_ERROR_OBJECT(qtdemux,"fail to parse uuid atom in qtdemux_playready_parse_uuid func ");
+ return FALSE;
+}
+#endif
+#endif
static GstFlowReturn
gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
GstBuffer ** buf)
@@ -836,13 +1275,35 @@ gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query)
gst_query_parse_duration (query, &fmt, NULL);
if (fmt == GST_FORMAT_TIME) {
- gint64 duration = -1;
+ /* First try to query upstream */
+ res = gst_pad_query_default (pad, query);
+ if (!res) {
+ gint64 duration = -1;
+
+ gst_qtdemux_get_duration (qtdemux, &duration);
+ if (duration > 0) {
+ gst_query_set_duration (query, GST_FORMAT_TIME, duration);
+ res = TRUE;
+ }
+ }
+#ifdef QTDEMUX_MODIFICATION
+ else {
+ gint64 duration = -1;
- gst_qtdemux_get_duration (qtdemux, &duration);
- if (duration > 0) {
- gst_query_set_duration (query, GST_FORMAT_TIME, duration);
- res = TRUE;
+ gst_query_parse_duration(query, &fmt, &duration);
+
+ if (duration <= 0) {
+ gst_qtdemux_get_duration (qtdemux, &duration);
+
+ GST_LOG_OBJECT (pad, "set duration %"G_GUINT64_FORMAT, duration);
+
+ if (duration > 0) {
+ gst_query_set_duration (query, GST_FORMAT_TIME, duration);
+ res = TRUE;
+ }
+ }
}
+#endif
}
break;
}
@@ -1406,6 +1867,46 @@ no_format:
}
}
+#ifdef QTDEMUX_MODIFICATION
+static gboolean
+gst_qtdemux_update_moof_offset (GstQTDemux * qtdemux, QtDemuxStream *stream, gint64 desired_offset, guint64 *moof_offset)
+{
+ guint32 idx = 0;
+ QtDemuxTfraTable *tfra_table = stream->tfra_table;
+ QtDemuxTfraEntryInfo *entry = NULL;
+ gboolean found_entry = FALSE;
+ guint64 time = 0;
+
+ entry = &g_array_index (tfra_table->tfra_entries, QtDemuxTfraEntryInfo, idx);
+ *moof_offset = entry->moof_offset;
+
+ for (idx = 0; idx < tfra_table->n_entries; idx++) {
+ entry = &g_array_index (tfra_table->tfra_entries, QtDemuxTfraEntryInfo, idx);
+ if (gst_util_uint64_scale (entry->time, GST_SECOND, stream->timescale) > desired_offset) {
+ GST_INFO_OBJECT (stream->pad, "found moof time greater than desired time...");
+ found_entry = TRUE;
+ break;
+ }
+ *moof_offset = entry->moof_offset;
+ time = entry->time;
+ }
+
+ if (!found_entry) {
+ GST_WARNING_OBJECT (qtdemux, "Did not find entry...tfra table is not complete");
+ *moof_offset = -1;
+ stream->moof_seeked_time = 0;
+ return FALSE;
+ }
+
+ stream->moof_seeked_time = time;
+
+ GST_INFO_OBJECT (stream->pad, "desired moof_offset = %"G_GUINT64_FORMAT" & fragment start time = %"GST_TIME_FORMAT,
+ *moof_offset, gst_util_uint64_scale (time, GST_SECOND, stream->timescale));
+
+ return TRUE;
+}
+#endif
+
/* perform the seek.
*
* We set all segment_indexes in the streams to unknown and
@@ -1427,6 +1928,10 @@ gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment)
{
gint64 desired_offset;
gint n;
+#ifdef QTDEMUX_MODIFICATION
+ guint64 min_moof_offset = -1;
+ gboolean bret = TRUE;
+#endif
desired_offset = segment->last_stop;
@@ -1464,6 +1969,52 @@ gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment)
segment->last_stop = desired_offset;
segment->time = desired_offset;
+#ifdef QTDEMUX_MODIFICATION
+ if (qtdemux->fragmented) {
+ QtDemuxTfraEntryInfo *entry = NULL;
+ gint count = 0;
+
+ for (n = 0; n < qtdemux->n_streams; n++) {
+ QtDemuxStream *stream = qtdemux->streams[n];
+ guint64 cur_moof_offset = 0;
+
+ if (stream->tfra_table && bret) {
+ /* all streams' tfra should be complete.. otherwise ignore seeking using tfra/mfra */
+ bret = gst_qtdemux_update_moof_offset (qtdemux, stream, desired_offset, &cur_moof_offset);
+ count++;
+ if (!bret) {
+ min_moof_offset = -1;
+ break;
+ }
+ min_moof_offset = min_moof_offset < cur_moof_offset ? min_moof_offset : cur_moof_offset;
+ GST_INFO_OBJECT (qtdemux, "min moof offset = %"G_GUINT64_FORMAT, min_moof_offset < cur_moof_offset ? min_moof_offset : cur_moof_offset);
+ }
+ }
+
+ if (min_moof_offset != -1 && (count == qtdemux->n_streams)) {
+ GST_INFO_OBJECT (qtdemux, "updated moof_offset = %" G_GUINT64_FORMAT, min_moof_offset);
+ for (n = 0; n < qtdemux->moof_offsets->len; n++) {
+ entry = &g_array_index (qtdemux->moof_offsets, QtDemuxTfraEntryInfo, n);
+ if (entry->moof_offset == min_moof_offset)
+ break;
+ }
+ GST_INFO_OBJECT (qtdemux, "updated moof index = %d", n);
+ qtdemux->moof_offset = qtdemux->offset = min_moof_offset;
+ qtdemux->need_moof_parsing = TRUE;
+
+ GST_INFO_OBJECT (qtdemux, "after seek : offset = %" G_GUINT64_FORMAT, min_moof_offset);
+
+ /*reset sample table */
+ for (n = 0; n < qtdemux->n_streams; n++) {
+ QtDemuxStream *stream = qtdemux->streams[n];
+ g_free(stream->samples);
+ stream->n_samples = 0;
+ stream->stbl_index = -1;
+ }
+ }
+ }
+#endif
+
/* we stop at the end */
if (segment->stop == -1)
segment->stop = segment->duration;
@@ -1479,7 +2030,7 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
GstFormat format;
GstSeekFlags flags;
GstSeekType cur_type, stop_type;
- gint64 cur, stop;
+ gint64 cur = 0, stop;
gboolean flush;
gboolean update;
GstSegment seeksegment;
@@ -1657,8 +2208,11 @@ gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event)
}
if (qtdemux->pullbased) {
res = gst_qtdemux_do_seek (qtdemux, pad, event);
- } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams &&
- !qtdemux->fragmented) {
+ } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) {
+ GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked");
+ res = TRUE;
+ } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams
+ && !qtdemux->fragmented) {
res = gst_qtdemux_do_push_seek (qtdemux, pad, event);
} else {
GST_DEBUG_OBJECT (qtdemux,
@@ -1763,6 +2317,178 @@ gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw,
if (_index)
*_index = index;
}
+#ifdef QTDEMUX_MODIFICATION
+static void
+gst_qtdemux_reset(GstQTDemux *qtdemux, gboolean hard)
+{
+ gint n;
+ gint ret;
+
+ qtdemux->offset = 0;
+ gst_adapter_clear (qtdemux->adapter);
+ qtdemux->neededbytes = -1;
+
+ if(hard || qtdemux->is_dash)
+ {
+ qtdemux->state = QTDEMUX_STATE_INITIAL;
+ qtdemux->neededbytes = 16;
+ qtdemux->todrop = 0;
+ qtdemux->pullbased = FALSE;
+ qtdemux->posted_redirect = FALSE;
+ qtdemux->first_mdat = -1;
+ qtdemux->header_size = 0;
+ qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
+ if (qtdemux->mdatbuffer)
+ gst_buffer_unref (qtdemux->mdatbuffer);
+ qtdemux->mdatbuffer = NULL;
+ if (qtdemux->comp_brands)
+ gst_buffer_unref (qtdemux->comp_brands);
+ qtdemux->comp_brands = NULL;
+ if (qtdemux->tag_list)
+ gst_tag_list_free (qtdemux->tag_list);
+ qtdemux->tag_list = NULL;
+ gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
+ qtdemux->requested_seek_time = GST_CLOCK_TIME_NONE;
+ qtdemux->seek_offset = 0;
+ qtdemux->upstream_seekable = FALSE;
+ qtdemux->upstream_size = 0;
+ qtdemux->dash_init_offset = GST_CLOCK_TIME_NONE;
+ }
+
+ if(hard)
+ {
+ for (n = 0; n < qtdemux->n_streams; n++) {
+ gst_qtdemux_stream_free (qtdemux, qtdemux->streams[n]);
+ qtdemux->streams[n] = NULL;
+ }
+
+ qtdemux->got_moov = FALSE;
+ qtdemux->major_brand = 0;
+ qtdemux->n_streams = 0;
+ qtdemux->n_video_streams = 0;
+ qtdemux->n_audio_streams = 0;
+ qtdemux->n_sub_streams = 0;
+ qtdemux->is_dash = FALSE;
+ qtdemux->video_max_width = 0;
+ qtdemux->video_max_height = 0;
+
+ if (qtdemux->element_index)
+ gst_object_unref (qtdemux->element_index);
+ qtdemux->element_index = NULL;
+ // TODO: Check new modifications
+ if (qtdemux->file) {
+ fclose (qtdemux->file);
+ if (qtdemux->ofile)
+ fclose (qtdemux->ofile);
+ if(!remove (qtdemux->filename)) GST_DEBUG_OBJECT (qtdemux, "Error removing temp file");
+ }
+ qtdemux->file = NULL;
+ qtdemux->ofile = NULL;
+ qtdemux->filesize = 0;
+ qtdemux->maxbuffersize = QTDEMUX_MAX_BUFFER_SIZE;
+#ifdef DRM_ENABLE
+ if (qtdemux->pr_handle) {
+ if (!drm_trusted_close_decrypt_session(&qtdemux->pr_handle)) {
+ GST_ERROR_OBJECT (qtdemux, "failed to close decrypt session...");
+ }
+ }
+
+ ret = drm_process_request(DRM_REQUEST_TYPE_CLIENT_CLEAN_UP, NULL, NULL);
+ if (DRM_RETURN_SUCCESS == ret) {
+ GST_INFO_OBJECT(qtdemux, "Clean Up successful!!");
+ } else {
+ GST_ERROR_OBJECT (qtdemux, "Clean Up Failed!!, ret = 0x%x", ret);
+ }
+
+ ret = drm_trusted_handle_request(DRM_TRUSTED_REQ_TYPE_CLIENT_CLEAN_UP, NULL, NULL);
+ if (DRM_RETURN_SUCCESS == ret) {
+ GST_INFO_OBJECT(qtdemux, "Clean Up successful!!");
+ } else {
+ GST_ERROR_OBJECT (qtdemux, "Clean Up Failed!!, ret = 0x%x", ret);
+ }
+
+ qtdemux->encrypt_content = FALSE;
+#endif
+#ifdef MULTI_AUDIO
+ if(qtdemux->Language_list)
+ {
+ g_list_free(qtdemux->Language_list);
+ qtdemux->Language_list = NULL;
+ }
+#endif
+
+ if (qtdemux->Subtitle_language_list) {
+ g_list_free (qtdemux->Subtitle_language_list);
+ qtdemux->Subtitle_language_list = NULL;
+ }
+
+ if(qtdemux->sub_sample_count) {
+ g_free(qtdemux->sub_sample_count);
+ qtdemux->sub_sample_count = NULL;
+ }
+ if(qtdemux->clear_data) {
+ g_free(qtdemux->clear_data);
+ qtdemux->clear_data = NULL;
+ }
+ if(qtdemux->encrypted_data) {
+ g_free(qtdemux->encrypted_data);
+ qtdemux->encrypted_data = NULL;
+ }
+
+ if(qtdemux->iv_data_audio) {
+ for(int i = 0; i < qtdemux->no_of_audio_samples; i++) {
+ g_free(qtdemux->iv_data_audio[i]);
+ qtdemux->iv_data_audio[i] = NULL;
+ }
+ g_free(qtdemux->iv_data_audio);
+ qtdemux->iv_data_audio = NULL;
+ }
+
+ if(qtdemux->iv_data_video) {
+ for(int i = 0; i < qtdemux->no_of_video_samples; i++) {
+ g_free(qtdemux->iv_data_video[i]);
+ qtdemux->iv_data_video[i] = NULL;
+ }
+ g_free(qtdemux->iv_data_video);
+ qtdemux->iv_data_video = NULL;
+ }
+ }else if(qtdemux->is_dash){
+ for (n = 0; n < qtdemux->n_streams; n++) {
+ gst_qtdemux_stream_clear(qtdemux,qtdemux->streams[n]);
+ qtdemux->streams[n]->dash_seek_offset = GST_CLOCK_TIME_NONE;
+ }
+ }else{
+ for (n = 0; n < qtdemux->n_streams; n++) {
+ qtdemux->streams[n]->last_ret = GST_FLOW_OK;
+ qtdemux->streams[n]->sent_eos = FALSE;
+ }
+ }
+}
+
+static gboolean
+gst_qtdemux_sink_setcaps (GstPad * pad, GstCaps *caps)
+{
+ const gchar *variant;
+ GstStructure *structure;
+ GstQTDemux *demux = GST_QTDEMUX_CAST (gst_pad_get_element_private(pad));
+
+ structure = gst_caps_get_structure (caps, 0);
+ variant = gst_structure_get_string (structure, "variant");
+ if(variant && strcmp (variant,"dash-fragmented") == 0) {
+ demux->is_dash = TRUE;
+ demux->fragmented = TRUE;
+ }
+
+ if (gst_structure_has_field(structure, "max-width")
+ && gst_structure_has_field(structure, "max-height"))
+ {
+ gst_structure_get_int(structure, "max-width", &demux->video_max_width);
+ gst_structure_get_int(structure, "max-height", &demux->video_max_height);
+ }
+
+ return gst_pad_set_caps (pad, caps);
+}
+#endif
static gboolean
gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
@@ -1793,6 +2519,31 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
"received format %d newsegment %" GST_SEGMENT_FORMAT, format,
&segment);
+#ifdef QTDEMUX_MODIFICATION
+ /*Processing event from dashdemux after seeking.*/
+ if(demux->is_dash && format == GST_FORMAT_TIME)
+ {
+ int i;
+ demux->dash_init_offset = start;
+
+ /* allows seek for multiple streams */
+ for (i = 0; i < demux->n_streams; i++) {
+ demux->streams[i]->dash_seek_offset = start;
+ }
+
+ gst_segment_set_newsegment_full (&demux->segment, update, rate, arate,
+ GST_FORMAT_TIME, start, stop, start);
+ GST_DEBUG_OBJECT (demux, "Pushing newseg update %d, rate %g, "
+ "applied rate %g, format %d, start %" GST_TIME_FORMAT ", "
+ "stop %" GST_TIME_FORMAT, update, rate, arate, GST_FORMAT_TIME,
+ GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
+ gst_qtdemux_push_event (demux,
+ gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME,
+ start, stop, start));
+ goto exit;
+ }
+#endif
+
/* chain will send initial newsegment after pads have been added */
if (demux->state != QTDEMUX_STATE_MOVIE || !demux->n_streams) {
GST_DEBUG_OBJECT (demux, "still starting, eating event");
@@ -1867,6 +2618,9 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
}
case GST_EVENT_FLUSH_STOP:
{
+#ifdef QTDEMUX_MODIFICATION
+ gst_qtdemux_reset(demux,FALSE);
+#else
gint i;
/* clean up, force EOS if no more info follows */
@@ -1878,6 +2632,7 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
demux->streams[i]->last_ret = GST_FLOW_OK;
demux->streams[i]->sent_eos = FALSE;
}
+#endif
break;
}
case GST_EVENT_EOS:
@@ -1886,6 +2641,23 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
if (!demux->pullbased) {
gint i;
gboolean has_valid_stream = FALSE;
+
+#ifdef QTDEMUX_MODIFICATION
+ if (demux->piff_fragmented) {
+ GstBuffer *pop_buf = NULL;
+ GstFlowReturn fret = GST_FLOW_OK;
+
+ for (i = 0; i < demux->n_streams; i++) {
+ if (demux->streams[i]->pad != NULL) {
+ while (!g_queue_is_empty (demux->streams[i]->frag_queue)) {
+ pop_buf = g_queue_pop_head (demux->streams[i]->frag_queue);
+ fret = gst_pad_push (demux->streams[i]->pad, pop_buf);
+ GST_DEBUG_OBJECT (demux->streams[i]->pad, "pushing from event_func : %s\n", gst_flow_get_name (fret));
+ }
+ }
+ }
+ }
+#endif
for (i = 0; i < demux->n_streams; i++) {
if (demux->streams[i]->pad != NULL) {
has_valid_stream = TRUE;
@@ -1942,6 +2714,366 @@ gst_qtdemux_get_index (GstElement * element)
return result;
}
+#ifdef QTDEMUX_MODIFICATION
+static void
+mpeg4_util_par_from_info (guint8 aspect_ratio_info, guint8 * par_width,
+ guint8 * par_height)
+{
+ switch (aspect_ratio_info) {
+ case 0x02:
+ *par_width = 12;
+ *par_height = 11;
+ break;
+ case 0x03:
+ *par_width = 10;
+ *par_height = 11;
+ break;
+ case 0x04:
+ *par_width = 16;
+ *par_height = 11;
+ break;
+ case 0x05:
+ *par_width = 40;
+ *par_height = 33;
+ break;
+
+ case 0x01:
+ default:
+ *par_width = 1;
+ *par_height = 1;
+ }
+}
+
+static gboolean
+parse_quant (GstBitReader * br, guint8 quant_mat[64],
+ const guint8 default_quant_mat[64], guint8 * load_quant_mat)
+{
+ READ_UINT8 (br, *load_quant_mat, 1);
+ if (*load_quant_mat) {
+ guint i;
+ guint8 val;
+
+ val = 1;
+ for (i = 0; i < 64; i++) {
+
+ if (val != 0)
+ READ_UINT8 (br, val, 8);
+
+ if (val == 0) {
+ if (i == 0)
+ goto invalid_quant_mat;
+ quant_mat[mpeg4_zigzag_8x8[i]] = quant_mat[mpeg4_zigzag_8x8[i - 1]];
+ } else
+ quant_mat[mpeg4_zigzag_8x8[i]] = val;
+ }
+ } else
+ memcpy (quant_mat, default_quant_mat, 64);
+
+ return TRUE;
+
+failed:
+ GST_WARNING ("failed parsing quant matrix");
+ return FALSE;
+
+invalid_quant_mat:
+ GST_WARNING ("the first value should be non zero");
+ goto failed;
+}
+static GstMpeg4ParseResult gst_mpeg4_parse_video_object_layer (GstMpeg4VideoObjectLayer * vol,
+ GstMpeg4VisualObject * vo, const guint8 * data, gsize size)
+{
+ guint8 video_object_layer_start_code;
+ /* Used for enums types */
+ guint8 tmp;
+ GstBitReader br = GST_BIT_READER_INIT (data, size);
+
+ g_return_val_if_fail (vol != NULL, GST_MPEG4_PARSER_ERROR);
+
+ GST_DEBUG ("Parsing video object layer");
+
+ READ_UINT8 (&br, video_object_layer_start_code, 8);
+ if (!(video_object_layer_start_code >= 0x20 &&
+ video_object_layer_start_code <= 0x2f))
+ goto wrong_start_code;
+
+ /* set default values */
+ if (vo) {
+ vol->verid = vo->verid;
+ vol->priority = vo->priority;
+ }
+
+ vol->low_delay = FALSE;
+ vol->chroma_format = 1;
+ vol->vbv_parameters = FALSE;
+ vol->quant_precision = 5;
+ vol->bits_per_pixel = 8;
+ vol->quarter_sample = FALSE;
+ vol->newpred_enable = FALSE;
+ vol->interlaced = 0;
+ vol->width = 0;
+ vol->height = 0;
+
+ READ_UINT8 (&br, vol->random_accessible_vol, 1);
+ READ_UINT8 (&br, vol->video_object_type_indication, 8);
+ READ_UINT8 (&br, vol->is_object_layer_identifier, 1);
+ if (vol->is_object_layer_identifier) {
+ READ_UINT8 (&br, vol->verid, 4);
+ READ_UINT8 (&br, vol->priority, 3);
+ }
+
+ READ_UINT8 (&br, tmp, 4);
+ vol->aspect_ratio_info = tmp;
+ if (vol->aspect_ratio_info != GST_MPEG4_EXTENDED_PAR) {
+ mpeg4_util_par_from_info (vol->aspect_ratio_info, &vol->par_width,
+ &vol->par_height);
+
+ } else {
+ gint v;
+
+ READ_UINT8 (&br, vol->par_width, 8);
+ v = vol->par_width;
+ CHECK_ALLOWED (v, 1, 255);
+
+ READ_UINT8 (&br, vol->par_height, 8);
+ v = vol->par_height;
+ CHECK_ALLOWED (v, 1, 255);
+ }
+ READ_UINT8 (&br, vol->control_parameters, 1);
+ if (vol->control_parameters) {
+ guint8 chroma_format;
+
+ READ_UINT8 (&br, chroma_format, 2);
+ vol->chroma_format = chroma_format;
+ READ_UINT8 (&br, vol->low_delay, 1);
+ READ_UINT8 (&br, vol->vbv_parameters, 1);
+ if (vol->vbv_parameters) {
+ CHECK_REMAINING (&br, 79);
+
+ vol->first_half_bitrate =
+ gst_bit_reader_get_bits_uint16_unchecked (&br, 15);
+ MARKER_UNCHECKED (&br);
+
+ vol->latter_half_bitrate =
+ gst_bit_reader_get_bits_uint16_unchecked (&br, 15);
+ MARKER_UNCHECKED (&br);
+
+ vol->bit_rate =
+ (vol->first_half_bitrate << 15) | vol->latter_half_bitrate;
+
+ vol->first_half_vbv_buffer_size =
+ gst_bit_reader_get_bits_uint16_unchecked (&br, 15);
+ GST_ERROR("reading the vol->first_half_vbv_buffer_size 15 bit = %x",vol->first_half_vbv_buffer_size);
+ MARKER_UNCHECKED (&br);
+
+ vol->latter_half_vbv_buffer_size =
+ gst_bit_reader_get_bits_uint8_unchecked (&br, 3);
+ MARKER_UNCHECKED (&br);
+
+ vol->vbv_buffer_size = (vol->first_half_vbv_buffer_size << 15) |
+ vol->latter_half_vbv_buffer_size;
+
+ vol->first_half_vbv_occupancy =
+ gst_bit_reader_get_bits_uint16_unchecked (&br, 11);
+ MARKER_UNCHECKED (&br);
+
+ vol->latter_half_vbv_occupancy =
+ gst_bit_reader_get_bits_uint16_unchecked (&br, 15);
+ MARKER_UNCHECKED (&br);
+ }
+ }
+
+ READ_UINT8 (&br, tmp, 2);
+ vol->shape = tmp;
+
+ if (vol->shape == GST_MPEG4_GRAYSCALE) {
+ /* TODO support grayscale shapes, for now we just pass */
+
+ /* Something the standard starts to define... */
+ GST_ERROR ("Grayscale shaped not supported");
+ goto failed;
+ }
+
+ if (vol->shape == GST_MPEG4_GRAYSCALE && vol->verid != 0x01)
+ READ_UINT8 (&br, vol->shape_extension, 4);
+ CHECK_REMAINING (&br, 19);
+
+ MARKER_UNCHECKED (&br);
+ vol->vop_time_increment_resolution =
+ gst_bit_reader_get_bits_uint16_unchecked (&br, 16);
+ if (vol->vop_time_increment_resolution < 1) {
+ GST_ERROR ("value not in allowed range. value: %d, range %d-%d",
+ vol->vop_time_increment_resolution, 1, G_MAXUINT16);
+ goto failed;
+ }
+ vol->vop_time_increment_bits =
+ g_bit_storage (vol->vop_time_increment_resolution);
+ MARKER_UNCHECKED (&br);
+ vol->fixed_vop_rate = gst_bit_reader_get_bits_uint8_unchecked (&br, 1);
+ if (vol->fixed_vop_rate)
+ READ_UINT16 (&br, vol->fixed_vop_time_increment,
+ vol->vop_time_increment_bits);
+
+ if (vol->shape != GST_MPEG4_BINARY_ONLY) {
+ if (vol->shape == GST_MPEG4_RECTANGULAR) {
+ CHECK_REMAINING (&br, 29);
+
+ MARKER_UNCHECKED (&br);
+ vol->width = gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
+ MARKER_UNCHECKED (&br);
+ vol->height = gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
+ MARKER_UNCHECKED (&br);
+ }
+
+ READ_UINT8 (&br, vol->interlaced, 1);
+ READ_UINT8 (&br, vol->obmc_disable, 1);
+
+ if (vol->verid == 0x1) {
+ READ_UINT8 (&br, tmp, 1);
+ vol->sprite_enable = tmp;
+ } else {
+ READ_UINT8 (&br, tmp, 2);
+ vol->sprite_enable = tmp;
+ }
+ if (vol->sprite_enable == GST_MPEG4_SPRITE_STATIC ||
+ vol->sprite_enable == GST_MPEG4_SPRITE_GMG) {
+
+ if (vol->sprite_enable == GST_MPEG4_SPRITE_GMG)
+ CHECK_REMAINING (&br, 9);
+ else {
+ CHECK_REMAINING (&br, 65);
+
+ vol->sprite_width = gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
+ MARKER_UNCHECKED (&br);
+ vol->sprite_height = gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
+ MARKER_UNCHECKED (&br);
+
+ vol->sprite_left_coordinate =
+ gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
+ MARKER_UNCHECKED (&br);
+
+ vol->sprite_top_coordinate =
+ gst_bit_reader_get_bits_uint16_unchecked (&br, 13);
+ MARKER_UNCHECKED (&br);
+ }
+ vol->no_of_sprite_warping_points =
+ gst_bit_reader_get_bits_uint8_unchecked (&br, 6);
+ vol->sprite_warping_accuracy =
+ gst_bit_reader_get_bits_uint8_unchecked (&br, 2);
+ vol->sprite_brightness_change =
+ gst_bit_reader_get_bits_uint8_unchecked (&br, 1);
+ if (vol->sprite_enable != GST_MPEG4_SPRITE_GMG) {
+ vol->low_latency_sprite_enable =
+ gst_bit_reader_get_bits_uint8_unchecked (&br, 1);
+ }
+ }
+
+ if (vol->shape != GST_MPEG4_RECTANGULAR) {
+ READ_UINT8 (&br, vol->sadct_disable, 1);
+ }
+ READ_UINT8 (&br, vol->not_8_bit, 1);
+ if (vol->not_8_bit) {
+ READ_UINT8 (&br, vol->quant_precision, 4);
+ CHECK_ALLOWED (vol->quant_precision, 3, 9);
+ READ_UINT8 (&br, vol->bits_per_pixel, 4);
+ CHECK_ALLOWED (vol->bits_per_pixel, 4, 12);
+ }
+
+ if (vol->shape == GST_MPEG4_GRAYSCALE) {
+ /* We don't actually support it */
+ READ_UINT8 (&br, vol->no_gray_quant_update, 1);
+ READ_UINT8 (&br, vol->composition_method, 1);
+ READ_UINT8 (&br, vol->linear_composition, 1);
+ }
+
+ READ_UINT8 (&br, vol->quant_type, 1);
+ if (vol->quant_type) {
+ if (!parse_quant (&br, vol->intra_quant_mat, default_intra_quant_mat,
+ &vol->load_intra_quant_mat))
+ goto failed;
+
+ if (!parse_quant (&br, vol->non_intra_quant_mat,
+ default_non_intra_quant_mat, &vol->load_non_intra_quant_mat))
+ goto failed;
+
+ if (vol->shape == GST_MPEG4_GRAYSCALE) {
+ /* Something the standard starts to define... */
+ GST_ERROR ("Grayscale shaped not supported");
+ goto failed;
+ }
+
+ } else {
+ memset (&vol->intra_quant_mat, 0, 64);
+ memset (&vol->non_intra_quant_mat, 0, 64);
+ }
+
+ if (vol->verid != 0x1)
+ READ_UINT8 (&br, vol->quarter_sample, 1);
+
+ READ_UINT8 (&br, vol->complexity_estimation_disable, 1);
+ if (!vol->complexity_estimation_disable)
+ goto complexity_estimation_error;
+
+
+ READ_UINT8 (&br, vol->resync_marker_disable, 1);
+ READ_UINT8 (&br, vol->data_partitioned, 1);
+ GST_DEBUG("reading vol->data_partitioned 1 bit = %x ",vol->data_partitioned);
+ if (vol->data_partitioned)
+ {
+ READ_UINT8 (&br, vol->reversible_vlc, 1);
+ }
+ }
+ /* ... */
+
+ return GST_MPEG4_PARSER_OK;
+
+failed:
+ GST_ERROR ("failed parsing \"Video Object Layer\"");
+ return GST_MPEG4_PARSER_ERROR;
+
+wrong_start_code:
+ GST_ERROR ("got buffer with wrong start code");
+ goto failed;
+
+complexity_estimation_error:
+ GST_ERROR ("don't support complexity estimation");
+ goto failed;
+}
+static GstMpeg4ParseResult
+gst_mpeg4_parse_visual_object (GstMpeg4VisualObject * vo, const guint8 * data, gsize size)
+{
+ guint8 vo_start_code, type;
+ GstBitReader br = GST_BIT_READER_INIT (data, size);
+
+ g_return_val_if_fail (vo != NULL, GST_MPEG4_PARSER_ERROR);
+
+ GST_DEBUG ("Parsing visual object");
+
+ READ_UINT8 (&br, vo_start_code, 8);
+ if (vo_start_code != 0xb5)
+ goto wrong_start_code;
+
+ /* set default values */
+ vo->verid = 0x1;
+ vo->priority = 1;
+ READ_UINT8 (&br, vo->is_identifier, 1);
+ if (vo->is_identifier) {
+ READ_UINT8 (&br, vo->verid, 4);
+ READ_UINT8 (&br, vo->priority, 3);
+ }
+
+ READ_UINT8 (&br, type, 4);
+ vo->type = type;
+ return GST_MPEG4_PARSER_OK;
+
+wrong_start_code:
+ GST_ERROR ("got buffer with wrong start code");
+ return GST_MPEG4_PARSER_ERROR;
+
+failed:
+ GST_ERROR ("failed parsing \"Visual Object\"");
+ return GST_MPEG4_PARSER_ERROR;
+}
+#endif
static void
gst_qtdemux_stbl_free (QtDemuxStream * stream)
@@ -1956,34 +3088,115 @@ gst_qtdemux_stbl_free (QtDemuxStream * stream)
stream->stts.data = NULL;
g_free ((gpointer) stream->stss.data);
stream->stss.data = NULL;
- g_free ((gpointer) stream->stps.data);
- stream->stps.data = NULL;
g_free ((gpointer) stream->ctts.data);
stream->ctts.data = NULL;
}
+#ifdef QTDEMUX_MODIFICATION
static void
-gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
+gst_qtdemux_stream_clear (GstQTDemux *qtdemux, QtDemuxStream *stream)
{
while (stream->buffers) {
gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
}
- if (stream->pad)
- gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
+
+#ifdef DRM_ENABLE
+ if (qtdemux->encrypt_content && qtdemux->dash_content == FALSE) {
+ int i =0;
+
+ for (i = 0 ; i < stream->n_samples; i++) {
+ if (stream->samples[i].sub_encry) {
+ if (stream->samples[i].sub_encry->sub_entry) {
+ g_free (stream->samples[i].sub_encry->sub_entry);
+ stream->samples[i].sub_encry->sub_entry = NULL;
+ }
+ free (stream->samples[i].sub_encry);
+ stream->samples[i].sub_encry = NULL;
+ }
+ free(stream->samples[i].iv);
+ stream->samples[i].iv = NULL;
+ }
+ }
+#endif
+
+ if (qtdemux->piff_fragmented && stream->frag_queue) {
+ while (!g_queue_is_empty (stream->frag_queue)) {
+ GstBuffer *buf = g_queue_pop_head (stream->frag_queue);
+ gst_buffer_unref (buf);
+ }
+ }
+
g_free (stream->samples);
- if (stream->caps)
- gst_caps_unref (stream->caps);
+ stream->samples = NULL;
g_free (stream->segments);
+ stream->segments = NULL;
+
if (stream->pending_tags)
gst_tag_list_free (stream->pending_tags);
+ stream->pending_tags = NULL;
g_free (stream->redirect_uri);
+ stream->redirect_uri = NULL;
/* free stbl sub-atoms */
gst_qtdemux_stbl_free (stream);
-#ifdef QTDEMUX_MODIFICATION
+
if (stream->trickplay_info)
g_free (stream->trickplay_info);
+ stream->trickplay_info = NULL;
+
+ stream->last_ret = GST_FLOW_OK;
+ stream->sent_eos = FALSE;
+ stream->segment_index = -1;
+ stream->time_position = 0;
+ stream->sample_index = -1;
+ stream->stbl_index = -1;
+ stream->n_samples = 0;
+}
#endif
+
+static void
+gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream)
+{
+#ifdef QTDEMUX_MODIFICATION
+ gst_qtdemux_stream_clear(qtdemux, stream);
+
+ if (qtdemux->piff_fragmented && stream->frag_queue) {
+ g_queue_free (stream->frag_queue);
+ stream->frag_queue = NULL;
+ }
+#else
+ while (stream->buffers) {
+ gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data));
+ stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
+ }
+
+ g_free (stream->samples);
+ stream->samples = NULL;
+ g_free (stream->segments);
+ stream->segments = NULL;
+
+ if (stream->pending_tags)
+ gst_tag_list_free (stream->pending_tags);
+ stream->pending_tags = NULL;
+ g_free (stream->redirect_uri);
+ stream->redirect_uri = NULL;
+ /* free stbl sub-atoms */
+ gst_qtdemux_stbl_free (stream);
+
+ stream->last_ret = GST_FLOW_OK;
+ stream->sent_eos = FALSE;
+ stream->segment_index = -1;
+ stream->time_position = 0;
+ stream->sample_index = -1;
+ stream->stbl_index = -1;
+ stream->n_samples = 0;
+#endif
+
+ if (stream->caps)
+ gst_caps_unref (stream->caps);
+ if (stream->pad)
+ gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
+
g_free (stream);
}
@@ -2004,6 +3217,9 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:{
+#ifdef QTDEMUX_MODIFICATION
+ gst_qtdemux_reset(qtdemux,TRUE);
+#else
gint n;
qtdemux->state = QTDEMUX_STATE_INITIAL;
@@ -2016,6 +3232,13 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
qtdemux->header_size = 0;
qtdemux->got_moov = FALSE;
qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
+#ifdef MULTI_AUDIO
+ if(qtdemux->Language_list)
+ {
+ g_list_free(qtdemux->Language_list);
+ qtdemux->Language_list = NULL;
+ }
+#endif
if (qtdemux->mdatbuffer)
gst_buffer_unref (qtdemux->mdatbuffer);
qtdemux->mdatbuffer = NULL;
@@ -2029,19 +3252,7 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
gst_object_unref (qtdemux->element_index);
qtdemux->element_index = NULL;
gst_adapter_clear (qtdemux->adapter);
-#ifdef QTDEMUX_MODIFICATION
- // TODO: Check new modifications
- if (qtdemux->file) {
- fclose (qtdemux->file);
- if (qtdemux->ofile)
- fclose (qtdemux->ofile);
- remove (qtdemux->filename);
- }
- qtdemux->file = NULL;
- qtdemux->ofile = NULL;
- qtdemux->filesize = 0;
- qtdemux->maxbuffersize = QTDEMUX_MAX_BUFFER_SIZE;
-#endif
+
for (n = 0; n < qtdemux->n_streams; n++) {
gst_qtdemux_stream_free (qtdemux, qtdemux->streams[n]);
qtdemux->streams[n] = NULL;
@@ -2056,6 +3267,7 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
qtdemux->seek_offset = 0;
qtdemux->upstream_seekable = FALSE;
qtdemux->upstream_size = 0;
+#endif
break;
}
default:
@@ -2092,8 +3304,26 @@ qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
qtdemux->major_brand = QT_FOURCC (buffer + 8);
GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (qtdemux->major_brand));
+
+#ifdef QTDEMUX_MODIFICATION
+ /* checking for piff major brand */
+ if (qtdemux->major_brand == GST_MAKE_FOURCC ('i', 's', 'm', 'l') ||
+ qtdemux->major_brand == GST_MAKE_FOURCC ('p', 'i', 'f', 'f')) {
+ GST_INFO_OBJECT (qtdemux, "clip has PIFF as major brand");
+ qtdemux->piff_fragmented = TRUE;
+ }
+#endif
+
buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16);
memcpy (GST_BUFFER_DATA (buf), buffer + 16, GST_BUFFER_SIZE (buf));
+
+#ifdef QTDEMUX_MODIFICATION
+ /* checking for piff major brand */
+ if (g_strrstr_len (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), "piff")) {
+ GST_INFO_OBJECT (qtdemux, "clip has PIFF as compatible brand");
+ qtdemux->piff_fragmented = TRUE;
+ }
+#endif
}
}
@@ -2297,6 +3527,8 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
stream->track_id, d_sample_duration, d_sample_size, d_sample_flags,
*base_offset);
+ qtdemux->sequence_id = stream->track_id;
+ GST_DEBUG("sequence id is %d", qtdemux->sequence_id);
/* presence of stss or not can't really tell us much,
* and flags and so on tend to be marginally reliable in these files */
if (stream->subtype == FOURCC_soun) {
@@ -2321,6 +3553,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
if (*base_offset == -1) {
GST_LOG_OBJECT (qtdemux, "base_offset at moof");
*base_offset = moof_offset;
+ ismv=TRUE;/*according to piff specification base_offset should be omitted*/
}
*running_offset = *base_offset + data_offset;
} else {
@@ -2401,9 +3634,21 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
goto out_of_memory;
if (G_UNLIKELY (stream->n_samples == 0)) {
+#ifdef QTDEMUX_MODIFICATION
+ if(qtdemux->is_dash && GST_CLOCK_TIME_IS_VALID(qtdemux->dash_init_offset))
+ timestamp = gst_util_uint64_scale(qtdemux->dash_init_offset, stream->timescale, GST_SECOND);
+ else if (qtdemux->fragmented) {
+ timestamp = stream->moof_seeked_time;
+ } else {
+ /* the timestamp of the first sample is also provided by the tfra entry
+ * but we shouldnt rely on it as it is at the end of files */
+ timestamp = 0;
+ }
+#else
/* the timestamp of the first sample is also provided by the tfra entry
* but we shouldn't rely on it as it is at the end of files */
timestamp = 0;
+#endif
} else {
/* subsequent fragments extend stream */
timestamp =
@@ -2453,11 +3698,20 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun,
/* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe,
* now idea how it relates to bitfield other than massive LE/BE confusion */
sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000);
+
+ /* For a video track, the first sample in a fragment MUST be an IDR frame(Key frame) according to
+ * PIFF specs (PIFF 1.1, page no. 17) */
+ if (ismv && stream->subtype == FOURCC_vide && i == 0) {
+ sample->keyframe = TRUE;
+ }
+
*running_offset += size;
timestamp += dur;
sample++;
}
-
+#ifdef QTDEMUX_MODIFICATION
+ stream->prev_n_samples = stream->n_samples;
+#endif
stream->n_samples += samples_count;
return TRUE;
@@ -2570,35 +3824,231 @@ unknown_stream:
return TRUE;
}
}
+#ifdef QTDEMUX_MODIFICATION
+static gboolean
+qtdemux_parse_sample_encryption(GstQTDemux * qtdemux, GstByteReader *sample_encrypt, QtDemuxStream * stream)
+{
+ guint32 flags = 0;
+ guint32 sample_count = 0;
+ guint32 i = 0;
+ guint32 algo_id;
+ guint8 iv_size = 0;
+
+ if (!gst_byte_reader_skip (sample_encrypt, 1) ||
+ !gst_byte_reader_get_uint24_be (sample_encrypt, &flags))
+ goto invalid_encryption;
+
+ if (flags & SE_OVERRIDE_TE_FLAGS) {
+ /* get algorithm id */
+ if (!gst_byte_reader_get_uint32_be (sample_encrypt, &algo_id))
+ goto invalid_encryption;
+
+ /* get IV size */
+ if (!gst_byte_reader_get_uint8 (sample_encrypt, &iv_size))
+ goto invalid_encryption;
+
+ GST_DEBUG_OBJECT(qtdemux,"iv is %2x",iv_size);
+ // TODO: need to add reading of KID
+ } else {
+ GST_INFO_OBJECT (qtdemux, "Override flags are not present... taking default IV_Size = 8");
+ iv_size = 8;
+ }
+
+ /* Get sample count*/
+ if (!gst_byte_reader_get_uint32_be (sample_encrypt, &sample_count))
+ goto invalid_encryption;
+
+ GST_INFO_OBJECT (qtdemux, "Sample count = %d", sample_count);
+
+#if 0
+ if (sample_count != stream->n_samples) {
+ GST_ERROR_OBJECT (qtdemux, "Not all samples has IV vectors... Don't know how to handle. sample_cnt = %d and stream->n_samples = %d",
+ sample_count, stream->n_samples);
+ goto invalid_encryption;
+ }
+#endif
+
+ for (i = stream->prev_n_samples; i < stream->n_samples; i++) {
+ guint8 iv_idx = iv_size;
+
+ /* resetting entire IV array */
+ stream->samples[i].iv = (guint8 *) malloc (iv_size);
+ if (NULL == stream->samples[i].iv) {
+ GST_ERROR_OBJECT (qtdemux, "Failed to allocate memory...\n");
+ GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory"));
+ return FALSE;
+ }
+ stream->samples[i].sub_encry = NULL;
+ memset (stream->samples[i].iv, 0x00, iv_size);
+
+ iv_idx = 0;
+ while (iv_idx < iv_size) {
+ /* get IV byte */
+ if (!gst_byte_reader_get_uint8 (sample_encrypt, &(stream->samples[i].iv[iv_idx])))
+ goto invalid_encryption;
+
+ iv_idx++;
+ }
+
+#ifdef DEBUG_IV
+ {
+ guint8 tmp_idx = 0;
+ g_print ("sample[%d] : 0x ", i);
+
+ while (tmp_idx < iv_size ) {
+ g_print ("%02x ", stream->samples[i].iv[tmp_idx]);
+ tmp_idx++;
+ }
+ g_print ("\n");
+ }
+#endif
+
+ if (flags & SE_USE_SUBSAMPLE_ENCRYPTION) {
+ guint16 n_entries;
+ guint16 n_idx;
+
+ /* NumberofEntries in SubSampleEncryption */
+ if (!gst_byte_reader_get_uint16_be (sample_encrypt, &n_entries))
+ goto invalid_encryption;
+
+ stream->samples[i].sub_encry = (QtDemuxSubSampleEncryption *) malloc (sizeof (QtDemuxSubSampleEncryption));
+ if (NULL == stream->samples[i].sub_encry) {
+ GST_ERROR_OBJECT (qtdemux, "Failed to allocate memory...\n");
+ GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory"));
+ return FALSE;
+ }
+
+ stream->samples[i].sub_encry->sub_entry = g_try_new0 (QtDemuxSubSampleEntryInfo, n_entries);
+ if (NULL == stream->samples[i].sub_encry->sub_entry) {
+ GST_ERROR_OBJECT (qtdemux, "Failed to allocate memory...\n");
+ GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory"));
+ return FALSE;
+ }
+
+ stream->samples[i].sub_encry->n_entries = n_entries;
+
+ GST_DEBUG_OBJECT (qtdemux,"No. of subsample entries = %d", stream->samples[i].sub_encry->n_entries);
+
+ for (n_idx = 0; n_idx < n_entries; n_idx++) {
+ if (!gst_byte_reader_get_uint16_be (sample_encrypt, &(stream->samples[i].sub_encry->sub_entry[n_idx].LenofClearData)))
+ goto invalid_encryption;
+
+ GST_DEBUG_OBJECT (qtdemux,"entry[%d] and lengthofClearData = %d", n_idx, stream->samples[i].sub_encry->sub_entry[n_idx].LenofClearData);
+
+ if (!gst_byte_reader_get_uint32_be (sample_encrypt, &(stream->samples[i].sub_encry->sub_entry[n_idx].LenofEncryptData)))
+ goto invalid_encryption;
+
+ GST_DEBUG_OBJECT (qtdemux,"entry[%d] and lengthofEncryptData = %d", n_idx, stream->samples[i].sub_encry->sub_entry[n_idx].LenofEncryptData);
+ }
+ }
+ }
+
+ return TRUE;
+
+invalid_encryption:
+ {
+ GST_ERROR_OBJECT (qtdemux, "invalid sample encryption header");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("invalid encryption"), (NULL));
+ return FALSE;
+ }
+}
+#endif
+
+static gboolean
+qtdemux_parse_tfdt (GstQTDemux * qtdemux, GstByteReader * br,
+ guint64 * decode_time)
+{
+ guint32 version = 0;
+
+ if (!gst_byte_reader_get_uint32_be (br, &version))
+ return FALSE;
+
+ version >>= 24;
+ if (version == 1) {
+ if (!gst_byte_reader_get_uint64_be (br, decode_time))
+ goto failed;
+ } else {
+ guint32 dec_time = 0;
+ if (!gst_byte_reader_get_uint32_be (br, &dec_time))
+ goto failed;
+ *decode_time = dec_time;
+ }
+
+ GST_INFO_OBJECT (qtdemux, "Track fragment decode time: %" G_GUINT64_FORMAT,
+ *decode_time);
+
+ return TRUE;
+
+failed:
+ {
+ GST_DEBUG_OBJECT (qtdemux, "parsing tfdt failed");
+ return FALSE;
+ }
+}
static gboolean
qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
guint64 moof_offset, QtDemuxStream * stream)
{
- GNode *moof_node, *traf_node, *tfhd_node, *trun_node;
- GstByteReader trun_data, tfhd_data;
+ GNode *moof_node, *traf_node, *tfhd_node, *trun_node, *tfdt_node, *senc_node;
+
+ GstByteReader trun_data, tfhd_data, tfdt_data;
guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
gint64 base_offset, running_offset;
-
+#ifdef QTDEMUX_MODIFICATION
+ GNode *uuid_node;
+ GstByteReader uuid_data;
+ gint64 uuid_offset=-1;
+ gboolean bret = FALSE;
+#endif
/* NOTE @stream ignored */
moof_node = g_node_new ((guint8 *) buffer);
+#ifdef QTDEMUX_MODIFICATION
+ GST_DEBUG_OBJECT(qtdemux, "in parse moof");
+ bret = qtdemux_parse_node (qtdemux, moof_node, buffer, length);
+ if(!bret) {
+ GST_ERROR_OBJECT(qtdemux, "failed to parse node object");
+ return bret;
+ }
+#else
qtdemux_parse_node (qtdemux, moof_node, buffer, length);
- qtdemux_node_dump (qtdemux, moof_node);
+#endif
+ //qtdemux_node_dump (qtdemux, moof_node);
/* unknown base_offset to start with */
base_offset = running_offset = -1;
traf_node = qtdemux_tree_get_child_by_type (moof_node, FOURCC_traf);
while (traf_node) {
/* Fragment Header node */
+ GST_DEBUG("Traf node detected");
tfhd_node =
qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
&tfhd_data);
if (!tfhd_node)
goto missing_tfhd;
+ else
+ GST_DEBUG("tfhd node detected");
if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
&ds_size, &ds_flags, &base_offset))
goto missing_tfhd;
+ tfdt_node =
+ qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfdt,
+ &tfdt_data);
+ if (tfdt_node) {
+ guint64 decode_time = 0;
+ GST_DEBUG("tfdt node detected");
+ qtdemux_parse_tfdt (qtdemux, &tfdt_data, &decode_time);
+ /* If there is a new segment pending, update the time/position */
+ if (qtdemux->pending_newsegment) {
+ gst_event_replace (&qtdemux->pending_newsegment,
+ gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
+ 0, GST_CLOCK_TIME_NONE,
+ gst_util_uint64_scale (decode_time,
+ GST_SECOND, stream->timescale)));
+ }
+ }
+
if (G_UNLIKELY (!stream)) {
/* we lost track of offset, we'll need to regain it,
* but can delay complaining until later or avoid doing so altogether */
@@ -2615,10 +4065,134 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
qtdemux_parse_trun (qtdemux, &trun_data, stream,
ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
&running_offset);
+ GST_DEBUG("trun node detected");
/* iterate all siblings */
trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
&trun_data);
}
+
+#ifdef QTDEMUX_MODIFICATION
+#ifdef DRM_ENABLE
+ uuid_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_uuid);
+
+ while (uuid_node) {
+ uuid_type_t uuid_type;
+
+ guint8 *buffer = (guint8 *) uuid_node->data;
+
+ GST_DEBUG_OBJECT(qtdemux, "uuid node detected");
+
+ gst_byte_reader_init (&uuid_data, buffer, QT_UINT32 (buffer));
+
+ uuid_type = qtdemux_get_uuid_type (qtdemux, &uuid_data, &uuid_offset);
+
+ if (UUID_SAMPLE_ENCRYPT == uuid_type) {
+ if (!qtdemux_parse_sample_encryption (qtdemux, &uuid_data, stream))
+ goto fail;
+ } else {
+ GST_WARNING_OBJECT (qtdemux, "Ignoring Wrong UUID...");
+ }
+
+ /* iterate all siblings */
+ uuid_node = qtdemux_tree_get_sibling_by_type (uuid_node, FOURCC_uuid);
+ }
+#endif //DRM_ENABLE
+
+ senc_node = qtdemux_tree_get_child_by_type (traf_node, FOURCC_senc);
+ if(senc_node) {
+ guint8 *buffer = (guint8 *) senc_node->data;
+ int i = 0;
+ int j = 0;
+
+ GST_INFO_OBJECT(qtdemux, "senc node detected...");
+ qtdemux->iv_size_video = 8;
+ qtdemux->iv_size_audio = 8;
+
+ /* video stream will have even sequence_id and audio will have odd sequence_id */
+ if(qtdemux->sequence_id%2 == 0) {
+
+ GST_DEBUG_OBJECT(qtdemux, "iv size is %d", qtdemux->iv_size_video);
+
+ qtdemux->sample_count[qtdemux->sequence_id - 1] = QT_UINT32 (buffer + 12);
+ GST_DEBUG("sample count for video is %d", qtdemux->sample_count[qtdemux->sequence_id - 1]);
+
+ qtdemux->iv_data_video = (gchar**)malloc(sizeof(gchar*) * qtdemux->sample_count[qtdemux->sequence_id - 1]);
+ if (NULL == qtdemux->iv_data_video) {
+ GST_ERROR_OBJECT (qtdemux, "failed to allocate memory for iv_data_video...");
+ goto fail;
+ }
+
+ qtdemux->no_of_video_samples = qtdemux->sample_count[qtdemux->sequence_id - 1];
+
+ for(i = 0 ; i < qtdemux->sample_count[qtdemux->sequence_id - 1] ; i++) {
+ qtdemux->iv_data_video[i] = (gchar*)malloc(sizeof(gchar) * qtdemux->iv_size_video);
+ if(NULL == qtdemux->iv_data_video[i]) {
+ GST_ERROR_OBJECT (qtdemux, "failed to allocate memory for iv_data_video index...");
+ goto fail;
+ }
+ }
+
+ qtdemux->sub_sample_count = (gint*)malloc(sizeof(gint) * qtdemux->sample_count[qtdemux->sequence_id - 1]);
+ if (NULL == qtdemux->sub_sample_count) {
+ GST_ERROR_OBJECT (qtdemux, "failed to allocate memory for sub_sample_count...");
+ goto fail;
+ }
+
+ qtdemux->clear_data = (gint*)malloc(sizeof(gint) * qtdemux->sample_count[qtdemux->sequence_id - 1]);
+ if (NULL == qtdemux->clear_data) {
+ GST_ERROR_OBJECT (qtdemux, "failed to allocate memory for clear_data...");
+ goto fail;
+ }
+
+ qtdemux->encrypted_data = (gint*)malloc(sizeof(gint) * qtdemux->sample_count[qtdemux->sequence_id - 1]);
+ if (NULL == qtdemux->encrypted_data) {
+ GST_ERROR_OBJECT (qtdemux, "failed to allocate memory for encrypted_data...");
+ goto fail;
+ }
+
+ for(i = 0 ; i < qtdemux->sample_count[qtdemux->sequence_id - 1] ; i++) {
+ for(j = 0 ; j < qtdemux->iv_size_video ; j++) {
+ qtdemux->iv_data_video[i][j] = buffer[16 + (qtdemux->iv_size_video * i * 2) + j];
+ }
+ qtdemux->sub_sample_count[i] = QT_UINT16 (buffer + 16 + (qtdemux->iv_size_video * i * 2) + qtdemux->iv_size_video);
+ qtdemux->clear_data[i] = QT_UINT16 (buffer + 16 + (qtdemux->iv_size_video * i * 2) + qtdemux->iv_size_video + 2);
+ qtdemux->encrypted_data[i] = QT_UINT32 (buffer + 16 + (qtdemux->iv_size_video * i * 2) + qtdemux->iv_size_video + 2 + 2);
+ }
+ qtdemux->current_sample[0] = 0;
+
+ } else if(qtdemux->sequence_id%2 == 1) {
+
+ GST_DEBUG_OBJECT(qtdemux, "iv size is %d", qtdemux->iv_size_audio);
+
+ qtdemux->sample_count[qtdemux->sequence_id - 1] = QT_UINT32 (buffer + 12);
+
+ GST_INFO_OBJECT(qtdemux, "sample count for audio is %d", qtdemux->sample_count[qtdemux->sequence_id - 1]);
+
+ qtdemux->iv_data_audio = (gchar**)malloc(sizeof(gchar*) * qtdemux->sample_count[qtdemux->sequence_id - 1]);
+ if (NULL == qtdemux->iv_data_audio) {
+ GST_ERROR_OBJECT (qtdemux, "failed to allocate memory for iv_data_audio...");
+ goto fail;
+ }
+
+ qtdemux->no_of_audio_samples = qtdemux->sample_count[qtdemux->sequence_id - 1];
+
+ for(i = 0 ; i < qtdemux->sample_count[qtdemux->sequence_id - 1] ; i++) {
+ qtdemux->iv_data_audio[i] = (gchar*)malloc(sizeof(gchar) * qtdemux->iv_size_audio);
+ if(NULL == qtdemux->iv_data_audio[i]) {
+ GST_ERROR_OBJECT (qtdemux, "failed to allocate memory for iv_data_audio index...");
+ goto fail;
+ }
+ }
+
+ for(i = 0 ; i < qtdemux->sample_count[qtdemux->sequence_id - 1] ; i++) {
+ for(j = 0 ; j < qtdemux->iv_size_audio ; j++) {
+ qtdemux->iv_data_audio[i][j] = buffer[16 + (qtdemux->iv_size_audio * i) + j];
+ }
+ }
+ qtdemux->current_sample[1] = 0;
+ }
+ }
+#endif
/* if no new base_offset provided for next traf,
* base is end of current traf */
base_offset = running_offset;
@@ -2649,21 +4223,25 @@ fail:
}
}
+#ifdef QTDEMUX_MODIFICATION
+
/* might be used if some day we actually use mfra & co
* for random access to fragments,
* but that will require quite some modifications and much less relying
* on a sample array */
-#if 0
static gboolean
-qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node,
- QtDemuxStream * stream)
+qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node)
{
guint64 time = 0, moof_offset = 0;
guint32 ver_flags, track_id, len, num_entries, i;
guint value_size, traf_size, trun_size, sample_size;
- GstBuffer *buf = NULL;
- GstFlowReturn ret;
GstByteReader tfra;
+ guint n = 0;
+ QtDemuxStream *stream = NULL;
+ gboolean found_track = FALSE;
+ QtDemuxTfraTable *tfra_table = NULL;
+
+ GST_DEBUG_OBJECT (qtdemux, "Starting tfra parsing...");
gst_byte_reader_init (&tfra, (guint8 *) tfra_node->data + (4 + 4),
QT_UINT32 ((guint8 *) tfra_node->data) - (4 + 4));
@@ -2676,44 +4254,67 @@ qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node,
gst_byte_reader_get_uint32_be (&tfra, &num_entries)))
return FALSE;
- GST_LOG_OBJECT (qtdemux, "id %d == stream id %d ?",
- track_id, stream->track_id);
- if (track_id != stream->track_id) {
+ for (n = 0; n < qtdemux->n_streams; n++) {
+ stream = qtdemux->streams[n];
+
+ if (stream->track_id == track_id) {
+ GST_INFO_OBJECT (qtdemux, "found stream with track_id = %d", track_id);
+ found_track = TRUE;
+ break;
+ }
+ }
+
+ if (!stream || !found_track) {
+ GST_WARNING_OBJECT (qtdemux, "not able to parse tfra...");
return FALSE;
}
+ GST_LOG_OBJECT (qtdemux, "id %d == stream id %d ?",
+ track_id, stream->track_id);
+
+ GST_INFO_OBJECT (stream->pad, "number of tfra entries = %u", num_entries);
+
+ if (num_entries == 0)
+ goto no_samples;
+
+ tfra_table = g_new0 (QtDemuxTfraTable, 1);
+ tfra_table->n_entries = num_entries;
+ tfra_table->tfra_entries = g_array_sized_new (FALSE, FALSE, sizeof (QtDemuxTfraEntryInfo), num_entries);
+
value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
sample_size = (len & 3) + 1;
trun_size = ((len & 12) >> 2) + 1;
traf_size = ((len & 48) >> 4) + 1;
- if (num_entries == 0)
- goto no_samples;
+ GST_DEBUG_OBJECT (stream->pad, "value_size = %u, sample_size = %u, trun_size = %u and traf_size = %u",
+ value_size, sample_size, trun_size, traf_size);
if (!qt_atom_parser_has_chunks (&tfra, num_entries,
value_size + value_size + traf_size + trun_size + sample_size))
goto corrupt_file;
for (i = 0; i < num_entries; i++) {
+ QtDemuxTfraEntryInfo entry = {0, };
+
qt_atom_parser_get_offset (&tfra, value_size, &time);
qt_atom_parser_get_offset (&tfra, value_size, &moof_offset);
qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size);
qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size);
qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size);
- GST_LOG_OBJECT (qtdemux,
- "fragment time: %" GST_TIME_FORMAT " moof_offset: %u",
- GST_TIME_ARGS (gst_util_uint64_scale (time, GST_SECOND,
- stream->timescale)), moof_offset);
+ entry.time = time;
+ entry.moof_offset = moof_offset;
+ g_array_append_val (tfra_table->tfra_entries, entry);
- ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf);
- if (ret != GST_FLOW_OK)
- goto corrupt_file;
- qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
- moof_offset, stream);
- gst_buffer_unref (buf);
+ GST_LOG_OBJECT (stream->pad,
+ "fragment time apend: %" GST_TIME_FORMAT " moof_offset: %"G_GUINT64_FORMAT,
+ GST_TIME_ARGS (gst_util_uint64_scale (time, GST_SECOND, stream->timescale)), entry.moof_offset);
}
+ stream->tfra_table = tfra_table;
+
+ GST_DEBUG_OBJECT (qtdemux, "successfully built tfra table...");
+
return TRUE;
/* ERRORS */
@@ -2730,41 +4331,91 @@ no_samples:
}
}
-static gboolean
-qtdemux_parse_mfra (GstQTDemux * qtdemux, QtDemuxStream * stream)
+
+static gint
+gst_qtdemux_moof_offset_compare (QtDemuxTfraEntryInfo * i1, QtDemuxTfraEntryInfo * i2)
{
- GstFlowReturn ret;
+ return (i1->moof_offset - i2->moof_offset);
+}
+
+static GstFlowReturn
+qtdemux_parse_mfra (GstQTDemux * qtdemux)
+{
+ GstFlowReturn ret = GST_FLOW_OK;
GNode *mfra_node, *tfra_node;
- GstBuffer *buffer;
+ gint n = 0;
+ GstBuffer *buffer = NULL;
+ gboolean bret = FALSE;
- if (!qtdemux->mfra_offset)
- return FALSE;
+ GST_DEBUG_OBJECT (qtdemux, "Starting mfra atom parsing...");
ret = gst_qtdemux_pull_atom (qtdemux, qtdemux->mfra_offset, 0, &buffer);
if (ret != GST_FLOW_OK)
goto corrupt_file;
mfra_node = g_node_new ((guint8 *) GST_BUFFER_DATA (buffer));
- qtdemux_parse_node (qtdemux, mfra_node, GST_BUFFER_DATA (buffer),
- GST_BUFFER_SIZE (buffer));
+ qtdemux_parse_node (qtdemux, mfra_node, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra);
while (tfra_node) {
- qtdemux_parse_tfra (qtdemux, tfra_node, stream);
+ bret = qtdemux_parse_tfra (qtdemux, tfra_node);
+ if (!bret) {
+ GST_WARNING_OBJECT (qtdemux, "issue in parsing tfra atom..");
+ break;
+ }
/* iterate all siblings */
tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra);
}
+
+ if (!bret) {
+ GST_WARNING_OBJECT (qtdemux, "ignore parsing of mfra atom because of errors");
+ goto exit;
+ }
+
+ qtdemux->moof_offsets = g_array_new (FALSE, FALSE, sizeof (QtDemuxTfraEntryInfo));
+
+ /* prepare consolidated moof_offset_table */
+ for (n = 0; n < qtdemux->n_streams; n++) {
+ QtDemuxStream *stream = qtdemux->streams[n];
+ QtDemuxTfraEntryInfo *entry = NULL;
+
+ if (stream->tfra_table) {
+ guint32 idx = 0;
+ guint32 prev_len = qtdemux->moof_offsets->len;
+
+ GST_INFO_OBJECT (stream->pad, "prev len = %d and entries = %d", prev_len, stream->tfra_table->n_entries);
+
+ for (idx = 0; idx < stream->tfra_table->n_entries; idx++) {
+ QtDemuxTfraEntryInfo new_entry = {0, };
+ entry = &g_array_index (stream->tfra_table->tfra_entries, QtDemuxTfraEntryInfo, idx);
+ new_entry.time = entry->time;
+ new_entry.moof_offset = entry->moof_offset;
+ g_array_append_val (qtdemux->moof_offsets, new_entry);
+ GST_DEBUG_OBJECT (stream->pad, "appended %"G_GUINT64_FORMAT" to moof_offsets", entry->moof_offset);
+ }
+ }
+ }
+
+ /* sort the moof_offsets array */
+ g_array_sort (qtdemux->moof_offsets, (GCompareFunc)gst_qtdemux_moof_offset_compare);
+
+ for (n = 0; n < qtdemux->moof_offsets->len; n++) {
+ QtDemuxTfraEntryInfo *entry = &g_array_index (qtdemux->moof_offsets, QtDemuxTfraEntryInfo, n);
+ GST_LOG_OBJECT (qtdemux, "idx = %d and moof_offset = %"G_GUINT64_FORMAT, n, entry->moof_offset);
+ }
+
+exit:
g_node_destroy (mfra_node);
gst_buffer_unref (buffer);
- return TRUE;
+ return GST_FLOW_OK;
corrupt_file:
{
GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE,
(_("This file is corrupt and cannot be played.")), (NULL));
- return FALSE;
+ return ret;
}
}
@@ -2778,6 +4429,8 @@ qtdemux_parse_mfro (GstQTDemux * qtdemux, guint64 * mfra_offset,
gint64 len;
GstFormat fmt = GST_FORMAT_BYTES;
+ GST_DEBUG_OBJECT (qtdemux, "Starting mfro atom parsing...");
+
if (!gst_pad_query_peer_duration (qtdemux->sinkpad, &fmt, &len)) {
GST_DEBUG_OBJECT (qtdemux, "upstream size not available; "
"can not locate mfro");
@@ -2789,8 +4442,10 @@ qtdemux_parse_mfro (GstQTDemux * qtdemux, guint64 * mfra_offset,
goto exit;
fourcc = QT_FOURCC (GST_BUFFER_DATA (mfro) + 4);
- if (fourcc != FOURCC_mfro)
+ if (fourcc != FOURCC_mfro) {
+ GST_WARNING_OBJECT (qtdemux, "mfro atom is not available, so mfra as well...");
goto exit;
+ }
GST_INFO_OBJECT (qtdemux, "Found mfro atom: fragmented mp4 container");
if (GST_BUFFER_SIZE (mfro) >= 16) {
@@ -2811,16 +4466,13 @@ exit:
return ret;
}
-static void
+static GstFlowReturn
qtdemux_parse_fragmented (GstQTDemux * qtdemux)
{
GstFlowReturn ret;
guint32 mfra_size = 0;
guint64 mfra_offset = 0;
- /* default */
- qtdemux->fragmented = FALSE;
-
/* We check here if it is a fragmented mp4 container */
ret = qtdemux_parse_mfro (qtdemux, &mfra_offset, &mfra_size);
if (ret == GST_FLOW_OK && mfra_size != 0 && mfra_offset != 0) {
@@ -2829,6 +4481,8 @@ qtdemux_parse_fragmented (GstQTDemux * qtdemux)
"mfra atom expected at offset %" G_GUINT64_FORMAT, mfra_offset);
qtdemux->mfra_offset = mfra_offset;
}
+
+ return ret;
}
#endif
@@ -2840,6 +4494,9 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
GstBuffer *buf = NULL;
GstFlowReturn ret = GST_FLOW_OK;
guint64 cur_offset = qtdemux->offset;
+#ifdef QTDEMUX_MODIFICATION
+ gboolean bret = FALSE;
+#endif
ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
if (G_UNLIKELY (ret != GST_FLOW_OK))
@@ -2862,15 +4519,33 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
switch (fourcc) {
case FOURCC_moof:
/* record for later parsing when needed */
+#ifdef QTDEMUX_MODIFICATION
if (!qtdemux->moof_offset) {
qtdemux->moof_offset = qtdemux->offset;
+ GST_INFO_OBJECT (qtdemux, "received first moof atom...");
+
+ if (!qtdemux->moof_offsets) {
+ GST_WARNING_OBJECT (qtdemux, "Did not receive mfra yet.. look at the end of file");
+
+ /* ignoring return value, as it is not mandatory for our seeking/playback */
+ ret = qtdemux_parse_fragmented (qtdemux);
+
+ if (qtdemux->mfra_offset) {
+ ret = qtdemux_parse_mfra (qtdemux);
+ if (ret != GST_FLOW_OK)
+ goto beach;
+ }
+ }
+ }
+#else
+ if (!qtdemux->moof_offset) {
+ qtdemux->moof_offset = qtdemux->offset;
}
+#endif
/* fall-through */
case FOURCC_mdat:
case FOURCC_free:
- case FOURCC_wide:
case FOURCC_PICT:
- case FOURCC_pnot:
{
GST_LOG_OBJECT (qtdemux,
"skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
@@ -2881,7 +4556,9 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
case FOURCC_moov:
{
GstBuffer *moov;
-
+#ifdef QTDEMUX_MODIFICATION
+ gboolean tree_ret;
+#endif
if (qtdemux->got_moov) {
GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already");
qtdemux->offset += length;
@@ -2931,10 +4608,24 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
}
qtdemux->offset += length;
- qtdemux_parse_moov (qtdemux, GST_BUFFER_DATA (moov), length);
+#ifdef QTDEMUX_MODIFICATION
+ bret = qtdemux_parse_moov (qtdemux, GST_BUFFER_DATA (moov), length);
+ if(!bret) {
+ GST_DEBUG_OBJECT(qtdemux, "Failed to parse moov atom");
+ return GST_FLOW_ERROR;
+ }
+#else
qtdemux_node_dump (qtdemux, qtdemux->moov_node);
-
+#endif
+#ifdef QTDEMUX_MODIFICATION
+ tree_ret = qtdemux_parse_tree (qtdemux);
+ if(!tree_ret) {
+ GST_ERROR_OBJECT(qtdemux,"fail to parse uuid atom in tree");
+ return GST_FLOW_ERROR;
+ }
+#else
qtdemux_parse_tree (qtdemux);
+#endif
g_node_destroy (qtdemux->moov_node);
gst_buffer_unref (moov);
qtdemux->moov_node = NULL;
@@ -2953,6 +4644,12 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
qtdemux->offset += length;
qtdemux_parse_ftyp (qtdemux, GST_BUFFER_DATA (ftyp),
GST_BUFFER_SIZE (ftyp));
+#ifndef TIZEN_KEEP_QT_ATOMS
+ if (qtdemux->major_brand == FOURCC_qt__) {
+ GST_ERROR_OBJECT (qtdemux, "Unsupported major brand...");
+ return GST_FLOW_ERROR;
+ }
+#endif
gst_buffer_unref (ftyp);
break;
}
@@ -2970,6 +4667,18 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
gst_buffer_unref (uuid);
break;
}
+#ifdef QTDEMUX_MODIFICATION
+ case FOURCC_mfra:
+ {
+ GST_DEBUG_OBJECT (qtdemux, "Got mfra atom...");
+ qtdemux->mfra_offset = cur_offset;
+ qtdemux->offset += length;
+ ret = qtdemux_parse_mfra (qtdemux);
+ if (ret != GST_FLOW_OK)
+ goto beach;
+ break;
+ }
+#endif
default:
{
GstBuffer *unknown;
@@ -2990,8 +4699,11 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
}
beach:
- if (ret == GST_FLOW_UNEXPECTED && qtdemux->got_moov) {
+ if ((qtdemux->moof_offset != 0 || ret == GST_FLOW_UNEXPECTED) && qtdemux->got_moov) {
/* digested all data, show what we have */
+#ifdef QTDEMUX_MODIFICATION
+ qtdemux_prepare_streams (qtdemux);
+#endif
ret = qtdemux_expose_streams (qtdemux);
/* Only post, event on pads is done after newsegment */
@@ -3022,7 +4734,7 @@ gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
#ifdef QTDEMUX_MODIFICATION
QtDemuxSample *sample;
gdouble minusone = -1;
- guint64 time_position;
+ guint64 time_position = 0;
#endif
/* Now we choose an arbitrary stream, get the previous keyframe timestamp
@@ -3032,7 +4744,12 @@ gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux)
QtDemuxStream *str = qtdemux->streams[n];
#ifdef QTDEMUX_MODIFICATION
- sample = &str->samples[str->sample_index];
+
+ sample = &str->samples[str->from_sample];
+
+ GST_DEBUG_OBJECT (qtdemux, "from sample timestamp = %"GST_TIME_FORMAT, GST_TIME_ARGS(sample->timestamp));
+
+ time_position = sample->timestamp;
if((time_position - (minusone *qtdemux->segment.rate)*sample->duration)>0)
time_position -= (minusone *qtdemux->segment.rate)*sample->duration;
@@ -3751,8 +5468,11 @@ gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
/* no further processing needed */
stream->need_process = FALSE;
}
-
+#ifdef QTDEMUX_MODIFICATION
+ if (G_UNLIKELY (stream->subtype != FOURCC_text) && G_UNLIKELY (stream->subtype != FOURCC_sbtl)) {
+#else
if (G_UNLIKELY (stream->subtype != FOURCC_text)) {
+#endif
return buf;
}
@@ -3824,6 +5544,21 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
goto exit;
}
+#ifdef QTDEMUX_MODIFICATION
+ if (qtdemux->is_dash && GST_CLOCK_TIME_IS_VALID(stream->dash_seek_offset) && qtdemux->n_video_streams == 0) {
+ /* Dropping audio buffers for syncronization audio and video streams when
+ *playing MPEG DASH content and audio stream have personal demuxer. */
+ if ( (timestamp < stream->dash_seek_offset || !keyframe) ) {
+ GST_LOG_OBJECT (qtdemux, "Ignoring dash buffer with ts=%"GST_TIME_FORMAT
+ " before newsegment start time.", GST_TIME_ARGS(timestamp));
+ gst_buffer_unref (buf);
+ goto exit;
+ } else {
+ stream->dash_seek_offset = GST_CLOCK_TIME_NONE;
+ }
+ }
+#endif
+
/* send out pending buffers */
while (stream->buffers) {
GstBuffer *buffer = (GstBuffer *) stream->buffers->data;
@@ -3891,6 +5626,231 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
gst_buffer_set_caps (buf, stream->caps);
+#ifdef QTDEMUX_MODIFICATION
+
+#ifdef DRM_ENABLE
+ if (qtdemux->encrypt_content) {
+ unsigned int i = 0;
+ QtDemuxSample *sample = NULL;
+ guint n_entries = 0;
+ guint running_offset = 0;
+
+ unsigned char *indata = NULL;
+ unsigned int insize = 0;
+ guint indata_offset = 0;
+ drm_trusted_payload_info_s read_input_data = {0, };
+ drm_trusted_read_decrypt_resp_data_s read_output_data = {0, };
+ drm_trusted_result_e drm_ret = DRM_TRUSTED_RETURN_SUCCESS;
+
+ sample = &stream->samples[stream->sample_index];
+
+ if (sample->sub_encry && qtdemux->dash_content == FALSE) {
+ n_entries = sample->sub_encry->n_entries;
+ GST_DEBUG_OBJECT (qtdemux, " number of sub-sample entries = %d", n_entries);
+ }
+
+ if (n_entries > 0) {
+ /* create indata */
+ indata = (unsigned char *) malloc (GST_BUFFER_SIZE(buf));
+ if (!indata) {
+ GST_ERROR_OBJECT (qtdemux, "failed to create indata...");
+ return GST_FLOW_ERROR;
+ }
+
+ /* copy encrypted data only */
+ do {
+ gint clear_len = 0;
+
+ gint encrypted_len = 0;
+
+ clear_len = sample->sub_encry->sub_entry[i].LenofClearData;
+ encrypted_len = sample->sub_encry->sub_entry[i].LenofEncryptData;
+ running_offset += clear_len;
+
+ GST_LOG_OBJECT (qtdemux, "entry = %d and copying %d bytes of encrypted data", i, encrypted_len);
+ memcpy (indata + insize, GST_BUFFER_DATA (buf)+ running_offset, encrypted_len);
+ insize += encrypted_len;
+ running_offset += encrypted_len;
+ i++;
+ } while (i < n_entries);
+ } else if(qtdemux->clear_data && stream->subtype == FOURCC_vide) {
+ gint clear_len = 0;
+ gint encrypted_len = 0;
+ indata = (unsigned char *) malloc (GST_BUFFER_SIZE(buf));
+ if (!indata) {
+ GST_ERROR_OBJECT (qtdemux, "failed to create indata...");
+ return GST_FLOW_ERROR;
+ }
+ clear_len = qtdemux->clear_data[qtdemux->current_sample[0]];
+ encrypted_len = qtdemux->encrypted_data[qtdemux->current_sample[0]];
+ running_offset += clear_len;
+
+ GST_LOG_OBJECT (qtdemux, "entry = %d and copying %d bytes of encrypted data", i, encrypted_len);
+ memcpy (indata + insize, GST_BUFFER_DATA (buf)+ running_offset, encrypted_len);
+ insize += encrypted_len;
+ running_offset += encrypted_len;
+ } else {
+ indata = GST_BUFFER_DATA (buf);
+ insize = GST_BUFFER_SIZE (buf);
+ }
+
+ read_input_data.media_offset = 0;
+ read_input_data.payload_data = indata;
+ read_input_data.payload_data_len = insize;
+ read_input_data.payload_data_output = read_input_data.payload_data; /* inplace decryption */
+
+ if(qtdemux->sample_count[0] || qtdemux->sample_count[1]) {
+ if(stream->subtype == FOURCC_vide) {
+ read_input_data.payload_iv_len = qtdemux->iv_size_video;
+ read_input_data.payload_iv = (unsigned char *) malloc (qtdemux->iv_size_video);
+ if (NULL == read_input_data.payload_iv) {
+ GST_ERROR_OBJECT (qtdemux, "Failed to allocate memory...\n");
+ GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory"));
+ ret = GST_FLOW_ERROR;
+ goto exit;
+ }
+ for(i = 0 ; i < read_input_data.payload_iv_len; i++) {
+ read_input_data.payload_iv[i] = (unsigned char*)qtdemux->iv_data_video[qtdemux->current_sample[0]][i];
+ }
+ qtdemux->current_sample[0]++;
+ } else if(stream->subtype == FOURCC_soun) {
+ read_input_data.payload_iv_len = qtdemux->iv_size_audio;
+ read_input_data.payload_iv = (unsigned char *) malloc (qtdemux->iv_size_audio);
+ if (NULL == read_input_data.payload_iv) {
+ GST_ERROR_OBJECT (qtdemux, "Failed to allocate memory...\n");
+ GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory"));
+ ret = GST_FLOW_ERROR;
+ goto exit;
+ }
+ for(i = 0 ; i < read_input_data.payload_iv_len; i++) {
+ read_input_data.payload_iv[i] = (unsigned char*)qtdemux->iv_data_audio[qtdemux->current_sample[1]][i];
+ }
+ qtdemux->current_sample[1]++;
+ }
+ } else {
+ read_input_data.payload_iv_len = 8;
+ read_input_data.payload_iv = (unsigned char *) malloc (8);
+ if (NULL == read_input_data.payload_iv) {
+ GST_ERROR_OBJECT (qtdemux, "Failed to allocate memory...\n");
+ GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory"));
+ ret = GST_FLOW_ERROR;
+ goto exit;
+ }
+ memcpy (read_input_data.payload_iv, sample->iv, 8);
+ }
+
+ drm_ret = drm_trusted_read_decrypt_session(qtdemux->pr_handle , &read_input_data, &read_output_data);
+ if (DRM_TRUSTED_RETURN_SUCCESS != drm_ret) {
+ GST_ERROR_OBJECT (qtdemux, "failed to decrypt buffer...");
+ if (DRM_TRUSTED_RETURN_OPL_ERROR == drm_ret) {
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("opl violation"), (NULL));
+ } else {
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("decryption failed"), (NULL));
+ }
+
+ free (read_input_data.payload_iv);
+ ret = GST_FLOW_ERROR;
+ goto exit;
+ }
+
+ GST_LOG_OBJECT (qtdemux, "decryption is successful");
+
+ if (read_output_data.read_size != read_input_data.payload_data_len) {
+ GST_INFO_OBJECT (qtdemux, "Decrypter did not consume data fully...");
+ }
+
+ if(read_input_data.payload_iv)
+ free (read_input_data.payload_iv);
+ read_input_data.payload_iv = NULL;
+
+
+ i = 0;
+ running_offset = 0;
+
+ if (n_entries > 0) {
+ do {
+ gint clear_len = 0;
+ gint encrypted_len = 0;
+
+ clear_len = sample->sub_encry->sub_entry[i].LenofClearData;
+ encrypted_len = sample->sub_encry->sub_entry[i].LenofEncryptData;
+ running_offset += clear_len;
+
+ GST_LOG_OBJECT (qtdemux, "entry = %d and copying back %d bytes of decrypted data", i, encrypted_len);
+ memcpy (GST_BUFFER_DATA (buf)+ running_offset, indata+indata_offset, encrypted_len);
+ running_offset += encrypted_len;
+ indata_offset += encrypted_len;
+ i++;
+ } while (i < n_entries);
+ free (indata);
+ } else if(qtdemux->clear_data > 0 && stream->subtype == FOURCC_vide) {
+ gint clear_len = 0;
+ gint encrypted_len = 0;
+
+ clear_len = qtdemux->clear_data[qtdemux->current_sample[0] - 1];
+ encrypted_len = qtdemux->encrypted_data[qtdemux->current_sample[0] - 1];
+ running_offset += clear_len;
+
+ GST_LOG_OBJECT (qtdemux, "entry = %d and copying back %d bytes of decrypted data", i, encrypted_len);
+ memcpy (GST_BUFFER_DATA (buf)+ running_offset, indata+indata_offset, encrypted_len);
+ running_offset += encrypted_len;
+ indata_offset += encrypted_len;
+ free (indata);
+ }
+ }
+#endif // DRM_ENABLE
+ if (qtdemux->piff_fragmented && !qtdemux->pullbased) {
+ gboolean all_queues_filled = TRUE;
+ gint i = 0;
+ QtDemuxStream *cstream = NULL;
+
+ /* push current buffer */
+ g_queue_push_tail (stream->frag_queue, buf);
+ GST_LOG_OBJECT (stream->pad, "pushing buffer into frag_queue with ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buf)));
+
+ for (i = 0; i < qtdemux->n_streams; i++) {
+ cstream = qtdemux->streams[i];
+ if (g_queue_is_empty (cstream->frag_queue)) {
+ all_queues_filled = FALSE;
+ }
+ }
+
+ if (all_queues_filled) {
+ /* all stream queues have data */
+ for (i = 0; i < qtdemux->n_streams; i++) {
+ GstBuffer *pop_buf = NULL;
+ cstream = qtdemux->streams[i];
+
+pop_again:
+ pop_buf = g_queue_pop_head (cstream->frag_queue);
+
+ qtdemux->max_pop_ts = GST_BUFFER_TIMESTAMP(pop_buf) > qtdemux->max_pop_ts ?
+ GST_BUFFER_TIMESTAMP(pop_buf) : qtdemux->max_pop_ts;
+
+ GST_DEBUG_OBJECT (cstream->pad,
+ "Pushing buffer with time %" GST_TIME_FORMAT ", duration %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (pop_buf)),
+ GST_TIME_ARGS (GST_BUFFER_DURATION (pop_buf)));
+
+ ret = gst_pad_push (cstream->pad, pop_buf);
+ if (ret != GST_FLOW_OK) {
+ GST_WARNING_OBJECT (cstream->pad, "pad_push returned = %s", gst_flow_get_name (ret));
+ return ret;
+ }
+
+ pop_buf = g_queue_peek_head (cstream->frag_queue);
+
+ if (pop_buf && (GST_BUFFER_TIMESTAMP(pop_buf) < qtdemux->max_pop_ts)) {
+ GST_LOG_OBJECT (cstream->pad, "pop again next ts = %"GST_TIME_FORMAT" and max_pop_ts = %"GST_TIME_FORMAT,
+ GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (pop_buf)), GST_TIME_ARGS(qtdemux->max_pop_ts));
+ goto pop_again;
+ }
+ }
+ }
+
+ return GST_FLOW_OK;
+ }
+#endif
GST_LOG_OBJECT (qtdemux,
"Pushing buffer with time %" GST_TIME_FORMAT ", duration %"
GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
@@ -3928,12 +5888,16 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
stream = qtdemux->streams[i];
position = stream->time_position;
-
/* position of -1 is EOS */
if (position != -1 && position < min_time) {
min_time = position;
index = i;
}
+ if (stream->sample_index == stream->n_samples && stream->sent_eos == FALSE) {
+ GST_DEBUG_OBJECT (qtdemux, "Sending EOS for Stream %d", i);
+ gst_pad_push_event(stream->pad, gst_event_new_eos ());
+ stream->sent_eos = TRUE;
+ }
}
/* all are EOS */
if (G_UNLIKELY (index == -1)) {
@@ -4010,15 +5974,23 @@ eos_stream:
static void
gst_qtdemux_loop (GstPad * pad)
{
- GstQTDemux *qtdemux;
- guint64 cur_offset;
- GstFlowReturn ret;
+ GstQTDemux *qtdemux = NULL;
+ guint64 cur_offset = 0;
+ GstFlowReturn ret = GST_FLOW_OK;
qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
cur_offset = qtdemux->offset;
GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d",
cur_offset, qtdemux->state);
+#ifdef QTDEMUX_MODIFICATION
+ if (qtdemux->need_moof_parsing) {
+ ret = qtdemux_prepare_streams (qtdemux);
+ if (ret != GST_FLOW_OK)
+ goto pause;
+ qtdemux->need_moof_parsing = FALSE;
+ }
+#endif
switch (qtdemux->state) {
case QTDEMUX_STATE_INITIAL:
@@ -4250,9 +6222,11 @@ done:
static GstFlowReturn
gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
{
- GstQTDemux *demux;
+ GstQTDemux *demux = NULL;
GstFlowReturn ret = GST_FLOW_OK;
-
+#ifdef QTDEMUX_MODIFICATION
+ gboolean bret = FALSE;
+#endif
demux = GST_QTDEMUX (gst_pad_get_parent (sinkpad));
#ifdef QTDEMUX_MODIFICATION
@@ -4284,7 +6258,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
#ifdef QTDEMUX_MODIFICATION
/* Added consideration of demuxer state and file size*/
- while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes
+ while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes
|| (demux->file && demux->state == QTDEMUX_STATE_MOVIE && demux->filesize >= demux->neededbytes))
&& ret == GST_FLOW_OK) {
#else
@@ -4296,6 +6270,12 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
"state:%d , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT,
demux->state, demux->neededbytes, demux->offset);
+#ifdef QTDEMUX_MODIFICATION
+ if(demux->is_dash && GST_CLOCK_TIME_IS_VALID(GST_BUFFER_TIMESTAMP(inbuf))) {
+ demux->dash_init_offset = GST_BUFFER_TIMESTAMP(inbuf);
+ }
+#endif
+
switch (demux->state) {
case QTDEMUX_STATE_INITIAL:{
const guint8 *data;
@@ -4386,7 +6366,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
demux->state = QTDEMUX_STATE_BUFFER_MDAT;
demux->neededbytes = size;
#ifdef QTDEMUX_MODIFICATION
- if ((demux->filename && demux->file == NULL) || !demux->mdatbuffer)
+ if ((demux->filename && demux->file == NULL) && !demux->mdatbuffer)
#else
if (!demux->mdatbuffer)
#endif
@@ -4428,6 +6408,29 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
if (fourcc == FOURCC_moov) {
GST_DEBUG_OBJECT (demux, "Parsing [moov]");
+#ifdef QTDEMUX_MODIFICATION
+ if (demux->got_moov && demux->fragmented) {
+ demux->need_parsing_moov = TRUE;//need to parse 2nd moov
+ GST_DEBUG_OBJECT (demux,
+ "Got a second moov, clean up data from old one");
+ if (demux->moov_node)
+ g_node_destroy (demux->moov_node);
+ demux->moov_node = NULL;
+ demux->moov_node_compressed = NULL;
+ }
+
+ /* prepare newsegment to send when streaming actually starts */
+ if (!demux->pending_newsegment && !demux->got_moov) {
+ demux->pending_newsegment =
+ gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
+ 0, GST_CLOCK_TIME_NONE, 0);
+ }
+ bret = qtdemux_parse_moov (demux, data, demux->neededbytes);
+ if(!bret) {
+ GST_DEBUG_OBJECT(demux, "returning from chain");
+ return GST_FLOW_ERROR;
+ }
+#else
demux->got_moov = TRUE;
/* prepare newsegment to send when streaming actually starts */
@@ -4436,12 +6439,29 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
0, GST_CLOCK_TIME_NONE, 0);
}
-
qtdemux_parse_moov (demux, data, demux->neededbytes);
+#endif
qtdemux_node_dump (demux, demux->moov_node);
qtdemux_parse_tree (demux);
- qtdemux_expose_streams (demux);
+#ifdef QTDEMUX_MODIFICATION
+ qtdemux_prepare_streams (demux);
+ if (!demux->got_moov)
+ qtdemux_expose_streams (demux);
+ else {
+ gint n;
+ for (n = 0; n < demux->n_streams; n++) {
+ QtDemuxStream *stream = demux->streams[n];
+
+ gst_qtdemux_configure_stream (demux, stream);
+ }
+ }
+
+ demux->got_moov = TRUE;
+ demux->need_parsing_moov = FALSE;//reset 2nd moov
+#else
+ qtdemux_expose_streams (demux);
+#endif
g_node_destroy (demux->moov_node);
demux->moov_node = NULL;
GST_DEBUG_OBJECT (demux, "Finished parsing the header");
@@ -4559,7 +6579,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
GST_FOURCC_ARGS (QT_FOURCC (GST_BUFFER_DATA (buf) + 4)));
#ifdef QTDEMUX_MODIFICATION
- if (demux->filename == NULL) {
+ if (demux->file == NULL) {
#endif
if (demux->mdatbuffer)
demux->mdatbuffer = gst_buffer_join (demux->mdatbuffer, buf);
@@ -4645,8 +6665,8 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
gst_adapter_flush (demux->adapter, demux->todrop);
#ifdef QTDEMUX_MODIFICATION
else {
- fseek (demux->ofile, (long) demux->todrop, SEEK_CUR);
- demux->filesize -= demux->todrop;
+ if(!fseek (demux->ofile, (long) demux->todrop, SEEK_CUR))
+ demux->filesize -= demux->todrop;
}
#endif
demux->neededbytes -= demux->todrop;
@@ -4887,74 +6907,46 @@ qtdemux_inflate (void *z_buffer, guint z_length, guint length)
static gboolean
qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length)
{
- GNode *cmov;
-
+#ifdef QTDEMUX_MODIFICATION
+ gboolean bret = FALSE;
+#endif
qtdemux->moov_node = g_node_new ((guint8 *) buffer);
/* counts as header data */
qtdemux->header_size += length;
GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom");
- qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
-
- cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov);
- if (cmov) {
- guint32 method;
- GNode *dcom;
- GNode *cmvd;
-
- dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom);
- cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd);
- if (dcom == NULL || cmvd == NULL)
- goto invalid_compression;
- method = QT_FOURCC ((guint8 *) dcom->data + 8);
- switch (method) {
-#ifdef HAVE_ZLIB
- case GST_MAKE_FOURCC ('z', 'l', 'i', 'b'):{
- guint uncompressed_length;
- guint compressed_length;
- guint8 *buf;
-
- uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8);
- compressed_length = QT_UINT32 ((guint8 *) cmvd->data + 4) - 12;
- GST_LOG ("length = %u", uncompressed_length);
-
- buf =
- (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12,
- compressed_length, uncompressed_length);
-
- qtdemux->moov_node_compressed = qtdemux->moov_node;
- qtdemux->moov_node = g_node_new (buf);
-
- qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf,
- uncompressed_length);
- break;
- }
-#endif /* HAVE_ZLIB */
- default:
- GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression "
- "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method));
- break;
- }
+#ifdef QTDEMUX_MODIFICATION
+ bret = qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
+ if(!bret) {
+ GST_ERROR_OBJECT(qtdemux, "Failed to parse node object");
+ return bret;
}
+#else
+ qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length);
+#endif
return TRUE;
-
- /* ERRORS */
-invalid_compression:
- {
- GST_ERROR_OBJECT (qtdemux, "invalid compressed header");
- return FALSE;
- }
}
static gboolean
+#ifdef QTDEMUX_MODIFICATION
+qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, guint8 * buf,
+ const guint8 * end)
+#else
qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
const guint8 * end)
+#endif
{
while (G_UNLIKELY (buf < end)) {
GNode *child;
guint32 len;
+#ifdef QTDEMUX_MODIFICATION
+ guint32 fourcc;
+ guint8 i,j;
+ gboolean found_sinf = FALSE;
+ gboolean bret = FALSE;
+#endif
if (G_UNLIKELY (buf + 4 > end)) {
GST_LOG_OBJECT (qtdemux, "buffer overrun");
@@ -4975,11 +6967,43 @@ qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf,
break;
}
+#ifdef QTDEMUX_MODIFICATION
+/* Replacing the fourcc value of 'enca' or 'encv' node with the fourcc value of original format stored in 'sinf' node. */
+ fourcc = QT_FOURCC (buf + 4);
+
+ if((fourcc & 0xFFFFFF) == GST_MAKE_FOURCC ('e', 'n', 'c', 0)) {
+ for(i = 0; i < len ; i++) {
+ fourcc = QT_FOURCC(buf + i);
+ if( fourcc == GST_MAKE_FOURCC ('s','i','n','f')) {
+ found_sinf = TRUE;
+ break;
+ }
+ }
+
+ if (!found_sinf) {
+ GST_ERROR_OBJECT (qtdemux, "failed to find sinf for encx format");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("failed to find sinf atom"), (NULL));
+ return FALSE;
+ }
+
+ fourcc = QT_FOURCC(buf + i +12);
+ GST_LOG_OBJECT(qtdemux,"Original format present in encX node is %"GST_FOURCC_FORMAT , GST_FOURCC_ARGS(fourcc));
+ for(j=0; j<4;j++)
+ buf[4+j] = buf[12 + i + j];
+ }
+#endif
child = g_node_new ((guint8 *) buf);
g_node_append (node, child);
GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len);
+#ifdef QTDEMUX_MODIFICATION
+ bret = qtdemux_parse_node (qtdemux, child, buf, len);
+ if(!bret) {
+ GST_ERROR_OBJECT(qtdemux, "failed to parse node object");
+ return bret;
+ }
+#else
qtdemux_parse_node (qtdemux, child, buf, len);
-
+#endif
buf += len;
}
return TRUE;
@@ -5054,6 +7078,9 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
guint32 node_length = 0;
const QtNodeType *type;
const guint8 *end;
+#ifdef QTDEMUX_MODIFICATION
+ gboolean bret = FALSE;
+#endif
GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length);
@@ -5079,9 +7106,35 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
goto broken_atom_size;
if (type->flags & QT_FLAG_CONTAINER) {
+#ifdef QTDEMUX_MODIFICATION
+ bret = qtdemux_parse_container (qtdemux, node, buffer + 8, end);
+ if(!bret) {
+ GST_ERROR_OBJECT(qtdemux, "failed to parse container object");
+ return bret;
+ }
+#else
qtdemux_parse_container (qtdemux, node, buffer + 8, end);
+#endif
} else {
switch (fourcc) {
+ case FOURCC_saiz:
+ {
+ GST_DEBUG("found saiz atom");
+ if(qtdemux->sequence_id == 2) {
+ qtdemux->iv_size_audio = QT_UINT32 (buffer + 9);
+ GST_INFO("iv size is %d", qtdemux->iv_size_audio);
+ } else if(qtdemux->sequence_id == 1) {
+ qtdemux->iv_size_video = QT_UINT32 (buffer + 9);
+ qtdemux->iv_size_video = 8;
+ GST_INFO("iv size is %d", qtdemux->iv_size_video);
+ }
+ break;
+ }
+ case FOURCC_saio:
+ {
+ GST_DEBUG("found saio atom");
+ break;
+ }
case FOURCC_stsd:
{
if (node_length < 20) {
@@ -5093,6 +7146,220 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
qtdemux_parse_container (qtdemux, node, buffer + 16, end);
break;
}
+#ifdef DRM_ENABLE
+#ifdef QTDEMUX_MODIFICATION
+ case FOURCC_uuid:
+ {
+ GstByteReader uuid_data;
+ guint8 *protection_header_data = NULL;
+ gint64 uuid_offset = 0;
+ uuid_type_t uuid_type;
+
+ gst_byte_reader_init (&uuid_data, buffer, QT_UINT32 (buffer));
+ uuid_type = qtdemux_get_uuid_type (qtdemux, &uuid_data, &uuid_offset);
+ if (UUID_PROTECTION_HEADER != uuid_type) {
+ GST_WARNING_OBJECT (qtdemux, "Ignoring Wrong UUID...");
+ return TRUE;
+ } else {
+ qtdemux->protection_header_present = TRUE;
+ GST_DEBUG_OBJECT (qtdemux, "protection_header is present in uuid...");
+ }
+ break;
+ }
+#endif
+ case FOURCC_pssh:
+ {
+ GstByteReader uuid_data;
+ guint32 protection_header_size = 0;
+ gchar *protection_header_data = NULL;
+ int ret = -1;
+ drm_trusted_open_decrypt_info_s open_input_data;
+ drm_trusted_open_decrypt_resp_data_s open_output_data;
+ drm_trusted_set_consumption_state_info_s state_input_data;
+ guint8 *buffer = (guint8 *) node->data;
+ drm_permission_type_e status_perm_type;
+ drm_license_status_e license_status = DRM_LICENSE_STATUS_UNDEFINED;
+ GstQuery *query = NULL;
+ gchar *file_path = NULL;
+ gchar *query_file_path = NULL;
+ gchar *modified_path = NULL;
+ gchar *drm_compatible_path = NULL;
+ drm_bool_type_e is_drm_file = DRM_UNKNOWN;
+ gint len_protection_header = 0;
+ int a = 0;
+
+ if (qtdemux->dash_content) {
+ GST_WARNING_OBJECT (qtdemux, "Skipping second pssh");
+ break;
+ }
+
+ qtdemux->dash_content = TRUE;
+
+ gst_byte_reader_init (&uuid_data, buffer, QT_UINT32 (buffer));
+
+ /* Skipping fourcc & length*/
+ if (!gst_byte_reader_skip (&uuid_data, 8))
+ goto not_enough_data;
+
+ if (!qtdemux_parse_playready_system_id (qtdemux, &uuid_data)) {
+ GST_ERROR_OBJECT (qtdemux, "not a playready system id..");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), ("not a valid playready system ID"));
+ return FALSE;
+ }
+
+ query = gst_query_new_uri ();
+ if (!gst_pad_peer_query (qtdemux->sinkpad, query)) {
+ GST_ERROR_OBJECT (qtdemux, "failed to query URI from upstream");
+ GST_ELEMENT_ERROR (qtdemux, CORE, FAILED, (NULL), ("failed to get uri from upstream"));
+ gst_query_unref (query);
+ return FALSE;
+ }
+ gst_query_parse_uri (query, &query_file_path);
+
+ gst_query_unref (query);
+ query = NULL;
+
+ if (g_str_has_prefix (query_file_path, "file://")) {
+ modified_path = query_file_path + 7;
+ } else {
+ modified_path = query_file_path;
+ }
+ GST_INFO_OBJECT (qtdemux, "file path : %s", query_file_path);
+ GST_INFO_OBJECT (qtdemux, "modified path : %s", modified_path);
+ drm_compatible_path = g_uri_unescape_string(modified_path, NULL);
+ if(drm_compatible_path == NULL) {
+ GST_WARNING_OBJECT(qtdemux, "unsuccessful in converting the string continuing with the modified path");
+ drm_compatible_path = modified_path;
+ } else {
+ GST_INFO_OBJECT (qtdemux, "DRM Compatible path : %s", drm_compatible_path);
+ if(query_file_path) {
+ g_free (query_file_path);
+ query_file_path = NULL;
+ }
+ }
+
+ //drm_compatible_path = "http://img.samsungvideohub.com/cms-private/store/057e52tw7h/PRD/C00000682330/Movie_HEVC_SD/I00001645829/I00001645829.mpd?AWSAccessKeyId=AKIAJW7RUCSLREJCX3IA&Expires=1374745300&Signature=FnPehyWVpoQoSKs9dxSSm7U551E%3D[]<>0418hwir93[]<>nk61lxvw9v[]<>356449050005148[]<>10060079181422907[]<>V[]<>STG";
+ //drm_compatible_path = "http://img.samsungvideohub.com/cms-private/store/057e52tw7h/PRD/C00000107359/Movie_HEVC_SD/I00001645159/I00001645159.mpd?AWSAccessKeyId=AKIAJW7RUCSLREJCX3IA&Expires=1375434936&Signature=rzKiT1HUIVsTEa5QHaSDhEqR%2Frc%3D[]<>0418hwir93[]<>nk61lxvw9v[]<>356449050005148[]<>10060081508510332[]<>V[]<>STG";
+
+
+ /* Enters into if body when valid system ID is present.*/
+ gst_byte_reader_skip (&uuid_data, 12);
+
+ gst_byte_reader_get_uint16_le (&uuid_data, &protection_header_size);
+
+ GST_DEBUG_OBJECT(qtdemux,"protection header size: %d ", protection_header_size);
+
+ if (!gst_byte_reader_dup_data(&uuid_data, protection_header_size , &protection_header_data)) {
+ GST_ERROR_OBJECT (qtdemux, "failed to duplicate bytereader data...");
+ GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory"));
+ return FALSE;
+ }
+ /* for adding wchar null check */
+ protection_header_size = protection_header_size + 2;
+
+ protection_header_data = (guint8 *) realloc (protection_header_data,protection_header_size);
+ if (!protection_header_data) {
+ GST_ERROR_OBJECT (qtdemux, "failed to allocate memory...");
+ GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory"));
+ return FALSE;
+ }
+
+ /* adding wchar null character */
+ protection_header_data[protection_header_size-2] = '\0';
+ protection_header_data[protection_header_size-1] = '\0';
+
+ GST_DEBUG_OBJECT(qtdemux,"protection header size: %d ", protection_header_size);
+
+ file_path = (gchar*)calloc((4 + 1 + 32 + 4 + 1 + protection_header_size), sizeof(gchar));
+ if(file_path == NULL) {
+ GST_ERROR_OBJECT (qtdemux, "failed to allocate memory...");
+ GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory"));
+ return FALSE;
+ }
+
+ strcpy(file_path, "dash");
+
+ file_path[4] = '?';
+ for(a = 0; a < 16; a++) {
+ sprintf(file_path + 4 + 1 + (a*2), "%02x", qtdemux->uuid_playready[a]);
+ }
+
+ file_path[4 + 1 + 32] = '?';
+ len_protection_header = sprintf(file_path + 4 + 1 + 32 + 1, "%d", protection_header_size);
+ for(a = 0; a < protection_header_size; a++) {
+ file_path[4 + 1 + 32 + len_protection_header + 1 + a] = protection_header_data[a];
+ }
+
+ GST_DEBUG_OBJECT(qtdemux, "pssh header is %s", file_path);
+
+ GST_DEBUG_OBJECT(qtdemux, "file path info is %s", query_file_path);
+
+ ret = drm_is_drm_file(file_path, &is_drm_file);
+ if(ret != 0) {
+ GST_ERROR_OBJECT (qtdemux, "Failed to read is DRM_FILE information");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("Failed to read is DRM_FILE information"), (NULL));
+ free (protection_header_data);
+ free (file_path);
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT(qtdemux, "is drm is drm %d", is_drm_file);
+
+ status_perm_type = DRM_PERMISSION_TYPE_PLAY;
+ ret = drm_get_license_status (file_path, status_perm_type, &license_status);
+ if (DRM_RETURN_SUCCESS != ret) {
+ GST_ERROR_OBJECT (qtdemux, "failed to get license status : 0x%x", ret);
+ qtdemux_post_drm_error (qtdemux, DRM_LICENSE_STATUS_UNDEFINED);
+ free (protection_header_data);
+ free (file_path);
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT(qtdemux, "license status is %d", license_status);
+ if(license_status != DRM_LICENSE_STATUS_VALID) {
+ if(!qtdemux_get_playready_licence(qtdemux, protection_header_data, protection_header_size, drm_compatible_path)) {
+ GST_ERROR_OBJECT (qtdemux, "failed to get playready license");
+ free (protection_header_data);
+ free (file_path);
+ return FALSE;
+ }
+ }
+
+ open_input_data.file_type = DRM_TRUSTED_TYPE_PIFF;
+ open_input_data.permission = DRM_TRUSTED_PERMISSION_TYPE_PLAY;
+ open_input_data.operation_callback.callback = test_drm_trusted_operation_cb;
+ open_input_data.lic_header.header = (unsigned char *) protection_header_data;
+ open_input_data.lic_header.header_len = protection_header_size;
+
+ /* Open Decrypt Session*/
+ ret = drm_trusted_open_decrypt_session(&open_input_data, &open_output_data, &(qtdemux->pr_handle));
+ if (DRM_TRUSTED_RETURN_SUCCESS != ret) {
+ GST_ERROR_OBJECT (qtdemux, "failed to open decrypt session");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("failed to open decrypt session"), (NULL));
+ free (protection_header_data);
+ free (file_path);
+ return FALSE;
+ }
+
+ /* Before Read, Appropriate state MUST be SET */
+ state_input_data.state = DRM_CONSUMPTION_STARTED;
+ ret = drm_trusted_set_decrypt_state(qtdemux->pr_handle, &state_input_data);
+ if (DRM_TRUSTED_RETURN_SUCCESS != ret) {
+ GST_ERROR_OBJECT (qtdemux, "failed to set decrypt state...");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("failed to set decrypt state"), (NULL));
+ free (protection_header_data);
+ free (file_path);
+ return FALSE;
+ }
+
+ qtdemux->encrypt_content = TRUE;
+
+ free (protection_header_data);
+ free (file_path);
+ /* iterate all siblings */
+ break;
+ }
+#endif
case FOURCC_mp4a:
case FOURCC_alac:
{
@@ -5132,7 +7399,7 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer,
offset = 0x24;
break;
case 1:
- offset = 0x34;
+ offset = 0x24;
break;
case 2:
offset = 0x48;
@@ -5334,6 +7601,218 @@ qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
}
+#ifdef QTDEMUX_MODIFICATION
+static void
+gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
+{
+
+ gst_segment_set_newsegment (&stream->segment, FALSE, 1.0, GST_FORMAT_TIME,
+ 0, GST_CLOCK_TIME_NONE, 0);
+
+ if (stream->subtype == FOURCC_vide) {
+
+ /* fps is calculated base on the duration of the first frames since
+ * qt does not have a fixed framerate. */
+ if ((stream->n_samples == 1) && (stream->min_duration == 0)) {
+ /* still frame */
+ stream->fps_n = 0;
+ stream->fps_d = 1;
+ } else {
+ stream->fps_n = stream->timescale;
+ if (stream->min_duration == 0)
+ stream->fps_d = 1;
+ else
+ stream->fps_d = stream->min_duration;
+ }
+
+ if (stream->caps) {
+ gboolean gray;
+ gint depth, palette_count;
+ const guint32 *palette_data = NULL;
+
+ stream->caps = gst_caps_make_writable (stream->caps);
+
+ gst_caps_set_simple (stream->caps,
+ "width", G_TYPE_INT, stream->width,
+ "height", G_TYPE_INT, stream->height,
+ "framerate", GST_TYPE_FRACTION, stream->fps_n, stream->fps_d, NULL);
+
+ if((qtdemux->video_max_width > 0) && (qtdemux->video_max_height > 0)) {
+ gst_caps_set_simple (stream->caps,
+ "max-width", G_TYPE_INT, qtdemux->video_max_width,
+ "max-height", G_TYPE_INT, qtdemux->video_max_height, NULL);
+ }
+
+ /* calculate pixel-aspect-ratio using display width and height */
+ GST_DEBUG_OBJECT (qtdemux,
+ "video size %dx%d, target display size %dx%d", stream->width,
+ stream->height, stream->display_width, stream->display_height);
+
+ if (stream->display_width > 0 && stream->display_height > 0 &&
+ stream->width > 0 && stream->height > 0) {
+ gint n, d;
+
+ /* calculate the pixel aspect ratio using the display and pixel w/h */
+ n = stream->display_width * stream->height;
+ d = stream->display_height * stream->width;
+ if (n == d)
+ n = d = 1;
+ GST_DEBUG_OBJECT (qtdemux, "setting PAR to %d/%d", n, d);
+ gst_caps_set_simple (stream->caps, "pixel-aspect-ratio",
+ GST_TYPE_FRACTION, n, d, NULL);
+ }
+
+ /* qt file might have pasp atom */
+ if (stream->par_w > 0 && stream->par_h > 0) {
+ GST_DEBUG_OBJECT (qtdemux, "par %d:%d", stream->par_w, stream->par_h);
+ gst_caps_set_simple (stream->caps, "pixel-aspect-ratio",
+ GST_TYPE_FRACTION, stream->par_w, stream->par_h, NULL);
+ }
+
+ depth = stream->bits_per_sample;
+
+ /* more than 32 bits means grayscale */
+ gray = (depth > 32);
+ /* low 32 bits specify the depth */
+ depth &= 0x1F;
+
+ /* different number of palette entries is determined by depth. */
+ palette_count = 0;
+ if ((depth == 1) || (depth == 2) || (depth == 4) || (depth == 8))
+ palette_count = (1 << depth);
+
+ switch (palette_count) {
+ case 0:
+ break;
+ case 2:
+ palette_data = ff_qt_default_palette_2;
+ break;
+ case 4:
+ palette_data = ff_qt_default_palette_4;
+ break;
+ case 16:
+ if (gray)
+ palette_data = ff_qt_grayscale_palette_16;
+ else
+ palette_data = ff_qt_default_palette_16;
+ break;
+ case 256:
+ if (gray)
+ palette_data = ff_qt_grayscale_palette_256;
+ else
+ palette_data = ff_qt_default_palette_256;
+ break;
+ default:
+ GST_ELEMENT_WARNING (qtdemux, STREAM, DEMUX,
+ (_("The video in this file might not play correctly.")),
+ ("unsupported palette depth %d", depth));
+ break;
+ }
+ if (palette_data) {
+ GstBuffer *palette;
+
+ /* make sure it's not writable. We leave MALLOCDATA to NULL so that we
+ * don't free any of the buffer data. */
+ palette = gst_buffer_new ();
+ GST_BUFFER_FLAG_SET (palette, GST_BUFFER_FLAG_READONLY);
+ GST_BUFFER_DATA (palette) = (guint8 *) palette_data;
+ GST_BUFFER_SIZE (palette) = sizeof (guint32) * palette_count;
+
+ gst_caps_set_simple (stream->caps, "palette_data",
+ GST_TYPE_BUFFER, palette, NULL);
+ gst_buffer_unref (palette);
+ }
+ }
+ } else if (stream->subtype == FOURCC_soun) {
+ if (stream->caps) {
+ stream->caps = gst_caps_make_writable (stream->caps);
+ gst_caps_set_simple (stream->caps,
+ "rate", G_TYPE_INT, (int) stream->rate,
+ "channels", G_TYPE_INT, stream->n_channels, NULL);
+ }
+ }
+
+ if (stream->pad) {
+ GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
+
+ gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
+ gst_pad_set_query_type_function (stream->pad,
+ gst_qtdemux_get_src_query_types);
+ gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);
+ gst_pad_set_active(stream->pad, TRUE);
+ gst_pad_use_fixed_caps (stream->pad);
+
+ GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT, stream->caps);
+ gst_pad_set_caps (stream->pad, stream->caps);
+
+ }
+ return;
+}
+static gboolean
+gst_qtdemux_add_stream (GstQTDemux * qtdemux,
+ QtDemuxStream * stream, GstTagList * list)
+{
+ /* consistent default for push based mode */
+ gst_segment_init (&stream->segment, GST_FORMAT_TIME);
+
+ if (stream->subtype == FOURCC_vide) {
+ gchar *name = g_strdup_printf ("video_%02d", qtdemux->n_video_streams);
+
+ stream->pad =
+ gst_pad_new_from_static_template (&gst_qtdemux_videosrc_template, name);
+ g_free (name);
+ gst_qtdemux_configure_stream(qtdemux,stream);
+ qtdemux->n_video_streams++;
+ } else if (stream->subtype == FOURCC_soun) {
+ gchar *name = g_strdup_printf ("audio_%02d", qtdemux->n_audio_streams);
+
+ stream->pad =
+ gst_pad_new_from_static_template (&gst_qtdemux_audiosrc_template, name);
+ g_free (name);
+ gst_qtdemux_configure_stream(qtdemux,stream);
+ qtdemux->n_audio_streams++;
+ }else if (stream->subtype == FOURCC_strm) {
+ GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
+#ifdef QTDEMUX_MODIFICATION
+ } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl) {
+#else
+ } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text) {
+#endif
+ gchar *name = g_strdup_printf ("subtitle_%02d", qtdemux->n_sub_streams);
+
+ stream->pad =
+ gst_pad_new_from_static_template (&gst_qtdemux_subsrc_template, name);
+ g_free (name);
+ gst_qtdemux_configure_stream(qtdemux,stream);
+ qtdemux->n_sub_streams++;
+ } else {
+ GST_DEBUG_OBJECT (qtdemux, "unknown stream type");
+ goto done;
+ }
+
+
+ if (stream->pad) {
+ GST_DEBUG_OBJECT (qtdemux, "adding pad %s %p to qtdemux %p",
+ GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
+ gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), stream->pad);
+ if (stream->pending_tags)
+ gst_tag_list_free (stream->pending_tags);
+ stream->pending_tags = list;
+ if (list) {
+ /* post now, send event on pad later */
+ GST_DEBUG_OBJECT (qtdemux, "Posting tags %" GST_PTR_FORMAT, list);
+ gst_element_post_message (GST_ELEMENT (qtdemux),
+ gst_message_new_tag_full (GST_OBJECT (qtdemux), stream->pad,
+ gst_tag_list_copy (list)));
+ }
+ /* global tags go on each pad anyway */
+ stream->send_global_tags = TRUE;
+ }
+done:
+ return TRUE;
+}
+#else
+/* Deprecated */
static gboolean
gst_qtdemux_add_stream (GstQTDemux * qtdemux,
QtDemuxStream * stream, GstTagList * list)
@@ -5475,7 +7954,11 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux,
qtdemux->n_audio_streams++;
} else if (stream->subtype == FOURCC_strm) {
GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad");
+#ifdef QTDEMUX_MODIFICATION
+ } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl) {
+#else
} else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text) {
+#endif
gchar *name = g_strdup_printf ("subtitle_%02d", qtdemux->n_sub_streams);
stream->pad =
@@ -5519,7 +8002,7 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux,
done:
return TRUE;
}
-
+#endif
/* find next atom with @fourcc starting at @offset */
static GstFlowReturn
qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
@@ -5676,7 +8159,6 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
}
/* sync sample atom */
- stream->stps_present = FALSE;
if ((stream->stss_present =
! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss,
&stream->stss) ? TRUE : FALSE) == TRUE) {
@@ -5693,29 +8175,6 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl)
if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4))
goto corrupt_file;
}
-
- /* partial sync sample atom */
- if ((stream->stps_present =
- ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps,
- &stream->stps) ? TRUE : FALSE) == TRUE) {
- /* copy atom data into a new buffer for later use */
- stream->stps.data = g_memdup (stream->stps.data, stream->stps.size);
-
- /* skip version + flags */
- if (!gst_byte_reader_skip (&stream->stps, 1 + 3) ||
- !gst_byte_reader_get_uint32_be (&stream->stps,
- &stream->n_sample_partial_syncs))
- goto corrupt_file;
-
- /* if there are no entries, the stss table contains the real
- * sync samples */
- if (stream->n_sample_partial_syncs) {
- /* make sure there's enough data */
- if (!qt_atom_parser_has_chunks (&stream->stps,
- stream->n_sample_partial_syncs, 4))
- goto corrupt_file;
- }
- }
}
/* sample size */
@@ -5878,9 +8337,13 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n)
goto out_of_samples;
GST_OBJECT_LOCK (qtdemux);
+#ifdef QTDEMUX_MODIFICATION
+ if (n <= stream->stbl_index && !qtdemux->need_parsing_moov)
+ goto already_parsed;
+#else
if (n <= stream->stbl_index)
goto already_parsed;
-
+#endif
GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n);
if (!stream->stsz.data) {
@@ -6089,7 +8552,21 @@ done2:
(guint) (cur - samples), j,
GST_TIME_ARGS (gst_util_uint64_scale (stts_time, GST_SECOND,
stream->timescale)));
-
+#ifdef QTDEMUX_MODIFICATION
+ if(G_UNLIKELY (stream->subtype != FOURCC_text) && G_UNLIKELY (stream->subtype != FOURCC_sbtl) &&
+ G_UNLIKELY (stream->subtype != FOURCC_subp)) { /* can not make bogus code check for sub-titles */
+ if(gst_util_uint64_scale (stts_duration, GST_SECOND,stream->timescale) > QTDEMUX_MAX_SAMPLE_DURATION && stream->stts_samples ==1) {
+ GST_WARNING_OBJECT(qtdemux,"got the bogus duration and hence finding the last correct duration to replace with");
+ for(k = (guint)(cur - samples-1);k >= 0;k--) {
+ if(gst_util_uint64_scale (samples[k].duration, GST_SECOND,stream->timescale) < QTDEMUX_MAX_SAMPLE_DURATION) {
+ stts_duration = samples[k].duration;
+ GST_INFO_OBJECT(qtdemux,"find the last valid duration and replacing bogus duration with %d",samples[k].duration);
+ break;
+ }
+ }
+ }
+ }
+#endif
cur->timestamp = stts_time;
cur->duration = stts_duration;
@@ -6155,37 +8632,6 @@ done3:
/* save state */
stream->stss_index = i;
}
-
- /* stps marks partial sync frames like open GOP I-Frames */
- if (stream->stps_present == TRUE) {
- guint32 n_sample_partial_syncs;
-
- n_sample_partial_syncs = stream->n_sample_partial_syncs;
-
- /* if there are no entries, the stss table contains the real
- * sync samples */
- if (n_sample_partial_syncs) {
- for (i = stream->stps_index; i < n_sample_partial_syncs; i++) {
- /* note that the first sample is index 1, not 0 */
- guint32 index;
-
- index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps);
-
- if (G_LIKELY (index > 0 && index <= n_samples)) {
- index -= 1;
- samples[index].keyframe = TRUE;
- GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index);
- /* and exit if we have enough samples */
- if (G_UNLIKELY (index >= n)) {
- i++;
- break;
- }
- }
- }
- /* save state */
- stream->stps_index = i;
- }
- }
} else {
/* no stss, all samples are keyframes */
stream->all_keyframe = TRUE;
@@ -6359,6 +8805,21 @@ qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream,
GST_DEBUG_OBJECT (qtdemux, "found %d non-empty segments", count);
stream->n_segments = count;
}
+
+#ifdef QTDEMUX_MODIFICATION
+ if (stream->n_segments) {
+ GstClockTime movie_duration =
+ gst_util_uint64_scale (stream->duration, GST_SECOND, stream->timescale);
+ /* Any difference between the movie's duration and the track's duration
+ * is expressed as an implicit empty edit */
+ if (stime != movie_duration) {
+ GST_WARNING_OBJECT (qtdemux, "Movie duration and track duration from segments "
+ "is not matching, so assuming empty edits");
+ stream->n_segments = 0;
+ }
+ }
+#endif
+
done:
/* push based does not handle segments, so act accordingly here,
@@ -6644,6 +9105,67 @@ bad_data:
return 0;
}
+#ifdef QTDEMUX_MODIFICATION
+static gboolean
+gst_codec_utils_hevc_caps_set_level_and_profile (GstCaps * caps,
+ const guint8 * sps, guint len)
+{
+ const gchar *level, *profile;
+
+ g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
+ g_return_val_if_fail (GST_CAPS_IS_SIMPLE (caps), FALSE);
+ g_return_val_if_fail (GST_SIMPLE_CAPS_HAS_NAME (caps, "video/hevc"), FALSE);
+ g_return_val_if_fail (sps != NULL, FALSE);
+
+ level = gst_codec_utils_h264_get_level (sps, len);
+
+ if (level != NULL)
+ gst_caps_set_simple (caps, "level", G_TYPE_STRING, level, NULL);
+
+ profile = gst_codec_utils_h264_get_profile (sps, len);
+
+ if (profile != NULL)
+ gst_caps_set_simple (caps, "profile", G_TYPE_STRING, profile, NULL);
+
+ GST_LOG ("profile : %s", (profile) ? profile : "---");
+ GST_LOG ("level : %s", (level) ? level : "---");
+
+ return (level != NULL && profile != NULL);
+}
+#endif
+
+#ifdef QTDEMUX_MODIFICATION
+static QtDemuxStream *
+_create_stream(GstQTDemux *qtdemux)
+{
+ QtDemuxStream *stream;
+ stream = g_new0 (QtDemuxStream, 1);
+ /* new streams always need a discont */
+ stream->discont = TRUE;
+ /* we enable clipping for raw audio/video streams */
+ stream->need_clip = FALSE;
+ stream->need_process = FALSE;
+ stream->segment_index = -1;
+ stream->time_position = 0;
+ stream->sample_index = -1;
+ stream->last_ret = GST_FLOW_OK;
+ /* dash specified */
+ stream->dash_seek_offset = GST_CLOCK_TIME_NONE;
+
+ stream->trickplay_info = g_new0 (TrickPlayInfo, 1);
+ stream->trickplay_info->prev_kidx = 0;
+ stream->trickplay_info->next_kidx = 0;
+ stream->trickplay_info->kidxs_dur_diff = 0;
+ stream->orientation = -1;
+ stream->prev_n_samples=0;
+ stream->moof_seeked_time = 0;
+
+ if (qtdemux->piff_fragmented)
+ stream->frag_queue = g_queue_new ();
+
+ return stream;
+}
+#endif
/* parse the traks.
* With each track we associate a new QtDemuxStream that contains all the info
* about the trak.
@@ -6653,29 +9175,41 @@ static gboolean
qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
{
GstByteReader tkhd;
- int offset;
- GNode *mdia;
- GNode *mdhd;
- GNode *hdlr;
- GNode *minf;
- GNode *stbl;
- GNode *stsd;
- GNode *mp4a;
- GNode *mp4v;
- GNode *wave;
- GNode *esds;
- GNode *pasp;
- QtDemuxStream *stream;
+ int offset = 0;
+ GNode *mdia = NULL;
+ GNode *mdhd = NULL;
+ GNode *hdlr = NULL;
+ GNode *minf = NULL;
+ GNode *stbl = NULL;
+ GNode *stsd = NULL;
+ GNode *mp4a = NULL;
+ GNode *mp4v = NULL;
+ GNode *wave = NULL;
+ GNode *esds = NULL;
+ GNode *pasp = NULL;
+ QtDemuxStream *stream = NULL;
GstTagList *list = NULL;
gchar *codec = NULL;
- const guint8 *stsd_data;
- guint16 lang_code; /* quicktime lang code or packed iso code */
- guint32 version;
+ const guint8 *stsd_data = NULL;
+ guint16 lang_code = 0; /* quicktime lang code or packed iso code */
+ guint32 version = 0;
guint32 tkhd_flags = 0;
guint8 tkhd_version = 0;
- guint32 fourcc;
- guint value_size, len;
-
+ guint32 fourcc = 0;
+ guint value_size = 0, len = 0;
+#ifdef QTDEMUX_MODIFICATION
+ guint32 track_id;
+ guint32 a = 0;
+ guint32 b = 0;
+ guint32 c = 0;
+ guint32 d = 0;
+
+ guint16 layer = 0;
+ guint16 alternate_group = 0;
+ guint16 volume = 0;
+ GstClockTime duration = 0;
+ int height, width;
+#else
stream = g_new0 (QtDemuxStream, 1);
/* new streams always need a discont */
stream->discont = TRUE;
@@ -6686,12 +9220,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
stream->time_position = 0;
stream->sample_index = -1;
stream->last_ret = GST_FLOW_OK;
-#ifdef QTDEMUX_MODIFICATION
- stream->trickplay_info = g_new0 (TrickPlayInfo, 1);
- stream->trickplay_info->prev_kidx = 0;
- stream->trickplay_info->next_kidx = 0;
- stream->trickplay_info->kidxs_dur_diff = 0;
#endif
+
if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd)
|| !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version)
|| !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags))
@@ -6700,12 +9230,117 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
/* pick between 64 or 32 bits */
value_size = tkhd_version == 1 ? 8 : 4;
if (!gst_byte_reader_skip (&tkhd, value_size * 2) ||
- !gst_byte_reader_get_uint32_be (&tkhd, &stream->track_id))
+ !gst_byte_reader_get_uint32_be (&tkhd, &track_id))
goto corrupt_file;
+#ifdef QTDEMUX_MODIFICATION
+ if (!qtdemux->got_moov) {
+ if (!qtdemux->got_moov && qtdemux_find_stream (qtdemux, track_id))
+ goto existing_stream;
+ stream = _create_stream (qtdemux);
+ stream->track_id = track_id;
+ } else {
+ stream = qtdemux_find_stream (qtdemux, track_id);
+ if (!stream) {
+ GST_WARNING_OBJECT (qtdemux, "Stream not found, going to ignore it");
+ goto skip_track;
+ }
+ }
+#endif
+
GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u",
tkhd_version, tkhd_flags, stream->track_id);
+#ifdef QTDEMUX_MODIFICATION
+ if (!gst_byte_reader_skip (&tkhd, 4)) //skipping for reserved bits
+ goto corrupt_file;
+
+ //reading duration from the tkhd atom, 64 bits if version is 1 and 32 bits if version is 0
+ if(tkhd_version == 1) {
+ if(gst_byte_reader_get_uint64_be (&tkhd, &duration)) {
+ GST_LOG_OBJECT(qtdemux, "the duration from tkhd field is %"GST_TIME_FORMAT, GST_TIME_ARGS(duration));
+ } else {
+ GST_LOG_OBJECT(qtdemux ,"no duration information present");
+ goto corrupt_file;
+ }
+ } else {
+ if(gst_byte_reader_get_uint32_be (&tkhd, &duration)) {
+ GST_LOG_OBJECT(qtdemux, "the duration from tkhd field is %"GST_TIME_FORMAT, GST_TIME_ARGS(duration));
+ } else {
+ GST_LOG_OBJECT(qtdemux, "no duration information present");
+ goto corrupt_file;
+ }
+ }
+
+ if(!gst_byte_reader_skip(&tkhd, 8)) { //skipping for reserved
+ GST_LOG_OBJECT(qtdemux, "tkhd doesn't have more information");
+ goto corrupt_file;
+ }
+
+//reading layer, alternate_group and volume information from tkhd atom
+ if(!gst_byte_reader_get_uint16_be(&tkhd, &layer) ||
+ !gst_byte_reader_get_uint16_be(&tkhd, &alternate_group) ||
+ !gst_byte_reader_get_uint16_be(&tkhd, &volume)) {
+ GST_LOG_OBJECT(qtdemux, "tkhd doesn't have layer, group or volume information");
+ goto corrupt_file;
+ } else {
+ GST_LOG_OBJECT(qtdemux, "the layer info is %d", layer);
+ GST_LOG_OBJECT(qtdemux, "the alternate group info is %d", alternate_group);
+ GST_LOG_OBJECT(qtdemux, "the volume information is %d", volume);
+ }
+ if(!gst_byte_reader_skip(&tkhd, 2)) { //skipping for reserved
+ GST_LOG_OBJECT(qtdemux, "no information present in tkhd");
+ goto corrupt_file;
+ }
+
+ //reading the orientation matrix information
+ if(!gst_byte_reader_get_uint32_be(&tkhd, &a) ||
+ !gst_byte_reader_get_uint32_be(&tkhd, &b)) {
+ GST_LOG_OBJECT(qtdemux, "No matrix information present");
+ goto corrupt_file;
+ } else {
+ GST_LOG_OBJECT(qtdemux, "matrix a and b info is %08x %08x", a, b);
+ }
+
+ if(!gst_byte_reader_skip(&tkhd, 4)) { //skipping some unrelevant information
+ GST_LOG_OBJECT(qtdemux, "tkhd doesn't have more information");
+ goto corrupt_file;
+ }
+ if(!gst_byte_reader_get_uint32_be(&tkhd, &c) ||
+ !gst_byte_reader_get_uint32_be(&tkhd, &d)) {
+ goto corrupt_file;
+ } else {
+ GST_LOG_OBJECT(qtdemux, "c and d info is %08x %08x", c, d);
+ }
+
+ //Checking the orientation information with parsed matrix information
+ if(a == 0x00010000 && b == 0x00000000 && c == 0x00000000 && d == 0x00010000)
+ stream->orientation = 0;
+ if(a == 0x00000000 && b == 0x00010000 && c == 0xFFFF0000 && d == 0x00000000)
+ stream->orientation = 90;
+ if(a == 0xFFFF0000 && b == 0x00000000 && c == 0x00000000 && d == 0xFFFF0000)
+ stream->orientation = 180;
+ if(a == 0x00000000 && b == 0xFFFF0000 && c == 0x00010000 && d == 0x00000000)
+ stream->orientation = 270;
+
+ GST_INFO("the orientation is %d", stream->orientation);
+ int cur_pos = gst_byte_reader_get_pos(&tkhd);
+ if(!gst_byte_reader_skip(&tkhd, 16)) { //skipping some unrelevant information
+ GST_LOG_OBJECT(qtdemux, "tkhd doesn't have more information");
+ goto corrupt_file;
+ }
+
+ if(!gst_byte_reader_get_uint32_be(&tkhd, &height) ||
+ !gst_byte_reader_get_uint32_be(&tkhd, &width)) {
+ goto corrupt_file;
+ } else {
+ height = height >> 16;
+ width = width >> 16;
+ GST_LOG_OBJECT(qtdemux, "height and width info is %d %d", height, width);
+ }
+
+ gst_byte_reader_set_pos(&tkhd, cur_pos);
+#endif
if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia)))
goto corrupt_file;
@@ -6765,7 +9400,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
* some of those trailers, nowadays, have prologue images that are
* themselves vide tracks as well. I haven't really found a way to
* identify those yet, except for just looking at their duration. */
- if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
+ if ((stream->subtype == FOURCC_vide) && tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) {
GST_WARNING_OBJECT (qtdemux,
"Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT
" vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream "
@@ -6817,9 +9452,12 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
stream->fourcc = fourcc = QT_FOURCC (stsd_data + 16 + 4);
GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (stream->fourcc));
-
+#ifdef QTDEMUX_MODIFICATION
+ if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi) )
+#else
if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi) ||
((fourcc & 0xFFFFFF00) == GST_MAKE_FOURCC ('e', 'n', 'c', 0)))
+#endif
goto error_encrypted;
if (stream->subtype == FOURCC_vide) {
@@ -6828,9 +9466,15 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
stream->sampled = TRUE;
/* version 1 uses some 64-bit ints */
+#ifdef QTDEMUX_MODIFICATION
+ if (!gst_byte_reader_get_uint32_be (&tkhd, &w)
+ || !gst_byte_reader_get_uint32_be (&tkhd, &h))
+#else
if (!gst_byte_reader_skip (&tkhd, 56 + value_size)
|| !gst_byte_reader_get_uint32_be (&tkhd, &w)
|| !gst_byte_reader_get_uint32_be (&tkhd, &h))
+#endif
+
goto corrupt_file;
stream->display_width = w >> 16;
@@ -6842,6 +9486,14 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
stream->width = QT_UINT16 (stsd_data + offset + 32);
stream->height = QT_UINT16 (stsd_data + offset + 34);
+ if(stream->height == 0) {
+ stream->height = height;
+ GST_WARNING_OBJECT(qtdemux, "replacing height by tkhd since height obtained from stsd is zero");
+ }
+ if(stream->width == 0) {
+ stream->width = width;
+ GST_WARNING_OBJECT(qtdemux, "replacing width by tkhd since width obtained from stsd is zero");
+ }
stream->fps_n = 0; /* this is filled in later */
stream->fps_d = 0; /* this is filled in later */
stream->bits_per_sample = QT_UINT16 (stsd_data + offset + 82);
@@ -6985,6 +9637,59 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
break;
}
+#ifdef QTDEMUX_MODIFICATION
+ case GST_MAKE_FOURCC ('h', 'v', 'c', '1'):
+ {
+ gint len = QT_UINT32 (stsd_data) - 0x66;
+ const guint8 *hevc_data = stsd_data + 0x66;
+
+ /* find hevc */
+ while (len >= 0x8) {
+ gint size;
+
+ if (QT_UINT32 (hevc_data) <= len)
+ size = QT_UINT32 (hevc_data) - 0x8;
+ else
+ size = len - 0x8;
+
+ if (size < 1)
+ /* No real data, so break out */
+ break;
+
+ switch (QT_FOURCC (hevc_data + 0x4)) {
+ case GST_MAKE_FOURCC ('h', 'v', 'c', 'C'):
+ {
+ /* parse, if found */
+ GstBuffer *buf;
+
+ GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd");
+
+ /* First 4 bytes are the length of the atom, the next 4 bytes
+ * are the fourcc, the next 1 byte is the version, and the
+ * subsequent bytes are sequence parameter set like data. */
+ //gst_codec_utils_hevc_caps_set_level_and_profile (stream->caps,
+ // hevc_data + 8 + 1, size - 1);
+
+ buf = gst_buffer_new_and_alloc (size);
+ memcpy (GST_BUFFER_DATA (buf), hevc_data + 0x8, size);
+ gst_caps_set_simple (stream->caps,
+ "codec_data", GST_TYPE_BUFFER, buf, NULL);
+ gst_buffer_unref (buf);
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ len -= size + 8;
+ hevc_data += size + 8;
+ }
+
+ break;
+ }
+#endif
case FOURCC_mp4v:
case FOURCC_MP4V:
case FOURCC_fmp4:
@@ -7006,7 +9711,13 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
guint8 *data;
GstBuffer *buf;
gint len;
-
+#ifdef QTDEMUX_MODIFICATION
+ gsize size_codec=0;
+ gint idx=0;
+ guint8 *vol1=NULL;
+ guint8 *vol_start=NULL;
+ guint8 *vo_start=NULL;
+#endif
GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd");
data = glbl->data;
len = QT_UINT32 (data);
@@ -7014,8 +9725,36 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
len -= 0x8;
buf = gst_buffer_new_and_alloc (len);
memcpy (GST_BUFFER_DATA (buf), data + 8, len);
- gst_caps_set_simple (stream->caps,
- "codec_data", GST_TYPE_BUFFER, buf, NULL);
+#ifdef QTDEMUX_MODIFICATION
+ vol1 = GST_BUFFER_DATA (buf);
+ size_codec=len;
+ while (idx < size_codec) {
+ if (vol1[idx] == 0x00 && vol1[idx+1] == 0x00 && vol1[idx+2] == 0x01 && (vol1[idx+3] >= 0x20 && vol1[idx+3] <= 0x2f)) {
+ vol_start = vol1+idx+3;
+ GST_DEBUG_OBJECT(qtdemux,"find the vol start byte");
+ break;
+ }
+ if (vol1[idx] == 0x00 && vol1[idx+1] == 0x00 && vol1[idx+2] == 0x01 && vol1[idx+3]==0xb5) {
+ vo_start = vol1+idx+3;
+ GST_DEBUG_OBJECT(qtdemux,"find the vo start byte");
+ }
+ idx++;
+ }
+ GstMpeg4VideoObjectLayer * vol;
+ GstMpeg4VisualObject * vo;
+ vol=g_malloc(sizeof(GstMpeg4VideoObjectLayer));
+ vo=g_malloc(sizeof(GstMpeg4VisualObject));
+ vol->data_partitioned=0;
+ if (vo_start!=NULL && vol_start!=NULL) {
+ if (gst_mpeg4_parse_visual_object(vo,vo_start,size_codec)==GST_MPEG4_PARSER_OK) {
+ gst_mpeg4_parse_video_object_layer (vol,vo,vol_start,size_codec);
+ }
+ }
+ gst_caps_set_simple (stream->caps,"data_partitioned", G_TYPE_BOOLEAN, vol->data_partitioned, NULL);
+ g_free(vol);
+ g_free(vo);
+#endif
+ gst_caps_set_simple (stream->caps,"codec_data", GST_TYPE_BUFFER, buf, NULL);
gst_buffer_unref (buf);
}
}
@@ -7602,6 +10341,9 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
}
} else
GST_DEBUG ("Didn't find waveheadernode for this codec");
+ } else {
+ GST_ERROR_OBJECT(qtdemux, "failed to parse node object");
+ return FALSE;
}
g_node_destroy (wavenode);
}
@@ -7717,8 +10459,11 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
goto unknown_stream;
}
stream->sampled = TRUE;
+#ifdef QTDEMUX_MODIFICATION
+ } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl) {
+#else
} else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text) {
-
+#endif
stream->sampled = TRUE;
offset = 16;
@@ -7802,8 +10547,58 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
}
/* collect sample information */
- if (!qtdemux_stbl_init (qtdemux, stream, stbl))
- goto samples_failed;
+#ifdef QTDEMUX_MODIFICATION
+ if(!qtdemux->got_moov){
+ if (!qtdemux_stbl_init (qtdemux, stream, stbl))
+ goto samples_failed;
+ }
+#else
+ if (!qtdemux_stbl_init (qtdemux, stream, stbl))
+ goto samples_failed;
+#endif
+#ifdef QTDEMUX_MODIFICATION
+ if(stream->caps && stream->subtype == FOURCC_soun) {
+ GstStructure *s;
+
+ if(stream->caps) {
+ float new_bitrate = 0;
+ gint bitrate = 0;
+ float duration;
+ float audio_stream_size;
+ GST_DEBUG("the details for bitrate are as follows timescale: %u sec", stream->timescale);
+ GST_DEBUG("duration is %" G_GUINT64_FORMAT, stream->duration);
+ GST_DEBUG("sample size is %u", stream->sample_size);
+ GST_DEBUG("sample count is %u", stream->n_samples);
+
+ //new_bitrate = (stream->sample_size * stream->n_samples) /(stream->duration/stream->timescale) * 8;
+ if(stream->timescale != 0) {
+ duration = (float)stream->duration/(float)stream->timescale;
+ audio_stream_size = (float)stream->sample_size * (float)stream->n_samples;
+ GST_DEBUG("the audio stream size is %f and duration is %f", audio_stream_size, duration);
+ if(duration != 0.0) {
+ new_bitrate = (audio_stream_size / duration) * 8;
+ GST_DEBUG("the new bitrate is %f", new_bitrate);
+ }
+ }
+
+ s = gst_caps_get_structure (stream->caps, 0);
+ gst_structure_get_int (s, "bitrate", &bitrate);
+ GST_DEBUG("the original bitrate is %u", bitrate);
+
+ /* some bitrate info may have ended up in caps */
+ if (bitrate <= 0.0 && new_bitrate > 0.0) {
+ GST_DEBUG("tagging the new bitrate");
+ bitrate = new_bitrate;
+ if (!list) {
+ GST_DEBUG("creating a new list for tag");
+ list = gst_tag_list_new ();
+ }
+ gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE,
+ bitrate, NULL);
+ }
+ }
+ }
+#endif
if (qtdemux->fragmented) {
guint32 dummy;
@@ -7844,26 +10639,58 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak)
lang_code = gst_tag_get_language_code (stream->lang_id);
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL);
+#ifdef MULTI_AUDIO
+ if (stream->subtype == FOURCC_soun)
+ qtdemux->Language_list = g_list_append (qtdemux->Language_list,(lang_code) ? lang_code : stream->lang_id);
+#endif
+#ifdef QTDEMUX_MODIFICATION
+ if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl) {
+ QtDemuxLanguageStruct* new = NULL;
+ new = g_new0 (QtDemuxLanguageStruct, 1);
+ new->language_code = (lang_code ? lang_code : stream->lang_id);
+ new->language_key = (lang_code ? lang_code : stream->lang_id);
+ new->active = FALSE;
+ qtdemux->Subtitle_language_list = g_list_append (qtdemux->Subtitle_language_list, new);
+ }
+#endif
}
/* now we are ready to add the stream */
if (qtdemux->n_streams >= GST_QTDEMUX_MAX_STREAMS)
goto too_many_streams;
- stream->pending_tags = list;
- qtdemux->streams[qtdemux->n_streams] = stream;
- qtdemux->n_streams++;
- GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams);
+#ifdef QTDEMUX_MODIFICATION
+ if(!qtdemux->got_moov) {
+ stream->pending_tags = list;
+ qtdemux->streams[qtdemux->n_streams] = stream;
+ qtdemux->n_streams++;
+ GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams);
+ }
+#else
+ stream->pending_tags = list;
+ qtdemux->streams[qtdemux->n_streams] = stream;
+ qtdemux->n_streams++;
+ GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams);
+#endif
return TRUE;
/* ERRORS */
+#ifdef QTDEMUX_MODIFICATION
+skip_track:
+ {
+ GST_INFO_OBJECT (qtdemux, "skip track");
+ g_free (stream);
+ return TRUE;
+ }
+#endif
corrupt_file:
{
GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
(_("This file is corrupt and cannot be played.")), (NULL));
#ifdef QTDEMUX_MODIFICATION
- g_free (stream->trickplay_info);
+ if(stream && stream->trickplay_info)
+ g_free (stream->trickplay_info);
#endif
g_free (stream);
return FALSE;
@@ -7889,6 +10716,14 @@ segments_failed:
g_free (stream);
return FALSE;
}
+#ifdef QTDEMUX_MODIFICATION
+existing_stream:
+ {
+ GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists",
+ track_id);
+ return TRUE;
+ }
+#endif
unknown_stream:
{
GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT,
@@ -8001,6 +10836,147 @@ gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux)
GST_TAG_BITRATE, bitrate, NULL);
}
+#ifdef QTDEMUX_MODIFICATION
+static GstFlowReturn
+qtdemux_prepare_streams (GstQTDemux * qtdemux)
+{
+ gint i;
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ GST_DEBUG_OBJECT (qtdemux, "preparing streams");
+
+ for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
+ QtDemuxStream *stream = qtdemux->streams[i];
+ guint32 sample_num = 0;
+ guint samples = 20;
+ GArray *durations;
+
+ GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
+ i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc));
+ if (qtdemux->fragmented) {
+ /* need all moov samples first */
+ GST_OBJECT_LOCK (qtdemux);
+ while (stream->n_samples == 0)
+ if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK)
+ break;
+ GST_OBJECT_UNLOCK (qtdemux);
+ } else {
+ /* discard any stray moof */
+ qtdemux->moof_offset = 0;
+ }
+
+ /* prepare braking */
+ if (ret != GST_FLOW_ERROR)
+ ret = GST_FLOW_OK;
+
+ /* in pull mode, we should have parsed some sample info by now;
+ * and quite some code will not handle no samples.
+ * in push mode, we'll just have to deal with it */
+ if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) {
+ GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding");
+ gst_qtdemux_stream_free (qtdemux, stream);
+ memmove (&(qtdemux->streams[i]), &(qtdemux->streams[i + 1]),
+ sizeof (QtDemuxStream *) * (GST_QTDEMUX_MAX_STREAMS - i - 1));
+ qtdemux->streams[GST_QTDEMUX_MAX_STREAMS - 1] = NULL;
+ qtdemux->n_streams--;
+ i--;
+ continue;
+ }
+
+ /* parse number of initial sample to set frame rate cap */
+ while (sample_num < stream->n_samples && sample_num < samples) {
+ if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
+ break;
+ ++sample_num;
+ }
+ /* collect and sort durations */
+ samples = MIN (stream->stbl_index + 1, samples);
+ GST_DEBUG_OBJECT (qtdemux, "%d samples for framerate", samples);
+ if (samples) {
+ durations = g_array_sized_new (FALSE, FALSE, sizeof (guint32), samples);
+ sample_num = 0;
+ while (sample_num < samples) {
+ g_array_append_val (durations, stream->samples[sample_num].duration);
+ sample_num++;
+ }
+ g_array_sort (durations, less_than);
+ stream->min_duration = g_array_index (durations, guint32, samples / 2);
+ g_array_free (durations, TRUE);
+ }
+ }
+ return ret;
+}
+
+static GstFlowReturn
+qtdemux_expose_streams (GstQTDemux * qtdemux)
+{
+ gint i;
+ GstFlowReturn ret = GST_FLOW_OK;
+ GSList *oldpads = NULL;
+ GSList *iter;
+
+ GST_DEBUG_OBJECT (qtdemux, "exposing streams");
+
+ for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
+ QtDemuxStream *stream = qtdemux->streams[i];
+ GstPad *oldpad = stream->pad;
+ GstTagList *list;
+
+ GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
+ i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc));
+
+ /* now we have all info and can expose */
+ list = stream->pending_tags;
+ stream->pending_tags = NULL;
+ if (oldpad)
+ oldpads = g_slist_prepend (oldpads, oldpad);
+ gst_qtdemux_add_stream (qtdemux, stream, list);
+#ifdef DRM_ENABLE
+ if (qtdemux->encrypt_content && stream->subtype == FOURCC_vide) {
+ GstEvent* drm_custom_event = NULL;
+ drm_custom_event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
+ gst_structure_new ("Content_Is_DRM_Playready", NULL));
+ GST_DEBUG_OBJECT (qtdemux, "sending custom event to xvimagesink for playready content");
+ if (!gst_pad_push_event (stream->pad, drm_custom_event))
+ GST_ERROR_OBJECT (qtdemux, "failed to push custom event for drm playready content");
+ }
+#endif
+ }
+
+ gst_qtdemux_guess_bitrate (qtdemux);
+
+ gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux));
+
+ for (iter = oldpads; iter; iter = g_slist_next (iter)) {
+ GstPad *oldpad = iter->data;
+
+ gst_pad_push_event (oldpad, gst_event_new_eos ());
+ gst_pad_set_active (oldpad, FALSE);
+ gst_element_remove_pad (GST_ELEMENT (qtdemux), oldpad);
+ gst_object_unref (oldpad);
+ }
+
+ /* check if we should post a redirect in case there is a single trak
+ * and it is a redirecting trak */
+ if (qtdemux->n_streams == 1 && qtdemux->streams[0]->redirect_uri != NULL) {
+ GstMessage *m;
+
+ qtdemux_post_global_tags (qtdemux);
+
+ GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with "
+ "an external content");
+ m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
+ gst_structure_new ("redirect",
+ "new-location", G_TYPE_STRING, qtdemux->streams[0]->redirect_uri,
+ NULL));
+ gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
+ qtdemux->posted_redirect = TRUE;
+ }
+
+ return ret;
+}
+#else
+/* Deprecated */
static GstFlowReturn
qtdemux_expose_streams (GstQTDemux * qtdemux)
{
@@ -8100,6 +11076,7 @@ qtdemux_expose_streams (GstQTDemux * qtdemux)
return ret;
}
+#endif
/* check if major or compatible brand is 3GP */
static inline gboolean
qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
@@ -8926,8 +11903,6 @@ qtdemux_tag_add_blob (GNode * node, GstQTDemux * demux)
if (QT_FOURCC (data + 4) == FOURCC_____ ||
(len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data))
style = "itunes";
- else if (demux->major_brand == FOURCC_qt__)
- style = "quicktime";
/* fall back to assuming iso/3gp tag style */
else
style = "iso";
@@ -9116,82 +12091,6 @@ qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references)
static gboolean
qtdemux_parse_redirects (GstQTDemux * qtdemux)
{
- GNode *rmra, *rmda, *rdrf;
-
- rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra);
- if (rmra) {
- GList *redirects = NULL;
-
- rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda);
- while (rmda) {
- GstQtReference ref = { NULL, NULL, 0, 0 };
- GNode *rmdr, *rmvc;
-
- if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) {
- ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12);
- GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u",
- ref.min_req_bitrate);
- }
-
- if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) {
- guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12);
- guint version = QT_UINT32 ((guint8 *) rmvc->data + 16);
-
-#ifndef GST_DISABLE_GST_DEBUG
- guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20);
-#endif
- guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24);
-
- GST_LOG_OBJECT (qtdemux,
- "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x"
- ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version,
- bitmask, check_type);
- if (package == FOURCC_qtim && check_type == 0) {
- ref.min_req_qt_version = version;
- }
- }
-
- rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf);
- if (rdrf) {
- guint32 ref_type;
- guint8 *ref_data;
-
- ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12);
- ref_data = (guint8 *) rdrf->data + 20;
- if (ref_type == FOURCC_alis) {
- guint record_len, record_version, fn_len;
-
- /* MacOSX alias record, google for alias-layout.txt */
- record_len = QT_UINT16 (ref_data + 4);
- record_version = QT_UINT16 (ref_data + 4 + 2);
- fn_len = QT_UINT8 (ref_data + 50);
- if (record_len > 50 && record_version == 2 && fn_len > 0) {
- ref.location = g_strndup ((gchar *) ref_data + 51, fn_len);
- }
- } else if (ref_type == FOURCC_url_) {
- ref.location = g_strdup ((gchar *) ref_data);
- } else {
- GST_DEBUG_OBJECT (qtdemux,
- "unknown rdrf reference type %" GST_FOURCC_FORMAT,
- GST_FOURCC_ARGS (ref_type));
- }
- if (ref.location != NULL) {
- GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location);
- redirects = g_list_prepend (redirects, g_memdup (&ref, sizeof (ref)));
- } else {
- GST_WARNING_OBJECT (qtdemux,
- "Failed to extract redirect location from rdrf atom");
- }
- }
-
- /* look for others */
- rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda);
- }
-
- if (redirects != NULL) {
- qtdemux_process_redirects (qtdemux, redirects);
- }
- }
return TRUE;
}
@@ -9207,8 +12106,6 @@ qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
fmt = "Motion JPEG 2000";
else if ((qtdemux->major_brand & 0xffff) == GST_MAKE_FOURCC ('3', 'g', 0, 0))
fmt = "3GP";
- else if (qtdemux->major_brand == FOURCC_qt__)
- fmt = "Quicktime";
else if (qtdemux->fragmented)
fmt = "ISO fMP4";
else
@@ -9230,14 +12127,17 @@ qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags)
static gboolean
qtdemux_parse_tree (GstQTDemux * qtdemux)
{
- GNode *mvhd;
- GNode *trak;
- GNode *udta;
- GNode *mvex;
- gint64 duration;
- guint64 creation_time;
+ GNode *mvhd = NULL;
+ GNode *trak = NULL;
+ GNode *udta = NULL;
+ GNode *mvex = NULL;
+ gint64 duration = 0;
+ guint64 creation_time = 0;
GstDateTime *datetime = NULL;
- gint version;
+ gint version = 0;
+#ifdef QTDEMUX_MODIFICATION
+ GNode *uuid_node;
+#endif
mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd);
if (mvhd == NULL) {
@@ -9322,8 +12222,33 @@ qtdemux_parse_tree (GstQTDemux * qtdemux)
/* iterate all siblings */
trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak);
}
-
- /* find tags */
+#ifdef MULTI_AUDIO
+ if(qtdemux->Language_list != NULL)
+ {
+ GstMessage *m;
+ m = gst_message_new_element(GST_OBJECT_CAST(qtdemux),gst_structure_new("Language_list","lang_list",G_TYPE_POINTER,qtdemux->Language_list,NULL));
+ gst_element_post_message(GST_ELEMENT_CAST(qtdemux),m);
+ }
+#endif
+#ifdef QTDEMUX_MODIFICATION
+ if (qtdemux->Subtitle_language_list != NULL) {
+ QtDemuxLanguageStruct* First_Language = g_list_nth_data (qtdemux->Subtitle_language_list, 0);
+ First_Language->active = TRUE;
+ GstMessage *m;
+ m = gst_message_new_element (GST_OBJECT_CAST (qtdemux),
+ gst_structure_new ("Int_Sub_Language_List", "lang_list",
+ G_TYPE_POINTER, qtdemux->Subtitle_language_list, NULL));
+ gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m);
+ }
+ uuid_node = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_uuid);
+#ifdef DRM_ENABLE
+ if (uuid_node && qtdemux->protection_header_present) {
+ GST_INFO_OBJECT(qtdemux,"calling qtdemux_playready_parse_uuid");
+ return qtdemux_playready_parse_uuid(uuid_node,qtdemux);
+ }
+#endif // DRM_ENABLE
+#endif
+/* find tags */
udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta);
if (udta) {
qtdemux_parse_udta (qtdemux, udta);
@@ -9556,10 +12481,43 @@ gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
/* Add the codec_data attribute to caps, if we have it */
if (data_ptr) {
GstBuffer *buffer;
+#ifdef QTDEMUX_MODIFICATION
+ gsize size_codec=0;
+ gint idx=0;
+ guint8 *vol1=NULL;
+ guint8 *vo_start=NULL;
+ guint8 *vol_start=NULL;
+ GstMpeg4VideoObjectLayer * vol;
+ GstMpeg4VisualObject * vo;
+#endif
buffer = gst_buffer_new_and_alloc (data_len);
memcpy (GST_BUFFER_DATA (buffer), data_ptr, data_len);
-
+#ifdef QTDEMUX_MODIFICATION
+ GST_DEBUG_OBJECT (qtdemux,"found not glbl data in stsd");
+ size_codec=data_len;
+ vol1 = GST_BUFFER_DATA (buffer);
+ while (idx < size_codec) {
+ if (vol1[idx] == 0x00 && vol1[idx+1] == 0x00 && vol1[idx+2] == 0x01 && (vol1[idx+3] >= 0x20 && vol1[idx+3] <= 0x2f)) {
+ vol_start = vol1+idx+3;
+ GST_DEBUG_OBJECT(qtdemux,"find the vol byte");
+ break;
+ }
+ if (vol1[idx] == 0x00 && vol1[idx+1] == 0x00 && vol1[idx+2] == 0x01 && vol1[idx+3]==0xb5) {
+ vo_start = vol1+idx+3;
+ GST_DEBUG_OBJECT(qtdemux,"find the VO byte");
+ }
+ idx++;
+ }
+ vol=g_malloc(sizeof(GstMpeg4VideoObjectLayer));
+ vo=g_malloc(sizeof(GstMpeg4VisualObject));
+ vol->data_partitioned=0;
+ if (vo_start!=NULL && vol_start!=NULL) {
+ if (gst_mpeg4_parse_visual_object(vo,vo_start,size_codec)==GST_MPEG4_PARSER_OK){
+ gst_mpeg4_parse_video_object_layer (vol,vo, vol_start,size_codec);
+ }
+ }
+#endif
GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds");
GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len);
@@ -9569,10 +12527,10 @@ gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
gboolean err = TRUE;
int i;
- GST_ERROR_OBJECT (qtdemux, "Checking for the Possibility of H263");
+ GST_INFO_OBJECT (qtdemux, "Checking for the Possibility of H263");
for(i=0; i<data_len-4; i++) {
if(QT_UINT32(data_ptr+i) == 0x00000120) {
- GST_ERROR_OBJECT (qtdemux, "Found the VOL Marker in DCI Info, It is MPEG-4 Content");
+ GST_INFO_OBJECT (qtdemux, "Found the VOL Marker in DCI Info, It is MPEG-4 Content");
err = FALSE;
break;
}
@@ -9586,7 +12544,11 @@ gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream,
}
}
#endif
-
+#ifdef QTDEMUX_MODIFICATION
+ gst_caps_set_simple (stream->caps,"data_partitioned", G_TYPE_BOOLEAN, vol->data_partitioned, NULL);
+ g_free(vol);
+ g_free(vo);
+#endif
gst_caps_set_simple (stream->caps, "codec_data", GST_TYPE_BUFFER,
buffer, NULL);
gst_buffer_unref (buffer);
@@ -9917,6 +12879,12 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'),
NULL);
break;
+#ifdef QTDEMUX_MODIFICATION
+ case GST_MAKE_FOURCC ('h', 'v', 'c', '1'):
+ _codec ("HEVC");
+ caps = gst_caps_from_string ("video/hevc");
+ break;
+#endif
case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'):
default:
{
@@ -9929,6 +12897,12 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
}
}
+#ifdef QTDEMUX_MODIFICATION
+ if(stream->orientation != -1) {
+ gst_caps_set_simple (caps, "orientation", G_TYPE_INT, stream->orientation, NULL);
+ }
+#endif
+
/* enable clipping for raw video streams */
s = gst_caps_get_structure (caps, 0);
name = gst_structure_get_name (s);
@@ -10216,7 +13190,10 @@ gst_qtdemux_forward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, gui
/* find no.of samples between present and previous key frames */
nsamples = stream->trickplay_info->next_kidx - stream->trickplay_info->prev_kidx;
-
+ if(nsamples == 0){
+ GST_LOG_OBJECT (qtdemux, "nsamples comes as 0.. not applying trickplay algo");
+ return;
+ }
/* find average duration between key frames */
stream->trickplay_info->kidxs_dur_diff = (QTSAMPLE_PTS (stream, &stream->samples[stream->trickplay_info->next_kidx]) -
QTSAMPLE_PTS (stream, &stream->samples[stream->trickplay_info->prev_kidx])) /
@@ -10316,5 +13293,193 @@ beach:
return new_index;
}
+#ifdef DRM_ENABLE
+/* Provides key to decrypt encrypted data.*/
+static gboolean
+qtdemux_get_playready_licence (GstQTDemux * qtdemux, unsigned char * data, guint size, char* query_file_path)
+{
+ int ret = -1;
+ char *tok = NULL;
+ char *parse_string = NULL;
+ char *parse_sub_string = NULL;
+ int query_length = 0;
+ int count = 0;
+ drm_trusted_piff_get_license_info_s license_info;
+ drm_trusted_request_type_e request_type = DRM_TRUSTED_REQ_TYPE_PIFF_GET_LICENSE;
+ GST_DEBUG_OBJECT(qtdemux,"size is %d",size);
+ memset(&license_info, 0x00, sizeof(drm_trusted_piff_get_license_info_s));
+
+ query_length = strlen(query_file_path);
+ license_info.lic_header.header = (unsigned char*)data;
+ license_info.lic_header.header_len = size;
+ //license_info.content_provider = DRM_TRUSTED_CONTENT_PROVIDER_VIDEO_HUB;
+
+ GST_DEBUG("filepath infor is %s and lenght is %d", query_file_path, query_length);
+ parse_string = (char*)malloc(sizeof(char) * (query_length + 1));
+ if(parse_string == NULL) {
+ GST_ERROR_OBJECT(qtdemux, "Failed to allocate memory");
+ return FALSE;
+ }
+ strncpy(parse_string, query_file_path, query_length);
+ parse_string[query_length] = '\0';
+ GST_DEBUG("parse string infor is %s and lenght is %d", parse_string, query_length);
+
+ parse_sub_string = strstr(parse_string, "[]<>");
+ GST_DEBUG("filepath infor is %s", parse_sub_string);
+
+ tok = strtok(parse_sub_string, "[]<>");
+ while(tok != NULL) {
+ GST_DEBUG("some info %s", tok);
+ count++;
+ if(count == 1)
+ strcpy(license_info.custom_data.app_id, tok);
+ if(count == 2)
+ strcpy(license_info.custom_data.user_guid, tok);
+ if(count == 3)
+ strcpy(license_info.custom_data.device_id, tok);
+ if(count == 4)
+ strcpy(license_info.custom_data.order_id, tok);
+ if(count == 5)
+ strcpy(license_info.custom_data.mv_id, tok);
+ if(count == 6)
+ strcpy(license_info.custom_data.svr_id, tok);
+ tok = strtok(NULL, "[]<>");
+ }
+ free (parse_string);
+ ret = drm_trusted_handle_request (request_type, (void *) &license_info, NULL);
+ GST_DEBUG_OBJECT(qtdemux, "return value from drm handle request is 0x%x", ret);
+ if (DRM_TRUSTED_RETURN_SUCCESS != ret) {
+ GST_ERROR_OBJECT (qtdemux,"failed to get license...");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT_NOKEY, ("failed to get playready license"), (NULL));
+ return FALSE;
+ }
+
+ GST_INFO_OBJECT (qtdemux, "Got license....\n");
+
+ return TRUE;
+}
+
+void
+test_drm_trusted_operation_cb(drm_trusted_user_operation_info_s *operation_info, void *output_data)
+{
+ GST_DEBUG ("Callback Hit:test_drm_trusted_operation_cb\n");
+ GST_DEBUG ("operation_status=%d\n",operation_info->operation_status);
+ GST_DEBUG ("operation_type=%d\n",operation_info->operation_type);
+}
+
+/* Find out which uuid type is present in uuid node.
+ and returns uuid_type present in uuid node.*/
+
+static uuid_type_t
+qtdemux_get_uuid_type(GstQTDemux * qtdemux, GstByteReader *uuid_data, gint64 *uuid_offset)
+{
+ guint32 box_len = 0;
+ guint64 box_long_len = 0;
+ gchar uuid[16] = {0,};
+ int i = 0;
+
+ if (!gst_byte_reader_get_uint32_be (uuid_data, &box_len))
+ goto invalid_uuid;
+
+ /* Skipping fourcc */
+ if (!gst_byte_reader_skip (uuid_data, 4))
+ goto invalid_uuid;
+ if (box_len == 1) {
+ GST_WARNING_OBJECT (qtdemux, "TfxdBoxLongLength field is present...");
+ if (!gst_byte_reader_get_uint64_be (uuid_data, &box_long_len))
+ goto invalid_uuid;
+
+ GST_DEBUG_OBJECT (qtdemux, "tfxd long length = %llu", box_long_len);
+
+ *uuid_offset = box_long_len;
+ } else {
+ GST_DEBUG_OBJECT (qtdemux, "Box Len = %d", (box_len));
+ *uuid_offset = box_len;
+ }
+
+ for (i = 0; i < sizeof (uuid); i++) {
+ if (!gst_byte_reader_get_uint8 (uuid_data, &(uuid[i])))
+ goto invalid_uuid;
+ }
+
+ if (!memcmp(uuid, tfxd_uuid, sizeof (uuid_t))) {
+ GST_INFO_OBJECT (qtdemux, "Found TFXD box");
+ return UUID_TFXD;
+ } else if (!memcmp(uuid, tfrf_uuid, sizeof (uuid_t))) {
+ GST_INFO_OBJECT (qtdemux, "Found TFRF box");
+ return UUID_TFRF;
+ } else if (!memcmp(uuid, encrypt_uuid, sizeof (uuid_t))) {
+ GST_INFO_OBJECT (qtdemux,"Found sample encryption box");
+ return UUID_SAMPLE_ENCRYPT;
+ } else if (!memcmp(uuid, protection_uuid, sizeof (uuid_t))) {
+ GST_INFO_OBJECT (qtdemux,"Found protection header box");
+ for (i = 0; i < sizeof (uuid); i++) {
+ qtdemux->uuid_protection[i] = uuid[i];
+ }
+ return UUID_PROTECTION_HEADER;
+ } else {
+ GST_WARNING_OBJECT (qtdemux, "Not an valid UUID box..");
+ goto invalid_uuid;
+ }
+
+invalid_uuid:
+ GST_ERROR_OBJECT (qtdemux, "Error in parsing UUID atom...");
+ return UUID_UNKNOWN;
+}
+
+/*Authenticating whether valid play ready system ID is present in uuid node or not.*/
+
+static gboolean
+qtdemux_parse_playready_system_id(GstQTDemux * qtdemux, GstByteReader *uuid_data)
+{
+ //gchar uuid[16] = {0,};
+ guint i;
+ if (!gst_byte_reader_skip (uuid_data, 4))
+ goto invalid_uuid;
+
+ for (i = 0; i < sizeof (qtdemux->uuid_playready); i++){
+ if (!gst_byte_reader_get_uint8 (uuid_data, &(qtdemux->uuid_playready[i])))
+ goto invalid_uuid;
+ }
+
+ if (!memcmp(qtdemux->uuid_playready, playready_system_id, sizeof (uuid_t))){
+ GST_INFO_OBJECT (qtdemux, "Found Play Ready System ID");
+ return TRUE;
+ }
+
+ if (!memcmp(qtdemux->uuid_playready, dash_playready_system_id, sizeof (uuid_t))){
+ GST_INFO_OBJECT (qtdemux, "Found DASH Play Ready System ID");
+ return TRUE;
+ }
+
+invalid_uuid:
+ GST_ERROR_OBJECT (qtdemux, "Invalid system ID...");
+ return FALSE;
+}
+
+static void
+qtdemux_post_drm_error (GstQTDemux * qtdemux, int drm_error)
+{
+ switch (drm_error) {
+ case DRM_LICENSE_STATUS_EXPIRED:
+ GST_ERROR_OBJECT (qtdemux, "posting error as rights expired");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT_NOKEY, ("rights expired"), (NULL));
+ break;
+ case DRM_LICENSE_STATUS_NO_LICENSE:
+ GST_ERROR_OBJECT (qtdemux, "posting error as no rights");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT_NOKEY, ("no rights"), (NULL));
+ break;
+ case DRM_LICENSE_STATUS_FUTURE_USE:
+ GST_ERROR_OBJECT (qtdemux, "posting error as rights for future");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT_NOKEY, ("has future rights"), (NULL));
+ break;
+ case DRM_LICENSE_STATUS_UNDEFINED:
+ default:
+ GST_ERROR_OBJECT (qtdemux, "posting error as undefined");
+ GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT_NOKEY, ("undefind error"), (NULL));
+ break;
+ }
+}
+#endif
#endif
diff --git a/gst/isomp4/qtdemux.h b/gst/isomp4/qtdemux.h
index dd60dcc..71bd4a2 100644..100755
--- a/gst/isomp4/qtdemux.h
+++ b/gst/isomp4/qtdemux.h
@@ -25,9 +25,17 @@
#include <gst/base/gstadapter.h>
#define QTDEMUX_MODIFICATION
+#define MULTI_AUDIO 1
#ifdef QTDEMUX_MODIFICATION
#include <stdio.h>
+#ifdef DRM_ENABLE
+#include <drm_client.h>
+#include <drm_trusted_client.h>
+#include <drm_client_types.h>
+#include <drm_trusted_client_types.h>
+#endif
+#include <gst/base/gstbitreader.h>
#endif
G_BEGIN_DECLS
@@ -58,6 +66,149 @@ typedef struct _GstQTDemux GstQTDemux;
typedef struct _GstQTDemuxClass GstQTDemuxClass;
typedef struct _QtDemuxStream QtDemuxStream;
+#ifdef QTDEMUX_MODIFICATION
+typedef struct _GstMpeg4VideoObjectLayer GstMpeg4VideoObjectLayer;
+typedef struct _GstMpeg4VisualObject GstMpeg4VisualObject;
+typedef struct _QtDemuxDrm QtDemuxDrm;
+typedef enum {
+ GST_MPEG4_SQUARE = 0x01,
+ GST_MPEG4_625_TYPE_4_3 = 0x02,
+ GST_MPEG4_525_TYPE_4_3 = 0x03,
+ GST_MPEG4_625_TYPE_16_9 = 0x04,
+ GST_MPEG4_525_TYPE_16_9 = 0x05,
+ GST_MPEG4_EXTENDED_PAR = 0x0f,
+} GstMpeg4AspectRatioInfo;
+typedef enum {
+ GST_MPEG4_VIDEO_ID = 0x01,
+ GST_MPEG4_STILL_TEXTURE_ID = 0x02,
+ GST_MPEG4_STILL_MESH_ID = 0x03,
+ GST_MPEG4_STILL_FBA_ID = 0x04,
+ GST_MPEG4_STILL_3D_MESH_ID = 0x05,
+ /*... reserved */
+
+} GstMpeg4VisualObjectType;
+typedef enum {
+ /* Other value are reserved */
+ GST_MPEG4_CHROMA_4_2_0 = 0x01
+} GstMpeg4ChromaFormat;
+typedef enum {
+ GST_MPEG4_RECTANGULAR,
+ GST_MPEG4_BINARY,
+ GST_MPEG4_BINARY_ONLY,
+ GST_MPEG4_GRAYSCALE
+} GstMpeg4VideoObjectLayerShape;
+typedef enum {
+ GST_MPEG4_SPRITE_UNUSED,
+ GST_MPEG4_SPRITE_STATIC,
+ GST_MPEG4_SPRITE_GMG
+} GstMpeg4SpriteEnable;
+typedef enum {
+ GST_MPEG4_PARSER_OK,
+ GST_MPEG4_PARSER_BROKEN_DATA,
+ GST_MPEG4_PARSER_NO_PACKET,
+ GST_MPEG4_PARSER_NO_PACKET_END,
+ GST_MPEG4_PARSER_ERROR,
+} GstMpeg4ParseResult;
+struct _GstMpeg4VideoObjectLayer {
+ guint8 random_accessible_vol;
+ guint8 video_object_type_indication;
+
+ guint8 is_object_layer_identifier;
+ /* if is_object_layer_identifier */
+ guint8 verid;
+ guint8 priority;
+
+ GstMpeg4AspectRatioInfo aspect_ratio_info;
+ guint8 par_width;
+ guint8 par_height;
+
+ guint8 control_parameters;
+ /* if control_parameters */
+ GstMpeg4ChromaFormat chroma_format;
+ guint8 low_delay;
+ guint8 vbv_parameters;
+ /* if vbv_parameters */
+ guint16 first_half_bitrate;
+ guint16 latter_half_bitrate;
+ guint16 first_half_vbv_buffer_size;
+ guint16 latter_half_vbv_buffer_size;
+ guint16 first_half_vbv_occupancy;
+ guint16 latter_half_vbv_occupancy;
+
+ /* Computed values */
+ guint32 bit_rate;
+ guint32 vbv_buffer_size;
+
+ GstMpeg4VideoObjectLayerShape shape;
+ /* if shape == GST_MPEG4_GRAYSCALE && verid =! 1 */
+ guint8 shape_extension;
+
+ guint16 vop_time_increment_resolution;
+ guint8 vop_time_increment_bits;
+ guint8 fixed_vop_rate;
+ /* if fixed_vop_rate */
+ guint16 fixed_vop_time_increment;
+
+ guint16 width;
+ guint16 height;
+ guint8 interlaced;
+ guint8 obmc_disable;
+
+ GstMpeg4SpriteEnable sprite_enable;
+ /* if vol->sprite_enable == SPRITE_GMG or SPRITE_STATIC*/
+ /* if vol->sprite_enable != GST_MPEG4_SPRITE_GMG */
+ guint16 sprite_width;
+ guint16 sprite_height;
+ guint16 sprite_left_coordinate;
+ guint16 sprite_top_coordinate;
+
+ guint8 no_of_sprite_warping_points;
+ guint8 sprite_warping_accuracy;
+ guint8 sprite_brightness_change;
+ /* if vol->sprite_enable != GST_MPEG4_SPRITE_GMG */
+ guint8 low_latency_sprite_enable;
+
+ /* if shape != GST_MPEG4_RECTANGULAR */
+ guint8 sadct_disable;
+
+ guint8 not_8_bit;
+
+ /* if no_8_bit */
+ guint8 quant_precision;
+ guint8 bits_per_pixel;
+
+ /* if shape == GRAYSCALE */
+ guint8 no_gray_quant_update;
+ guint8 composition_method;
+ guint8 linear_composition;
+
+ guint8 quant_type;
+ /* if quant_type */
+ guint8 load_intra_quant_mat;
+ guint8 intra_quant_mat[64];
+ guint8 load_non_intra_quant_mat;
+ guint8 non_intra_quant_mat[64];
+
+ guint8 quarter_sample;
+ guint8 complexity_estimation_disable;
+ guint8 resync_marker_disable;
+ guint8 data_partitioned;
+ guint8 reversible_vlc;
+ guint8 newpred_enable;
+ guint8 reduced_resolution_vop_enable;
+ guint8 scalability;
+ guint8 enhancement_type;
+
+ //GstMpeg4VideoPlaneShortHdr short_hdr;
+};
+struct _GstMpeg4VisualObject {
+ guint8 is_identifier;
+ /* If is_identifier */
+ guint8 verid;
+ guint8 priority;
+ GstMpeg4VisualObjectType type;
+};
+#endif
struct _GstQTDemux {
GstElement element;
@@ -77,7 +228,6 @@ struct _GstQTDemux {
guint32 timescale;
guint64 duration;
-
gboolean fragmented;
/* offset of the mfra atom */
guint64 mfra_offset;
@@ -118,14 +268,49 @@ struct _GstQTDemux {
gboolean upstream_seekable;
gboolean upstream_size;
-
+#ifdef MULTI_AUDIO
+ GList *Language_list;
+#endif
#ifdef QTDEMUX_MODIFICATION
+ /*For smooth streaming*/
+ gboolean is_dash;
+ gint64 dash_init_offset;
+ gint video_max_width;
+ gint video_max_height;
+
+ gboolean protection_header_present;
FILE* file;
FILE* ofile;
gchar* filename;
guint filesize;
guint maxbuffersize;
gboolean fwdtrick_mode;
+#ifdef DRM_ENABLE
+ gboolean encrypt_content;
+ DRM_DECRYPT_HANDLE pr_handle;
+ gchar uuid_playready[16];
+ gchar uuid_protection[16];
+#endif
+ gboolean piff_fragmented;
+ GstClockTime max_pop_ts;
+ gboolean playback_protected;
+ gboolean need_parsing_moov;
+ gint iv_size_video;
+ gint iv_size_audio;
+ gchar **iv_data_video;
+ gchar **iv_data_audio;
+ gint sequence_id;
+ gint sample_count[2];
+ gint current_sample[2];
+ gint *sub_sample_count;
+ gint *clear_data;
+ gint *encrypted_data;
+ gboolean dash_content;
+ gint no_of_audio_samples;
+ gint no_of_video_samples;
+ GArray *moof_offsets;
+ gboolean need_moof_parsing;
+ GList *Subtitle_language_list;
#endif
};
diff --git a/gst/isomp4/qtdemux_dump.c b/gst/isomp4/qtdemux_dump.c
index fa66767..a45afd9 100644
--- a/gst/isomp4/qtdemux_dump.c
+++ b/gst/isomp4/qtdemux_dump.c
@@ -330,27 +330,6 @@ qtdemux_dump_stts (GstQTDemux * qtdemux, GstByteReader * data, int depth)
}
gboolean
-qtdemux_dump_stps (GstQTDemux * qtdemux, GstByteReader * data, int depth)
-{
- guint32 ver_flags = 0, num_entries = 0, i;
-
- if (!gst_byte_reader_get_uint32_be (data, &ver_flags) ||
- !gst_byte_reader_get_uint32_be (data, &num_entries))
- return FALSE;
-
- GST_LOG ("%*s version/flags: %08x", depth, "", ver_flags);
- GST_LOG ("%*s n entries: %d", depth, "", num_entries);
-
- if (!qt_atom_parser_has_chunks (data, num_entries, 4))
- return FALSE;
-
- for (i = 0; i < num_entries; i++) {
- GST_LOG ("%*s sample: %u", depth, "", GET_UINT32 (data));
- }
- return TRUE;
-}
-
-gboolean
qtdemux_dump_stss (GstQTDemux * qtdemux, GstByteReader * data, int depth)
{
guint32 ver_flags = 0, num_entries = 0, i;
@@ -489,27 +468,6 @@ qtdemux_dump_co64 (GstQTDemux * qtdemux, GstByteReader * data, int depth)
}
gboolean
-qtdemux_dump_dcom (GstQTDemux * qtdemux, GstByteReader * data, int depth)
-{
- if (!qt_atom_parser_has_remaining (data, 4))
- return FALSE;
-
- GST_LOG ("%*s compression type: %" GST_FOURCC_FORMAT, depth, "",
- GST_FOURCC_ARGS (GET_FOURCC (data)));
- return TRUE;
-}
-
-gboolean
-qtdemux_dump_cmvd (GstQTDemux * qtdemux, GstByteReader * data, int depth)
-{
- if (!qt_atom_parser_has_remaining (data, 4))
- return FALSE;
-
- GST_LOG ("%*s length: %d", depth, "", GET_UINT32 (data));
- return TRUE;
-}
-
-gboolean
qtdemux_dump_mfro (GstQTDemux * qtdemux, GstByteReader * data, int depth)
{
if (!qt_atom_parser_has_remaining (data, 4))
@@ -721,6 +679,28 @@ qtdemux_dump_mehd (GstQTDemux * qtdemux, GstByteReader * data, int depth)
}
gboolean
+qtdemux_dump_tfdt (GstQTDemux * qtdemux, GstByteReader * data, int depth)
+{
+ guint32 version = 0;
+ guint64 decode_time;
+ guint value_size;
+
+ if (!gst_byte_reader_get_uint32_be (data, &version))
+ return FALSE;
+
+ GST_LOG ("%*s version/flags: %08x", depth, "", version);
+
+ value_size = ((version >> 24) == 1) ? sizeof (guint64) : sizeof (guint32);
+ if (qt_atom_parser_get_offset (data, value_size, &decode_time)) {
+ GST_LOG ("%*s Track fragment decode time: %" G_GUINT64_FORMAT,
+ depth, "", decode_time);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
qtdemux_dump_sdtp (GstQTDemux * qtdemux, GstByteReader * data, int depth)
{
guint32 version;
diff --git a/gst/isomp4/qtdemux_dump.h b/gst/isomp4/qtdemux_dump.h
index 9bb1f95..988c822 100644
--- a/gst/isomp4/qtdemux_dump.h
+++ b/gst/isomp4/qtdemux_dump.h
@@ -45,8 +45,6 @@ gboolean qtdemux_dump_stts (GstQTDemux * qtdemux, GstByteReader * data,
int depth);
gboolean qtdemux_dump_stss (GstQTDemux * qtdemux, GstByteReader * data,
int depth);
-gboolean qtdemux_dump_stps (GstQTDemux * qtdemux, GstByteReader * data,
- int depth);
gboolean qtdemux_dump_stsc (GstQTDemux * qtdemux, GstByteReader * data,
int depth);
gboolean qtdemux_dump_stsz (GstQTDemux * qtdemux, GstByteReader * data,
@@ -55,10 +53,6 @@ gboolean qtdemux_dump_stco (GstQTDemux * qtdemux, GstByteReader * data,
int depth);
gboolean qtdemux_dump_co64 (GstQTDemux * qtdemux, GstByteReader * data,
int depth);
-gboolean qtdemux_dump_dcom (GstQTDemux * qtdemux, GstByteReader * data,
- int depth);
-gboolean qtdemux_dump_cmvd (GstQTDemux * qtdemux, GstByteReader * data,
- int depth);
gboolean qtdemux_dump_ctts (GstQTDemux * qtdemux, GstByteReader * data,
int depth);
gboolean qtdemux_dump_mfro (GstQTDemux * qtdemux, GstByteReader * data,
@@ -75,6 +69,8 @@ gboolean qtdemux_dump_mehd (GstQTDemux * qtdemux, GstByteReader * data,
int depth);
gboolean qtdemux_dump_sdtp (GstQTDemux * qtdemux, GstByteReader * data,
int depth);
+gboolean qtdemux_dump_tfdt (GstQTDemux * qtdemux, GstByteReader * data,
+ int depth);
gboolean qtdemux_dump_unknown (GstQTDemux * qtdemux, GstByteReader * data,
int depth);
diff --git a/gst/isomp4/qtdemux_fourcc.h b/gst/isomp4/qtdemux_fourcc.h
index 6666a94..bc65dbc 100644..100755
--- a/gst/isomp4/qtdemux_fourcc.h
+++ b/gst/isomp4/qtdemux_fourcc.h
@@ -31,14 +31,10 @@ G_BEGIN_DECLS
#define FOURCC_clip GST_MAKE_FOURCC('c','l','i','p')
#define FOURCC_trak GST_MAKE_FOURCC('t','r','a','k')
#define FOURCC_udta GST_MAKE_FOURCC('u','d','t','a')
-#define FOURCC_ctab GST_MAKE_FOURCC('c','t','a','b')
#define FOURCC_tkhd GST_MAKE_FOURCC('t','k','h','d')
#define FOURCC_crgn GST_MAKE_FOURCC('c','r','g','n')
-#define FOURCC_matt GST_MAKE_FOURCC('m','a','t','t')
-#define FOURCC_kmat GST_MAKE_FOURCC('k','m','a','t')
#define FOURCC_edts GST_MAKE_FOURCC('e','d','t','s')
#define FOURCC_elst GST_MAKE_FOURCC('e','l','s','t')
-#define FOURCC_load GST_MAKE_FOURCC('l','o','a','d')
#define FOURCC_tref GST_MAKE_FOURCC('t','r','e','f')
#define FOURCC_imap GST_MAKE_FOURCC('i','m','a','p')
#define FOURCC___in GST_MAKE_FOURCC(' ',' ','i','n')
@@ -50,15 +46,12 @@ G_BEGIN_DECLS
#define FOURCC_minf GST_MAKE_FOURCC('m','i','n','f')
#define FOURCC_vmhd GST_MAKE_FOURCC('v','m','h','d')
#define FOURCC_smhd GST_MAKE_FOURCC('s','m','h','d')
-#define FOURCC_gmhd GST_MAKE_FOURCC('g','m','h','d')
-#define FOURCC_gmin GST_MAKE_FOURCC('g','m','i','n')
#define FOURCC_dinf GST_MAKE_FOURCC('d','i','n','f')
#define FOURCC_dref GST_MAKE_FOURCC('d','r','e','f')
#define FOURCC_stbl GST_MAKE_FOURCC('s','t','b','l')
#define FOURCC_stsd GST_MAKE_FOURCC('s','t','s','d')
#define FOURCC_stts GST_MAKE_FOURCC('s','t','t','s')
#define FOURCC_stss GST_MAKE_FOURCC('s','t','s','s')
-#define FOURCC_stps GST_MAKE_FOURCC('s','t','p','s')
#define FOURCC_stsc GST_MAKE_FOURCC('s','t','s','c')
#define FOURCC_stsz GST_MAKE_FOURCC('s','t','s','z')
#define FOURCC_stco GST_MAKE_FOURCC('s','t','c','o')
@@ -68,9 +61,6 @@ G_BEGIN_DECLS
#define FOURCC_strm GST_MAKE_FOURCC('s','t','r','m')
#define FOURCC_rtsp GST_MAKE_FOURCC('r','t','s','p')
#define FOURCC_co64 GST_MAKE_FOURCC('c','o','6','4')
-#define FOURCC_cmov GST_MAKE_FOURCC('c','m','o','v')
-#define FOURCC_dcom GST_MAKE_FOURCC('d','c','o','m')
-#define FOURCC_cmvd GST_MAKE_FOURCC('c','m','v','d')
#define FOURCC_hint GST_MAKE_FOURCC('h','i','n','t')
#define FOURCC_mp4a GST_MAKE_FOURCC('m','p','4','a')
#define FOURCC_mp4v GST_MAKE_FOURCC('m','p','4','v')
@@ -129,13 +119,7 @@ G_BEGIN_DECLS
#define FOURCC_free GST_MAKE_FOURCC('f','r','e','e')
#define FOURCC_data GST_MAKE_FOURCC('d','a','t','a')
#define FOURCC_SVQ3 GST_MAKE_FOURCC('S','V','Q','3')
-#define FOURCC_rmra GST_MAKE_FOURCC('r','m','r','a')
-#define FOURCC_rmda GST_MAKE_FOURCC('r','m','d','a')
-#define FOURCC_rdrf GST_MAKE_FOURCC('r','d','r','f')
#define FOURCC__gen GST_MAKE_FOURCC(0xa9, 'g', 'e', 'n')
-#define FOURCC_rmdr GST_MAKE_FOURCC('r','m','d','r')
-#define FOURCC_rmvc GST_MAKE_FOURCC('r','m','v','c')
-#define FOURCC_qtim GST_MAKE_FOURCC('q','t','i','m')
#define FOURCC_drms GST_MAKE_FOURCC('d','r','m','s')
#define FOURCC_drmi GST_MAKE_FOURCC('d','r','m','i')
#define FOURCC_avc1 GST_MAKE_FOURCC('a','v','c','1')
@@ -213,7 +197,13 @@ G_BEGIN_DECLS
#define FOURCC_XMP_ GST_MAKE_FOURCC('X','M','P','_')
#define FOURCC_uuid GST_MAKE_FOURCC('u','u','i','d')
-
+#define FOURCC_encv GST_MAKE_FOURCC('e','n','c','v')
+#define FOURCC_enca GST_MAKE_FOURCC('e','n','c','a')
+#define FOURCC_pssh GST_MAKE_FOURCC('p','s','s','h')
+#define FOURCC_saiz GST_MAKE_FOURCC('s','a','i','z')
+#define FOURCC_saio GST_MAKE_FOURCC('s','a','i','o')
+#define FOURCC_senc GST_MAKE_FOURCC('s','e','n','c')
+#define FOURCC_mfhd GST_MAKE_FOURCC('m','f','h','d')
/* Fragmented MP4 */
#define FOURCC_mehd GST_MAKE_FOURCC('m','e','h','d')
#define FOURCC_mfhd GST_MAKE_FOURCC('m','f','h','d')
@@ -229,7 +219,12 @@ G_BEGIN_DECLS
#define FOURCC_trun GST_MAKE_FOURCC('t','r','u','n')
#define FOURCC_ovc1 GST_MAKE_FOURCC('o','v','c','1')
#define FOURCC_owma GST_MAKE_FOURCC('o','w','m','a')
+#ifdef QTDEMUX_MODIFICATION
+#define FOURCC_sbtl GST_MAKE_FOURCC('s','b','t','l')
+#endif
+/* MPEG DASH */
+#define FOURCC_tfdt GST_MAKE_FOURCC('t','f','d','t')
G_END_DECLS
#endif /* __GST_QTDEMUX_FOURCC_H__ */
diff --git a/gst/isomp4/qtdemux_types.c b/gst/isomp4/qtdemux_types.c
index 38da35b..3f0a574 100644..100755
--- a/gst/isomp4/qtdemux_types.c
+++ b/gst/isomp4/qtdemux_types.c
@@ -28,16 +28,12 @@ static const QtNodeType qt_node_types[] = {
{FOURCC_clip, "clipping", QT_FLAG_CONTAINER,},
{FOURCC_trak, "track", QT_FLAG_CONTAINER,},
{FOURCC_udta, "user data", QT_FLAG_CONTAINER,}, /* special container */
- {FOURCC_ctab, "color table", 0,},
{FOURCC_tkhd, "track header", 0,
qtdemux_dump_tkhd},
{FOURCC_crgn, "clipping region", 0,},
- {FOURCC_matt, "track matte", QT_FLAG_CONTAINER,},
- {FOURCC_kmat, "compressed matte", 0,},
{FOURCC_edts, "edit", QT_FLAG_CONTAINER,},
{FOURCC_elst, "edit list", 0,
qtdemux_dump_elst},
- {FOURCC_load, "track load settings", 0,},
{FOURCC_tref, "track reference", QT_FLAG_CONTAINER,},
{FOURCC_imap, "track input map", QT_FLAG_CONTAINER,},
{FOURCC___in, "track input", 0,}, /* special container */
@@ -51,8 +47,6 @@ static const QtNodeType qt_node_types[] = {
{FOURCC_vmhd, "video media information", 0,
qtdemux_dump_vmhd},
{FOURCC_smhd, "sound media information", 0},
- {FOURCC_gmhd, "base media information header", 0},
- {FOURCC_gmin, "base media info", 0},
{FOURCC_dinf, "data information", QT_FLAG_CONTAINER},
{FOURCC_dref, "data reference", 0,
qtdemux_dump_dref},
@@ -61,8 +55,6 @@ static const QtNodeType qt_node_types[] = {
qtdemux_dump_stsd},
{FOURCC_stts, "time-to-sample", 0,
qtdemux_dump_stts},
- {FOURCC_stps, "partial sync sample", 0,
- qtdemux_dump_stps},
{FOURCC_stss, "sync sample", 0,
qtdemux_dump_stss},
{FOURCC_stsc, "sample-to-chunk", 0,
@@ -74,9 +66,6 @@ static const QtNodeType qt_node_types[] = {
{FOURCC_co64, "64-bit chunk offset", 0,
qtdemux_dump_co64},
{FOURCC_vide, "video media", 0},
- {FOURCC_cmov, "compressed movie", QT_FLAG_CONTAINER},
- {FOURCC_dcom, "compressed data", 0, qtdemux_dump_dcom},
- {FOURCC_cmvd, "compressed movie data", 0, qtdemux_dump_cmvd},
{FOURCC_hint, "hint", 0,},
{FOURCC_mp4a, "mp4a", 0,},
{FOURCC_mp4v, "mp4v", 0,},
@@ -141,9 +130,6 @@ static const QtNodeType qt_node_types[] = {
{FOURCC_data, "data", 0, qtdemux_dump_unknown},
{FOURCC_free, "free", 0,},
{FOURCC_SVQ3, "SVQ3", 0,},
- {FOURCC_rmra, "rmra", QT_FLAG_CONTAINER,},
- {FOURCC_rmda, "rmda", QT_FLAG_CONTAINER,},
- {FOURCC_rdrf, "rdrf", 0,},
{FOURCC__gen, "Custom Genre", QT_FLAG_CONTAINER,},
{FOURCC_ctts, "Composition time to sample", 0, qtdemux_dump_ctts},
{FOURCC_XiTh, "XiTh", 0},
@@ -171,6 +157,8 @@ static const QtNodeType qt_node_types[] = {
qtdemux_dump_mehd},
{FOURCC_ovc1, "ovc1", 0},
{FOURCC_owma, "owma", 0},
+ {FOURCC_senc, "senc", 0},
+ {FOURCC_tfdt, "Track fragment decode time", 0, qtdemux_dump_tfdt},
{0, "unknown", 0,},
};