AC_SUBST(HAVE_GCONFTOOL)
])
+AC_ARG_ENABLE(pcmdump, AC_HELP_STRING([--enable-pcmdump], [pcm dump]),
+ [
+ case "${enableval}" in
+ yes) PCM_DUMP_ENABLE=yes ;;
+ no) PCM_DUMP_ENABLE=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-pcmdump) ;;
+ esac
+ ],
+ [PCM_DUMP_ENABLE=no])
+AM_CONDITIONAL([PCM_DUMP_ENABLE], [test "x$PCM_DUMP_ENABLE" = "xyes"])
+
+if test "x$PCM_DUMP_ENABLE" = "xyes"; then
+PKG_CHECK_MODULES(VCONF, vconf)
+AC_SUBST(VCONF_CFLAGS)
+AC_SUBST(VCONF_LIBS)
+fi
+
dnl use divx drm --------------------------------------------------------------------------
AC_ARG_ENABLE(divx-drm, AC_HELP_STRING([--enable-divx-drm], [using divx drm]),
[
AG_GST_CHECK_PLUGIN(wavparse)
AG_GST_CHECK_PLUGIN(y4m)
+#PKG_CHECK_MODULES(DRM_CLIENT, drm-client)
+#AC_SUBST(DRM_CLIENT_CFLAGS)
+#AC_SUBST(DRM_CLIENT_LIBS)
+#PKG_CHECK_MODULES(DRM_TRUSTED, drm-trusted)
+#AC_SUBST(DRM_TRUSTED_CFLAGS)
+#AC_SUBST(DRM_TRUSTED_LIBS)
+
+PKG_CHECK_MODULES(INIPARSER, iniparser)
+AC_SUBST(INIPARSER_CFLAGS)
+AC_SUBST(INIPARSER_LIBS)
+
dnl *** checks for socket and nsl libraries ***
AC_CHECK_FUNC(socket,,[AC_CHECK_LIB(socket,socket)])
+++ /dev/null
-gst-plugins-good0.10 (0.10.31-1slp2+1) unstable; urgency=low
-
- * Upgrade 0.10.29 to 0.10.31
- * Git: slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.31-1slp2+1
-
- -- Hyunseok Lee <hs7388.lee@samsung.com> Wed, 27 Jun 2012 21:17:50 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+21) unstable; urgency=low
-
- * Adding unified trickplay of matroska demuxer
- * Git: slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+21
-
- -- Naveen Ch <naveen.ch@samsung.com> Tue, 17 Apr 2012 22:30:17 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+20) unstable; urgency=low
-
- * enable mutipart to use getUserMedia in webkit
- * Git: slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+20
-
- -- Jeongmo Yang <jm80.yang@samsung.com> Thu, 12 Apr 2012 20:16:17 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+19) unstable; urgency=low
-
- * Adding unified trickplay of QT & AVI demuxers
- * Git: slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+19
-
- -- Naveen Ch <naveen.ch@samsung.com> Fri, 30 Mar 2012 07:30:08 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+18) unstable; urgency=low
-
- * Add null check after gst_pad_pull_range in avidemux
- * Git: slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+18
-
- -- Sunghyun Eum <sunghyun.eum@samsung.com> Wed, 15 Feb 2012 07:09:08 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+17) unstable; urgency=low
-
- * Modify souphttpsrc plugin : adding Range field to request header at very first time with offset 0
- * Git: slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+17
-
- -- YeJin Cho <cho.yejin@samsung.com> Tue, 07 Feb 2012 15:46:44 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+16) unstable; urgency=low
-
- * update debian/control for libjpeg(7to8)
- * Git: slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+16
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Thu, 12 Jan 2012 13:23:16 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+15) unstable; urgency=low
-
- * Enable interleave for WebAudio API
- * Git: slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+15
-
- -- Hyunseok Lee <hs7388.lee@samsung.com> Fri, 16 Dec 2011 18:46:33 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+14) unstable; urgency=low
-
- * Code cleanup for source open
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+14
-
- -- Sangchul Lee <sc11.lee@samsung.com> Tue, 06 Dec 2011 16:18:21 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+13) unstable; urgency=low
-
- * Modify scale function for fast forward
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+13
-
- -- Sunghyun Eum <sunghyun.eum@samsung.com> Fri, 25 Nov 2011 19:11:24 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+12) unstable; urgency=low
-
- * Update gst_qtdemux_find_index function to latest version.
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+12
-
- -- Dowan Kim <dowan2171.kim@samsung.com> Mon, 07 Nov 2011 10:30:51 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+11) unstable; urgency=low
-
- * Check the possiblity of H263 in case of mp4v fourCC and correct it.
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+11
-
- -- Hyunseok Lee <hs7388.lee@samsung.com> Wed, 12 Oct 2011 10:31:26 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+10) unstable; urgency=low
-
- * Add dependancy for bz2
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+10
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Fri, 30 Sep 2011 11:38:17 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+9) unstable; urgency=low
-
- * Fix divx drm commit fails
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+9
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Tue, 06 Sep 2011 21:24:19 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+8) unstable; urgency=low
-
- * Remove GTK dependancy
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+8
-
- -- Seungbae Shin <seungbae.shin@samsung.com>> Tue, 06 Sep 2011 19:36:28 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+7) unstable; urgency=low
-
- * Increase version (already previous version exists)
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+7
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Thu, 25 Aug 2011 13:56:03 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+6) unstable; urgency=low
-
- * Remove divxsdk depedancy and use libmm-divxsdk plugin
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+6
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Thu, 25 Aug 2011 13:25:41 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+5) unstable; urgency=low
-
- * enable wavenc
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+5
-
- -- Jeongmo Yang <jm80.yang@samsung.com> Wed, 22 Jun 2011 11:52:03 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+4) unstable; urgency=low
-
- * [avi] merge unmerged divx drm related code
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+4
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Fri, 10 Jun 2011 19:27:03 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+3) unstable; urgency=low
-
- * remove old files
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+3
-
- -- Younghwan Ahn <younghwan_.an@samsung.com> Wed, 08 Jun 2011 17:01:37 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+2) unstable; urgency=low
-
- * update changelog
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+2
-
- -- Younghwan Ahn <younghwan_.an@samsung.com> Tue, 07 Jun 2011 20:41:03 +0900
-
-gst-plugins-good0.10 (0.10.29-18slp2+1) lucid; urgency=low
-
- * upgrade 17 to 29
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.29-18slp2+1
-
- -- Younghwan Ahn <younghwan_.an@samsung.com> Thu, 12 May 2011 19:44:48 +0900
-
-gst-plugins-good0.10 (0.10.17-18slp2+12) unstable; urgency=low
-
- * [avidemux] Fix to get duration from avih in case of push-mode
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-18slp2+12
-
- -- YeJin Cho <cho.yejin@samsung.com> Wed, 16 Mar 2011 17:25:51 +0900
-
-gst-plugins-good0.10 (0.10.17-18slp2+11) unstable; urgency=low
-
- * [avidemux] Return GST_FLOW_ERROR when avidemux fail to parse stream
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-18slp2+11
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Thu, 10 Mar 2011 14:07:20 +0900
-
-gst-plugins-good0.10 (0.10.17-18slp2+10) unstable; urgency=low
-
- * [avidemux] Post error when DivX DRM init fails
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-18slp2+10
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Mon, 07 Mar 2011 19:05:09 +0900
-
-gst-plugins-good0.10 (0.10.17-18slp2+9) unstable; urgency=low
-
- * Revert Disable matroska plugin
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-18slp2+9
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Wed, 16 Feb 2011 10:50:15 +0900
-
-gst-plugins-good0.10 (0.10.17-18slp2+8) unstable; urgency=low
-
- * Disable matroska plugin
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-18slp2+8
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Wed, 09 Feb 2011 15:25:55 +0900
-
-gst-plugins-good0.10 (0.10.17-18slp2+7) unstable; urgency=low
-
- * Support DivX JIT DRM
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-18slp2+7
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Wed, 05 Jan 2011 12:09:11 +0900
-
-gst-plugins-good0.10 (0.10.17-18slp2+6) unstable; urgency=low
-
- * Modified AVI demux to solved DIVX issues
- * Git: 165.213.180.234:slp/pkgs/g/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-18slp2+6
-
- -- Naveen Ch <naveen.ch@samsung.com> Tue, 07 Dec 2010 13:09:22 +0530
-
-gst-plugins-good0.10 (0.10.17-18slp2+5) unstable; urgency=low
-
- * Skip indexing when dd chunk
- * Git: 165.213.180.234:/git/slp/pkgs/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-18slp2+5
-
- -- Seungbae.shin <seungbae.shin@samsung.com> Fri, 03 Dec 2010 16:28:06 +0900
-
-gst-plugins-good0.10 (0.10.17-18slp2+4) unstable; urgency=low
-
- * Merge max/avg bitrate from latest qtdemux
- * Git: 165.213.180.234:/git/slp/pkgs/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-18slp2+4
-
- -- Seungbae.shin <seungbae.shin@samsung.com> Wed, 01 Dec 2010 16:59:38 +0900
-
-gst-plugins-good0.10 (0.10.17-18slp2+3) unstable; urgency=low
-
- * Fix for as-needed
- * Git: 165.213.180.234:/git/slp/pkgs/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-18slp2+3
-
- -- Seungbae.shin <seungbae.shin@samsung.com> Wed, 24 Nov 2010 21:04:10 +0900
-
-gst-plugins-good0.10 (0.10.17-18slp2+2) unstable; urgency=low
-
- * Add dbg package, PIC, as-needed
- * Git: 165.213.180.234:/git/slp/pkgs/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-18slp2+2
-
- -- Seungbae.shin <seungbae.shin@samsung.com> Fri, 19 Nov 2010 10:55:49 +0900
-
-gst-plugins-good0.10 (0.10.17-18slp2+1) unstable; urgency=low
-
- * fix h264 rtph264payloader not to check sps/pps info (by yoserb.yi)
- * enable mulaw,alaw enc/dec to support VoIP via Farsight (by yoserb.yi)
- * Git: 165.213.180.234:/git/slp/pkgs/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-18slp2+1
-
- -- Seungbae.shin <seungbae.shin@samsung.com> Wed, 10 Nov 2010 21:30:23 +0900
-
-gst-plugins-good0.10 (0.10.17-18slp2+0) unstable; urgency=low
-
- * Enable FLV demuxer
- * Git: 165.213.180.234:/git/slp/pkgs/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-18slp2+0
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Fri, 05 Nov 2010 20:26:27 +0900
-
-gst-plugins-good0.10 (0.10.17-17slp2+8) unstable; urgency=low
-
- * Rollback v4l2src to original src except for setting input index
- * Git: 165.213.180.234:/git/slp/pkgs/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-17slp2+8
-
- -- Wonhyung Cho <wh01.cho@samsung.com> Tue, 21 Sep 2010 20:14:45 +0900
-
-gst-plugins-good0.10 (0.10.17-17slp2+7) unstable; urgency=low
-
- * Repackaging for pulse (basesink updated)
- * Git: 165.213.180.234:/git/slp/pkgs/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-17slp2+7
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Wed, 01 Sep 2010 13:41:03 +0900
-
-gst-plugins-good0.10 (0.10.17-17slp2+6) unstable; urgency=low
-
- * Replace libsoup2.4-1 with libsoup2.4 in debian/control
- * Git: 165.213.180.234:/git/slp/pkgs/gst-plugins-good0.10
- * Tag: gst-plugins-good0.10_0.10.17-17slp2+6
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Wed, 25 Aug 2010 11:12:53 +0900
-
-gst-plugins-good0.10 (0.10.17-17slp2+5) unstable; urgency=low
-
- * No change in source code
- * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/gst-plugins-good-0.10.17
- * Tag: gst-plugins-good0.10_0.10.17-17slp2+5
-
- -- Naveen Ch <naveen.ch@samsung.com> Thu, 24 Jun 2010 08:48:53 +0530
-
-gst-plugins-good0.10 (0.10.17-17slp2+4) unstable; urgency=low
-
- * Matroska Demux: Added WebM support in matroska demuxer
- * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/gst-plugins-good-0.10.17
- * Tag: gst-plugins-good0.10_0.10.17-17slp2+4
-
- -- Naveen Ch <naveen.ch@samsung.com> Thu, 17 Jun 2010 16:45:27 +0530
-
-gst-plugins-good0.10 (0.10.17-17slp2+3) unstable; urgency=low
-
- * Matroska Demux: Added reverse trick play functionality
- * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/gst-plugins-good-0.10.17
- * Tag: gst-plugins-good0.10_0.10.17-17slp2+3
-
- -- Naveen Ch <naveen.ch@samsung.com> Wed, 02 Jun 2010 13:01:47 +0530
-
-gst-plugins-good0.10 (0.10.17-17slp2+2) unstable; urgency=low
-
- * AVI Demux: Modified the created mp3 caps to avoid selecting "mp3parse" element
- * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/gst-plugins-good-0.10.17
- * Tag: gst-plugins-good0.10_0.10.17-17slp2+2
-
- -- Prashanth Kumar D <prashanth.kd@samsung.com> Mon, 17 May 2010 18:21:49 +0530
-
-gst-plugins-good0.10 (0.10.17-17slp2+1) unstable; urgency=low
-
- * AVI Demux: Added "framed" field in the caps for AC3 content to avoid selecting ac3parse
- * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/gst-plugins-good-0.10.17
- * Tag: gst-plugins-good0.10_0.10.17-17slp2+1
-
- -- Prashanth Kumar D <prashanth.kd@samsung.com> Fri, 30 Apr 2010 18:08:54 +0530
-
-gst-plugins-good0.10 (0.10.17-17slp2+0) unstable; urgency=low
-
- * Add pulseaudio dependancy
- * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/gst-plugins-good-0.10.17
- * Tag: gst-plugins-good0.10_0.10.17-17slp2+0
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Mon, 12 Apr 2010 15:52:54 +0900
-
-gst-plugins-good0.10 (0.10.17-16slp+1) unstable; urgency=low
-
- * enable matroska demuxer/muxer
- * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/gst-plugins-good-0.10.17
- * Tag: gst-plugins-good0.10_0.10.17-16slp+1
-
- -- Heungsoon Rim <hs06.rim@samsung.com> Fri, 09 Apr 2010 13:32:52 +0900
-
-gst-plugins-good0.10 (0.10.17-16slp+0) unstable; urgency=low
-
- * Change package naming rule
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Thu, 25 Mar 2010 15:13:04 +0900
-
-gst-plugins-good0.10 (0.10.17-16) unstable; urgency=low
-
- * Add uploader list
-
- -- Wonhyung Cho <wh01.cho@samsung.com> Mon, 22 Mar 2010 14:56:25 +0900
-
-gst-plugins-good0.10 (0.10.17-15) unstable; urgency=low
-
- * Because gstbasesrc.h was changed, I build and upload this package again.
-
- -- Wonhyung Cho <wh01.cho@samsung.com> Mon, 22 Mar 2010 14:31:02 +0900
-
-gst-plugins-good0.10 (0.10.17-14) unstable; urgency=low
-
- * Remove taglib dependancy
-
- -- Seungbae Shin <seungbae.shin@samsung.com> Tue, 09 Mar 2010 18:05:52 +0900
-
-gst-plugins-good0.10 (0.10.17-13) unstable; urgency=low
-
- * Added support for generating index table with key frames as an alternate approach to CQ issue H0100083573
-
- -- Naveen Ch <naveen.ch@samsung.com> Mon, 08 Mar 2010 16:58:36 +0530
-
-gst-plugins-good0.10 (0.10.17-12) unstable; urgency=low
-
- * Repacking by kishore
-
- -- YoungHwan Ahn <younghwan_.an@samsung.com> Wed, 24 Feb 2010 21:13:22 +0900
-
-gst-plugins-good0.10 (0.10.17-11) unstable; urgency=low
-
- * avi audio handling for < 4x by kishore
-
- -- YoungHwan Ahn <younghwan_.an@samsung.com> Tue, 23 Feb 2010 22:33:34 +0900
-
-gst-plugins-good0.10 (0.10.17-10) unstable; urgency=low
-
- * avi fixes added for trick play
-
- -- Kishore Arepalli <kishore.a@samsung.com> Tue, 23 Feb 2010 12:39:42 +0530
-
-gst-plugins-good0.10 (0.10.17-9) unstable; urgency=low
-
- * Trick play added for avidemux by kishore
-
- -- younghwan ahn <younghwan_.an@samsung.com> Thu, 18 Feb 2010 13:47:03 +0900
-
-gst-plugins-good0.10 (0.10.17-8) unstable; urgency=low
-
- * Repacking due to the build failed
-
- -- younghwan ahn <younghwan_.an@samsung.com> Sat, 06 Feb 2010 01:42:14 +0900
-
-gst-plugins-good0.10 (0.10.17-7) unstable; urgency=low
-
- * jump to keyframe removed for trick play.. & modified number of lines reduced for trickplay
-
- -- younghwan ahn <younghwan_.an@samsung.com> Sat, 06 Feb 2010 01:26:15 +0900
-
-gst-plugins-good0.10 (0.10.17-6) unstable; urgency=low
-
- * trick play implementation
-
- -- younghwan ahn <younghwan_.an@samsung.com> Wed, 02 Dec 2009 10:35:21 +0900
-
-gst-plugins-good0.10 (0.10.17-5) unstable; urgency=low
-
- * check duplicated timestamp after binary sesearch in qtdemux
-
- -- jongmin lee <jm105.lee@samsung.com> Wed, 02 Dec 2009 10:35:21 +0900
-
-gst-plugins-good0.10 (0.10.17-4) unstable; urgency=low
-
- * force revision up
-
- -- sangho park <sangho.g.park@samsung.com> Mon, 30 Nov 2009 11:07:21 +0900
-
-gst-plugins-good0.10 (0.10.17-3) unstable; urgency=low
-
- * change timeout to 3 sec
-
- -- sangho park <sangho.g.park@samsung.com> Mon, 30 Nov 2009 10:44:16 +0900
-
-gst-plugins-good0.10 (0.10.17-2) unstable; urgency=low
-
- * resolve dependency break
-
- -- jongmin lee <jm105.lee@samsung.com> Tue, 17 Nov 2009 17:56:42 +0900
-
-gst-plugins-good0.10 (0.10.17-1) unstable; urgency=low
-
- * Initial release.
-
- -- jongmin lee <jm105.lee@samsung.com> The, 17 Nov 2009 12:50:00 +0900
+++ /dev/null
-Source: gst-plugins-good0.10
-Section: libs
-Priority: optional
-Maintainer: Shin Seung Bae <seungbae.shin@samsung.com>, JongHyuk Choi <jhchoi.choi@samsung.com>
-Uploaders: younghwan ahn <younghwan_.an@samsung.com>, Naveen Ch <naveen.ch@samsung.com>, Hyunseok Lee <hs7388.lee@samsung.com>, Jeongmo Yang <jm80.yang@samsung.com>
-Build-Depends: libglib2.0-dev (>= 2.16),
- liboil0.3-dev (>= 0.3.8),
- libgstreamer0.10-dev (>= 0.10.25),
- libgstreamer-plugins-base0.10-dev (>= 0.10.25),
- libjpeg8-dev,
- libpng12-dev,
- libsoup2.4-dev,
- libpulse-dev,
- libbz2-dev
-Standards-Version: 3.8.0
-
-Package: gstreamer0.10-plugins-good
-Architecture: any
-Section: libs
-Depends: ${misc:Depends}, ${shlibs:Depends},
- libglib2.0-0 (>= 2.16),
- liboil0.3 (>= 0.3.8),
- libgstreamer0.10-0 (>= 0.10.25),
- libgstreamer-plugins-base0.10-0 (>= 0.10.25),
- libjpeg8,
- libpng12-0,
- libsoup2.4,
- libpulse0,
- libbz2-1.0
-Description: GStreamer plugins from the "good" set
- GStreamer is a streaming media framework, based on graphs of filters
- which operate on media data. Applications using this library can do
- anything from real-time sound processing to playing videos, and just
- about anything else media-related. Its plugin-based architecture means
- that new data types or processing capabilities can be added simply by
- installing new plug-ins.
- .
- This package contains the GStreamer plugins from the "good" set, a set
- of good-quality plug-ins under the LGPL license.
-
-Package: gstreamer0.10-plugins-good-dbg
-Section: debug
-Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, gstreamer0.10-plugins-good (= ${Source-Version})
-Description: GStreamer plugins from the "good" set (unstripped)
+++ /dev/null
-This package was debianized by David I. Lehn <dlehn@debian.org> on
-Mon, 15 Jan 2001 18:21:37 -0500.
-
-It was downloaded from http://gstreamer.net/
-
-Upstream Authors:
-
- Erik Walthinsen <omegahacker@users.sourceforge.net>
- Wim Taymans <wim.taymans@chello.be>
- Richard Boulton <richard@tartarus.org>
- and many more...
-
-Copyright:
-
- This package is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This package is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this package; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-On Debian GNU/Linux systems, the complete text of the GNU Lesser General
-Public License can be found in `/usr/share/common-licenses/LGPL'.
-
+++ /dev/null
-@PREFIX@/lib/gstreamer-0.10/libgst*.so*
+++ /dev/null
-#!/usr/bin/make -f
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-# These are used for cross-compiling and for saving the configure script
-# from having to guess our platform (since we know it already)
-DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
-DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
-
-CFLAGS ?= -Wall -g -fPIC
-LDFLAGS ?=
-PREFIX ?= /usr
-DATADIR ?= /opt
-
-CONFIGURE_OPTION += --disable-static \
- --disable-nls \
- --with-html-dir=/tmp/dump \
- --disable-examples \
- --disable-gconftool \
- --disable-alpha \
- --disable-apetag \
- --disable-audiofx \
- --disable-auparse \
- --disable-cutter \
- --disable-debugutils \
- --disable-deinterlace \
- --disable-effectv \
- --disable-equalizer \
- --disable-icydemux \
- --disable-flx \
- --disable-goom \
- --disable-goom2k1 \
- --disable-level \
- --disable-monoscope \
- --disable-replaygain \
- --disable-smpte \
- --disable-spectrum \
- --disable-videobox \
- --disable-videomixer \
- --disable-y4m \
- --disable-directsound \
- --disable-oss \
- --disable-sunaudio \
- --disable-osx_aidio \
- --disable-osx_video \
- --disable-aalib \
- --disable-aalibtest \
- --disable-annodex \
- --disable-cairo \
- --disable-esd \
- --disable-esdtest \
- --disable-flac \
- --disable-gconf \
- --disable-hal \
- --disable-libcaca \
- --disable-libdv \
- --disable-dv1394 \
- --disable-shout2 \
- --disable-shout2test \
- --disable-speex \
- --disable-taglib
-
-#--disable-wavenc \
-#--disable-bz2 \
-#--disable-jpeg \
-#--disable-autodetext \
-#--disable-wavpack \
-#--disable-avi \
-#--disable-soup \
-#--disable-id3demux \
-#--disable-qtdemux \
-#--disable-rtp \
-#--disable-rtpmanager \
-#--disable-udp \
-#--disable-gst_v4l2 \
-#--disable-taglib \
-#--disable-zlib \
-#--disable-wavparse \
-#--disable-videofilter \
-#--disable-libpng \
-#--disable-x \
-#--disable-xshm \
-#--disable-xvideo \
-#--disable-videocrop \
-
-CFLAGS += -DGST_EXT_SOUP_MODIFICATION
-
-ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
- CFLAGS += -O0
-else
- CFLAGS += -O2
-endif
-
-#ifneq (,$(findstring arm, $(DEB_HOST_GNU_TYPE)))
-#endif
-
-# architecture is not arm
-ifneq (, $(findstring arm, $(DEB_HOST_ARCH)))
- # ARM
- CONFIGURE_OPTION += --enable-divx-drm
-else
- # OTHER
-endif
-
-LDFLAGS += -Wl,--hash-style=both -Wl,--as-needed
-
-config.status: configure
- dh_testdir
- ./autogen.sh
- # Add here commands to configure the package.
- ./configure $(CONFIGURE_OPTION) --prefix=$(PREFIX) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"
-
-#configure:
-# dh_testdir
-# ./autogen.sh
-
-build: build-stamp
-build-stamp: config.status
- dh_testdir
-
- # Add here commands to compile the package.
- $(MAKE)
-
- for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
- cat $$f > $${f%.in}; \
- sed -i -e "s#@PREFIX@#$(PREFIX)#g" $${f%.in}; \
- sed -i -e "s#@DATADIR@#$(DATADIR)#g" $${f%.in}; \
- done
-
- touch $@
-
-clean::
- dh_testdir
- dh_testroot
- rm -f build-stamp stamp-h1
- # Add here commands to clean up after the build process.
- -$(MAKE) clean
-
-ifneq "$(wildcard /usr/share/misc/config.sub)" ""
- cp -f /usr/share/misc/config.sub config.sub
-endif
-ifneq "$(wildcard /usr/share/misc/config.guess)" ""
- cp -f /usr/share/misc/config.guess config.guess
-endif
-
- find ./ -depth -name "Makefile" -exec rm {} -f \;
- find ./ -depth -name ".deps" -exec rm {} -rf \;
- rm -f common/shave
- rm -f common/shave-libtool
- rm -f docs/version.entities
- rm -f gconf/gstreamer.schemas
- rm -f pkgconfig/gstreamer-plugins-good-uninstalled.pc
- rm -f po/Makefile.in
- rm -f po/POTFILES
- rm -f tests/check/elements/.dirstamp
- rm -f win32/common/config.h-new
- rm -f _stdint.h
- rm -f config.h
- rm -f config.log
- rm -f config.status
- rm -f libtool
-
- for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
- rm -f $${f%.in}; \
- done
-
- dh_clean
-
-install: build
- dh_testdir
- dh_testroot
- dh_clean -k
- dh_installdirs
-
- # Add here commands to install the package into debian/tmp
- $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install
-
-# Build architecture-independent files here.
-binary-indep: build install
-# We have nothing to do by default.
-
-# Build architecture-dependent files here.
-binary-arch: build install
- dh_testdir
- dh_testroot
- dh_installchangelogs
- dh_installdocs
- dh_installexamples
- dh_install --sourcedir debian/tmp --list-missing
-# dh_installmenu
-# dh_installdebconf
-# dh_installlogrotate
-# dh_installemacsen
-# dh_installpam
-# dh_installmime
-# dh_python
-# dh_installinit
-# dh_installcron
-# dh_installinfo
- dh_installman
- dh_link
- dh_strip --dbg-package=gstreamer0.10-plugins-good-dbg
- dh_compress
- dh_fixperms
- dh_perl
- dh_makeshlibs
- dh_installdeb
- dh_shlibdeps
- dh_gencontrol
- dh_md5sums
- dh_builddeb
-
-binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install
+++ /dev/null
-#!/usr/bin/make -f
-
-include /usr/share/cdbs/1/rules/simple-patchsys.mk
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-# These are used for cross-compiling and for saving the configure script
-# from having to guess our platform (since we know it already)
-DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
-DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
-
-CFLAGS ?= -Wall -g
-LDFLAGS ?=
-PREFIX ?= /usr
-DATADIR ?= /opt
-
-CONFIGURE_OPTION= --disable-static \
---disable-nls \
---with-html-dir=/tmp/dump \
---disable-examples \
---disable-gconftool \
---disable-alpha \
---disable-apetag \
---disable-audiofx \
---disable-auparse \
---disable-cutter \
---disable-debugutils \
---disable-deinterlace \
---disable-effectv \
---disable-equalizer \
---disable-flv \
---disable-icydemux \
---disable-interleave \
---disable-flx \
---disable-goom \
---disable-goom2k1 \
---disable-law \
---disable-level \
---disable-matroska \
---disable-monoscope \
---disable-multifile \
---disable-multipart \
---disable-replaygain \
---disable-smpte \
---disable-spectrum \
---disable-videobox \
---disable-videomixer \
---disable-wavenc \
---disable-y4m \
---disable-directsound \
---disable-oss \
---disable-sunaudio \
---disable-osx_aidio \
---disable-osx_video \
---disable-aalib \
---disable-aalibtest \
---disable-annodex \
---disable-cairo \
---disable-esd \
---disable-esdtest \
---disable-flac \
---disable-gconf \
---disable-gdk_pixbuf \
---disable-hal \
---disable-libcaca \
---disable-libdv \
---disable-pulse \
---disable-dv1394 \
---disable-shout2 \
---disable-shout2test \
---disable-speex
-
-#--disable-bz2 \
-#--disable-jpeg \
-#--disable-autodetext \
-#--disable-wavpack \
-#--disable-avi \
-#--disable-soup \
-#--disable-id3demux \
-#--disable-qtdemux \
-#--disable-rtp \
-#--disable-rtpmanager \
-#--disable-udp \
-#--disable-gst_v4l2 \
-#--disable-taglib \
-#--disable-zlib \
-#--disable-wavparse \
-#--disable-videofilter \
-#--disable-libpng \
-#--disable-x \
-#--disable-xshm \
-#--disable-xvideo \
-#--disable-videocrop \
-
-
-ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
- CFLAGS += -O0
-else
- CFLAGS += -O2
-endif
-
-#ifneq (,$(findstring arm, $(DEB_HOST_GNU_TYPE)))
-#endif
-
-CFLAGS += -DMODEL_AQUILA
-
-config.status: configure
- dh_testdir
- # Add here commands to configure the package.
- ./configure $(CONFIGURE_OPTION) --prefix=$(PREFIX) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"
-
-#configure:
-# dh_testdir
-# ./autogen.sh
-
-build: build-stamp
-build-stamp: config.status
- dh_testdir
-
- # Add here commands to compile the package.
- $(MAKE)
-
- for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
- cat $$f > $${f%.in}; \
- sed -i -e "s#@PREFIX@#$(PREFIX)#g" $${f%.in}; \
- sed -i -e "s#@DATADIR@#$(DATADIR)#g" $${f%.in}; \
- done
-
- touch $@
-
-clean::
- dh_testdir
- dh_testroot
- rm -f build-stamp stamp-h1
- # Add here commands to clean up after the build process.
- -$(MAKE) clean
-
-ifneq "$(wildcard /usr/share/misc/config.sub)" ""
- cp -f /usr/share/misc/config.sub config.sub
-endif
-ifneq "$(wildcard /usr/share/misc/config.guess)" ""
- cp -f /usr/share/misc/config.guess config.guess
-endif
-
- find ./ -depth -name "Makefile" -exec rm {} -f \;
- find ./ -depth -name ".deps" -exec rm {} -rf \;
- rm -f common/shave
- rm -f common/shave-libtool
- rm -f docs/version.entities
- rm -f gconf/gstreamer.schemas
- rm -f pkgconfig/gstreamer-plugins-good-uninstalled.pc
- rm -f po/Makefile.in
- rm -f po/POTFILES
- rm -f tests/check/elements/.dirstamp
- rm -f win32/common/config.h-new
- rm -f _stdint.h
- rm -f config.h
- rm -f config.log
- rm -f config.status
- rm -f libtool
-
- for f in `find $(CURDIR)/debian/ -name "*.in"`; do \
- rm -f $${f%.in}; \
- done
-
- dh_clean
-
-install: build
- dh_testdir
- dh_testroot
- dh_clean -k
- dh_installdirs
-
- # Add here commands to install the package into debian/tmp
- $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install
-
-# Build architecture-independent files here.
-binary-indep: build install
-# We have nothing to do by default.
-
-# Build architecture-dependent files here.
-binary-arch: build install
- dh_testdir
- dh_testroot
- dh_installchangelogs
- dh_installdocs
- dh_installexamples
- dh_install --sourcedir debian/tmp --list-missing
-# dh_installmenu
-# dh_installdebconf
-# dh_installlogrotate
-# dh_installemacsen
-# dh_installpam
-# dh_installmime
-# dh_python
-# dh_installinit
-# dh_installcron
-# dh_installinfo
- dh_installman
- dh_link
- dh_strip
- dh_compress
- dh_fixperms
- dh_perl
- dh_makeshlibs
- dh_installdeb
- dh_shlibdeps
- dh_gencontrol
- dh_md5sums
- dh_builddeb
-
-binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install
pulsesrc.c \
pulseutil.c
-libgstpulse_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(PULSE_CFLAGS)
-libgstpulse_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) \
+libgstpulse_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(PULSE_CFLAGS) $(INIPARSER_CFLAGS)
+libgstpulse_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) $(INIPARSER_LIBS) \
-lgstinterfaces-$(GST_MAJORMINOR) -lgstpbutils-$(GST_MAJORMINOR) \
$(GST_BASE_LIBS) $(GST_LIBS) $(PULSE_LIBS)
libgstpulse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstpulse_la_LIBTOOLFLAGS = --tag=disable-static
+if PCM_DUMP_ENABLE
+libgstpulse_la_CFLAGS += $(VCONF_CFLAGS) -DPCM_DUMP_ENABLE
+libgstpulse_la_LIBADD += $(VCONF_LIBS)
+endif
+
noinst_HEADERS = \
pulsemixerctrl.h \
pulsemixer.h \
#include <gst/pbutils/pbutils.h> /* only used for GST_PLUGINS_BASE_VERSION_* */
#include <gst/glib-compat-private.h>
-
+#ifdef __TIZEN__
+#include <pulse/ext-policy.h>
+#ifdef PCM_DUMP_ENABLE
+#include <vconf.h>
+#endif
+#include <iniparser.h>
+#endif
#include "pulsesink.h"
#include "pulseutil.h"
PROP_MUTE,
PROP_CLIENT,
PROP_STREAM_PROPERTIES,
+#ifdef __TIZEN__
+ PROP_AUDIO_VOLUME_TYPE,
+ PROP_AUDIO_ROUTE_POLICY,
+ PROP_AUDIO_PRIORITY,
+ PROP_AUDIO_USER_ROUTE,
+ PROP_AUDIO_LATENCY,
+ PROP_AUDIO_CLOSE_HANDLE_ON_PREPARE,
+ PROP_AUDIO_SUPPORT_TYPE,
+ PROP_AUDIO_FADE_DURATION,
+#endif
PROP_LAST
};
+#ifdef __TIZEN__
+#define MEDIA_POLICY_AUTO "auto"
+#define MEDIA_POLICY_PHONE "phone"
+#define MEDIA_POLICY_ALL "all"
+
+#define PULSESINK_MID_LATENCY 150000
+#define PULSESINK_HIGH_LATENCY 400000
+#define PULSESINK_VERY_HIGH_LATENCY 2000000
+#define PULSESINK_WAIT_TIME_FADING 25000
+
+#ifdef PCM_DUMP_ENABLE
+#define GST_PULSESINK_DUMP_VCONF_KEY "memory/private/sound/pcm_dump"
+#define GST_PULSESINK_DUMP_INPUT_PATH_PREFIX "/tmp/dump_pulsesink_in_"
+#define GST_PULSESINK_DUMP_OUTPUT_PATH_PREFIX "/tmp/dump_pulsesink_out_"
+#define GST_PULSESINK_DUMP_INPUT_FLAG 0x00000400
+#define GST_PULSESINK_DUMP_OUTPUT_FLAG 0x00000800
+#endif
+
+typedef enum {
+ PULSESINK_AUDIOROUTE_USE_EXTERNAL_SETTING = -1,
+ PULSESINK_AUDIOROUTE_PLAYBACK_NORMAL,
+ PULSESINK_AUDIOROUTE_PLAYBACK_ALERT,
+ PULSESINK_AUDIOROUTE_PLAYBACK_HEADSET_ONLY
+}GstPulseSinkAudioRoutePolicy;
+
+typedef enum {
+ PULSESINK_USERROUTE_DEFAULT = 0,
+ PULSESINK_USERROUTE_AUTO = 0,
+ PULSESINK_USERROUTE_PHONE,
+ PULSESINK_USERROUTE_ALL
+}GstPulseSinkUserRoutePolicy;
+
+typedef enum {
+ PULSESINK_LATENCY_LOW,
+ PULSESINK_LATENCY_MID,
+ PULSESINK_LATENCY_HIGH,
+ PULSESINK_LATENCY_VERY_HIGH,
+} GstPulseSinkLatency;
+
+typedef enum {
+ PULSESINK_AUDIO_UNMUTE = 0,
+ PULSESINK_AUDIO_MUTE,
+#ifdef FADE_FEATURE
+ PULSESINK_AUDIO_MUTE_WITH_FADEDOWN_EFFECT,
+#endif
+}GstPulseSinkAudioMute;
+
+typedef enum {
+ PULSESINK_VOLUME_TYPE_SYSTEM,
+ PULSESINK_VOLUME_TYPE_NOTIFICATION,
+ PULSESINK_VOLUME_TYPE_ALARM,
+ PULSESINK_VOLUME_TYPE_RINGTONE,
+ PULSESINK_VOLUME_TYPE_MEDIA,
+ PULSESINK_VOLUME_TYPE_CALL,
+ PULSESINK_VOLUME_TYPE_VOIP,
+ PULSESINK_VOLUME_TYPE_SVOICE,
+ PULSESINK_VOLUME_TYPE_FIXED,
+ PULSESINK_VOLUME_TYPE_EXT_SYSTEM_JAVA,
+ PULSESINK_VOLUME_TYPE_NUM,
+ PULSESINK_VOLUME_TYPE_MAX = PULSESINK_VOLUME_TYPE_NUM
+}GstPulseSinkVolumeType;
+
+#endif
+
#define GST_TYPE_PULSERING_BUFFER \
(gst_pulseringbuffer_get_type())
#define GST_PULSERING_BUFFER(obj) \
gst_pulsering_destroy_context (pbuf);
pa_threaded_mainloop_unlock (mainloop);
+#ifdef __TIZEN__
+#ifdef PCM_DUMP_ENABLE
+ if (psink->dump_fd_input) {
+ fclose(psink->dump_fd_input);
+ psink->dump_fd_input = NULL;
+ }
+#endif
+#endif
+
GST_LOG_OBJECT (psink, "closed device");
return TRUE;
#endif
pa_cvolume *pv = NULL;
pa_stream_flags_t flags;
- const gchar *name;
+ const gchar *name = NULL;
GstAudioClock *clock;
#ifdef HAVE_PULSE_1_0
pa_format_info *formats[1];
#endif
#endif
+#ifdef __TIZEN__
+ int vol,gain;
+ const char *policy_str = NULL;
+ const char *cur_policy = NULL;
+ pa_sample_spec ss;
+#endif
+
psink = GST_PULSESINK_CAST (GST_OBJECT_PARENT (buf));
pbuf = GST_PULSERING_BUFFER_CAST (buf);
/* initialize the channel map */
#ifdef HAVE_PULSE_1_0
- if (pbuf->is_pcm && gst_pulse_gst_to_channel_map (&channel_map, spec))
- pa_format_info_set_channel_map (pbuf->format, &channel_map);
+ if (pbuf->is_pcm && gst_pulse_gst_to_channel_map (&channel_map, spec)) {
+ /* FIXME:smart switch is not working */
+ /* pa_format_info_set_channel_map (pbuf->format, &channel_map); */
+ }
#else
gst_pulse_gst_to_channel_map (&channel_map, spec);
#endif
if (psink->stream_name)
name = psink->stream_name;
else
+#ifdef __TIZEN__
+ name = psink->latency == PULSESINK_LATENCY_HIGH ? "HIGH LATENCY Playback" : "MID LATENCY Playback";
+#else
name = "Playback Stream";
+#endif
+
+#ifdef __TIZEN__
+#ifdef PCM_DUMP_ENABLE
+ if (psink->need_dump_input == TRUE && psink->dump_fd_input == NULL) {
+ char *suffix , *dump_path;
+ GDateTime *time = g_date_time_new_now_local();
+
+ suffix = g_date_time_format(time, "%m%d_%H%M%S");
+ dump_path = g_strdup_printf("%s_%s_%dch_%dhz_%s.pcm", GST_PULSESINK_DUMP_INPUT_PATH_PREFIX,
+ (psink->latency == PULSESINK_LATENCY_HIGH) ? "high" : "mid", pbuf->channels, spec->rate, suffix);
+
+ psink->dump_fd_input = fopen(dump_path, "w+");
+
+ g_free(suffix);
+ g_free(dump_path);
+ g_date_time_unref(time);
+ }
+#endif
+
+ if (!psink->proplist)
+ psink->proplist = pa_proplist_new();
+
+ // add gain and volume to proplist
+ vol = psink->volume_type & 0x000000FF;
+ gain = (psink->volume_type >> 8) & 0x000000FF;
+ GST_INFO(">>>>>>>>>> vol(%d), gain(%d), nvalue(%x)", vol, gain, psink->volume_type);
+ pa_proplist_setf(psink->proplist, PA_PROP_MEDIA_TIZEN_VOLUME_TYPE, "%d", vol);
+ pa_proplist_setf(psink->proplist, PA_PROP_MEDIA_TIZEN_GAIN_TYPE, "%d", gain);
+
+ /* set media policy */
+ cur_policy = pa_proplist_gets(psink->proplist, PA_PROP_MEDIA_POLICY);
+ GST_INFO(" : current media policy = [%s]", cur_policy);
+
+ if (cur_policy == NULL || (cur_policy && strlen(cur_policy) == 0)) {
+#ifdef HAVE_PULSE_1_0
+ /* FIXME : this code will moved to HAL later */
+ GST_WARNING_OBJECT (psink, "latency=%d, format=%d, width=%d, rate=%d, policy(%s)",
+ psink->latency, spec->format, spec->width, spec->rate, cur_policy);
+ if (psink->latency >= PULSESINK_LATENCY_HIGH) {
+ if (spec->format == GST_S24_LE && spec->width == 32 && spec->rate >= 44100) {
+ pa_proplist_sets(psink->proplist, PA_PROP_MEDIA_POLICY, "auto");
+ } else {
+ pa_proplist_sets(psink->proplist, PA_PROP_MEDIA_POLICY, "high-latency");
+ }
+ } else { /* low or mid */
+#endif
+ if(PULSESINK_USERROUTE_AUTO == psink->user_route) {
+ if (vol == PULSESINK_VOLUME_TYPE_NOTIFICATION
+ || vol == PULSESINK_VOLUME_TYPE_ALARM) {
+ policy_str = MEDIA_POLICY_ALL;
+ } else if (vol == PULSESINK_VOLUME_TYPE_CALL
+ || vol == PULSESINK_VOLUME_TYPE_RINGTONE
+ || vol == PULSESINK_VOLUME_TYPE_FIXED) {
+ policy_str = MEDIA_POLICY_PHONE;
+ } else {
+ policy_str = MEDIA_POLICY_AUTO;
+ }
+ } else if(PULSESINK_USERROUTE_PHONE == psink->user_route) {
+ policy_str = MEDIA_POLICY_PHONE;
+ } else if(PULSESINK_USERROUTE_ALL == psink->user_route) {
+ policy_str = MEDIA_POLICY_ALL;
+ }
+
+ pa_proplist_sets(psink->proplist, PA_PROP_MEDIA_POLICY, policy_str);
+
+ GST_ERROR(" ==> set property user-route : (%s)", policy_str);
+ }
+ } else {
+ GST_ERROR_OBJECT (psink, " : proplist exists : [%s]", pa_proplist_to_string(psink->proplist));
+ }
+
+#endif
/* create a stream */
#ifdef HAVE_PULSE_1_0
/* buffering requirements. When setting prebuf to 0, the stream will not pause
* when we cause an underrun, which causes time to continue. */
memset (&wanted, 0, sizeof (wanted));
+#ifdef __TIZEN__
+ if (cur_policy && strcmp (cur_policy, "voip") == 0) {
+ ss.format = PA_SAMPLE_S16LE;
+ ss.rate = spec->rate;
+ ss.channels = pbuf->channels;
+ wanted.minreq = pa_usec_to_bytes(20*PA_USEC_PER_MSEC, &ss);
+ wanted.tlength = pa_usec_to_bytes(100*PA_USEC_PER_MSEC, &ss);
+ wanted.maxlength = -1;
+ wanted.prebuf = -1;
+ } else {
+ wanted.tlength = spec->segtotal * spec->segsize;
+ wanted.maxlength = -1;
+ wanted.prebuf = 0;
+ wanted.minreq = spec->segsize;
+ }
+#else
wanted.tlength = spec->segtotal * spec->segsize;
wanted.maxlength = -1;
wanted.prebuf = 0;
wanted.minreq = spec->segsize;
+#endif
GST_INFO_OBJECT (psink, "tlength: %d", wanted.tlength);
GST_INFO_OBJECT (psink, "maxlength: %d", wanted.maxlength);
g_atomic_int_get (&GST_BASE_AUDIO_SINK (psink)->abidata.
ABI.eos_rendering))
gst_pulsering_set_corked (pbuf, FALSE, FALSE);
-
pa_threaded_mainloop_unlock (mainloop);
return TRUE;
/* make sure the commit method stops writing */
pbuf->paused = TRUE;
res = gst_pulsering_set_corked (pbuf, TRUE, TRUE);
+
if (pbuf->in_commit) {
/* we are waiting in a commit, signal */
GST_DEBUG_OBJECT (psink, "signal commit");
}
}
+#ifdef __TIZEN__
+
+GType
+gst_pulsesink_audio_route_get_type (void)
+{
+ static GType playback_audio_route_type = 0;
+ static const GEnumValue playback_audio_route[] = {
+ {PULSESINK_AUDIOROUTE_USE_EXTERNAL_SETTING, "Use external sound path", "external"},
+ {PULSESINK_AUDIOROUTE_PLAYBACK_NORMAL, "Auto change between speaker & earphone", "normal"},
+ {PULSESINK_AUDIOROUTE_PLAYBACK_ALERT, "Play via both speaker & earphone", "alert"},
+ {PULSESINK_AUDIOROUTE_PLAYBACK_HEADSET_ONLY, "Play via earphone only", "headset"},
+ {0, NULL, NULL},
+ };
+
+ if (!playback_audio_route_type) {
+ playback_audio_route_type =
+ g_enum_register_static ("GstPulseSinkAudioRoutePolicy", playback_audio_route);
+ }
+ return playback_audio_route_type;
+}
+
+GType
+gst_pulsesink_user_route_get_type (void)
+{
+ static GType user_route_type = 0;
+ static const GEnumValue user_route[] = {
+ {PULSESINK_USERROUTE_AUTO, "Route automatically", "auto"},
+ {PULSESINK_USERROUTE_PHONE, "Route to phone only", "phone"},
+ {PULSESINK_USERROUTE_ALL, "Route to all", "all"},
+ {0, NULL, NULL},
+ };
+
+ if (!user_route_type) {
+ user_route_type =
+ g_enum_register_static ("GstPulseSinkUserRoutePolicy",user_route);
+ }
+ return user_route_type;
+}
+
+GType
+gst_pulsesink_latency_get_type (void)
+{
+ static GType avsysaudio_latency_type = 0;
+ static const GEnumValue avsysaudio_latency[] = {
+ {PULSESINK_LATENCY_LOW, "Low latency", "low"},
+ {PULSESINK_LATENCY_MID, "Mid latency", "mid"},
+ {PULSESINK_LATENCY_HIGH, "High latency", "high"},
+ {PULSESINK_LATENCY_VERY_HIGH, "Very High latency", "very-high"},
+ {0, NULL, NULL},
+ };
+
+ if (!avsysaudio_latency_type) {
+ avsysaudio_latency_type =
+ g_enum_register_static ("GstPulseSinkLatency", avsysaudio_latency);
+ }
+ return avsysaudio_latency_type;
+}
+
+GType
+gst_pulsesink_audio_mute_get_type (void)
+{
+ static GType avaudio_mute_type = 0;
+ static const GEnumValue avaudio_mute[] = {
+ {PULSESINK_AUDIO_UNMUTE, "Unmute", "unmute"},
+ {PULSESINK_AUDIO_MUTE, "Mute immediately", "mute"},
+#ifdef FADE_FEATURE
+ {PULSESINK_AUDIO_MUTE_WITH_FADEDOWN_EFFECT, "Mute with fadedown effect", "fadedown"},
+#endif
+ {0, NULL, NULL},
+ };
+
+ if (!avaudio_mute_type) {
+ avaudio_mute_type =
+ g_enum_register_static ("GstPulseSinkAudioMute", avaudio_mute);
+ }
+ return avaudio_mute_type;
+}
+
+#ifdef PCM_DUMP_ENABLE
+static gboolean
+gst_pulsesink_pad_dump_handler (GstPad *pad, GstBuffer *buffer, gpointer data)
+{
+ GstPulseSink *psink = GST_PULSESINK_CAST (data);
+ int ret = -1;
+
+ if (psink->dump_fd_input)
+ ret = fwrite(GST_BUFFER_DATA(buffer), 1, GST_BUFFER_SIZE(buffer), psink->dump_fd_input);
+
+ return !!ret;
+}
+#endif
+#endif
+
static void
gst_pulsesink_class_init (GstPulseSinkClass * klass)
{
g_param_spec_double ("volume", "Volume",
"Linear volume of this stream, 1.0=100%", 0.0, MAX_VOLUME,
DEFAULT_VOLUME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+#ifdef __TIZEN__
+ g_object_class_install_property (gobject_class,
+ PROP_MUTE,
+ g_param_spec_enum("mute", "Tizen Audio Mute",
+ "Mute state of this stream", gst_pulsesink_audio_mute_get_type(), PULSESINK_AUDIO_UNMUTE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS ));
+#else
g_object_class_install_property (gobject_class,
PROP_MUTE,
g_param_spec_boolean ("mute", "Mute",
"Mute state of this stream", DEFAULT_MUTE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#endif
/**
* GstPulseSink:client
g_param_spec_boxed ("stream-properties", "stream properties",
"list of pulseaudio stream properties",
GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+#ifdef __TIZEN__
+ g_object_class_install_property (gobject_class,
+ PROP_AUDIO_VOLUME_TYPE,
+ g_param_spec_int ("volumetype", "Avsystem Volume Type",
+ "Select tizen audio software volume type", 0, G_MAXINT, 4,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_AUDIO_ROUTE_POLICY,
+ g_param_spec_enum("audio-route", "Audio Route Policy",
+ "Audio route policy of system", gst_pulsesink_audio_route_get_type(), PULSESINK_AUDIOROUTE_USE_EXTERNAL_SETTING,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS ));
+
+ g_object_class_install_property (gobject_class,
+ PROP_AUDIO_PRIORITY,
+ g_param_spec_int ("priority", "Avsystem Sound Priority",
+ "Avsystem sound priority", 0, G_MAXINT, 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_AUDIO_USER_ROUTE,
+ g_param_spec_enum("user-route", "User Route Policy",
+ "Select tizen user route policy", gst_pulsesink_user_route_get_type(), PULSESINK_USERROUTE_DEFAULT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class,
+ PROP_AUDIO_LATENCY,
+ g_param_spec_enum("latency", "Audio Backend Latency",
+ "Audio backend latency", gst_pulsesink_latency_get_type(), PULSESINK_LATENCY_MID,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS ));
+
+ g_object_class_install_property (gobject_class, PROP_AUDIO_CLOSE_HANDLE_ON_PREPARE,
+ g_param_spec_boolean ("close-handle-on-prepare", "Close Handle on Prepare",
+ "deprecated interface",
+ FALSE, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_AUDIO_SUPPORT_TYPE,
+ g_param_spec_int ("supporttype", "supporttype Type",
+ "Select avsystem audio support type", 0, G_MAXINT,
+ 0, G_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class, PROP_AUDIO_FADE_DURATION,
+ g_param_spec_int ("fade_duration", "fade duration time",
+ "Set fade duration time(msec)", 0, G_MAXINT,
+ 20, G_PARAM_READWRITE));
+#endif
+
}
/* returns the current time of the sink ringbuffer */
}
#endif
+#ifdef __TIZEN__
+static void
+gst_pulsesink_get_configuation(GstPulseSink * pulsesink)
+{
+ dictionary * dict = NULL;
+ const char* path = "/usr/etc/mmfw_audio_config.ini";
+
+ dict = iniparser_load(path);
+ if (!dict) {
+ GST_WARNING_OBJECT(pulsesink, "open failed. path(%s). use default latency", path);
+ pulsesink->buffer_time[PULSESINK_LOCAL_CONFIGURATION_MID] = PULSESINK_MID_LATENCY;
+ pulsesink->buffer_time[PULSESINK_LOCAL_CONFIGURATION_HIGH] = PULSESINK_HIGH_LATENCY;
+ pulsesink->buffer_time[PULSESINK_LOCAL_CONFIGURATION_VERY_HIGH] = PULSESINK_VERY_HIGH_LATENCY;
+ } else {
+ pulsesink->buffer_time[PULSESINK_LOCAL_CONFIGURATION_MID] = iniparser_getint(dict, "pulsesink:mid_buffer_time", PULSESINK_MID_LATENCY);
+ pulsesink->latency_time[PULSESINK_LOCAL_CONFIGURATION_MID] = iniparser_getint(dict, "pulsesink:mid_latency_time", 0);
+ pulsesink->buffer_time[PULSESINK_LOCAL_CONFIGURATION_HIGH] = iniparser_getint(dict, "pulsesink:high_buffer_time", PULSESINK_HIGH_LATENCY);
+ pulsesink->latency_time[PULSESINK_LOCAL_CONFIGURATION_HIGH] = iniparser_getint(dict, "pulsesink:high_latency_time", 0);
+ pulsesink->buffer_time[PULSESINK_LOCAL_CONFIGURATION_VERY_HIGH] = iniparser_getint(dict, "pulsesink:vhigh_buffer_time", PULSESINK_VERY_HIGH_LATENCY);
+ pulsesink->latency_time[PULSESINK_LOCAL_CONFIGURATION_VERY_HIGH] = iniparser_getint(dict, "pulsesink:vhigh_latency_time", 0);
+ }
+ iniparser_freedict(dict);
+}
+#endif
+
static void
gst_pulsesink_init (GstPulseSink * pulsesink, GstPulseSinkClass * klass)
{
+#ifdef __TIZEN__
+#ifdef PCM_DUMP_ENABLE
+ GstPad *sinkpad = NULL;
+ int vconf_dump = 0;
+#endif
+#endif
+
pulsesink->server = NULL;
pulsesink->device = NULL;
pulsesink->device_description = NULL;
pulsesink->mute_set = FALSE;
pulsesink->notify = 0;
+#ifdef __TIZEN__
+ /* default volume is media because webkit use pulsesink without player. */
+ pulsesink->volume_type = 4;
+ pulsesink->fade_stat = PULSESINK_AUDIO_UNMUTE;
+ pulsesink->fade_duration = 20;
+ pulsesink->support_type = 0;
+#ifdef PCM_DUMP_ENABLE
+ if (vconf_get_int(GST_PULSESINK_DUMP_VCONF_KEY, &vconf_dump)) {
+ GST_WARNING("vconf_get_int %s failed", GST_PULSESINK_DUMP_VCONF_KEY);
+ }
+ pulsesink->need_dump_input = vconf_dump & GST_PULSESINK_DUMP_INPUT_FLAG ? TRUE : FALSE;
+ pulsesink->dump_fd_input = NULL;
+ if (pulsesink->need_dump_input) {
+ sinkpad = gst_element_get_static_pad((GstElement *)pulsesink, "sink");
+ gst_pad_add_buffer_probe (sinkpad, G_CALLBACK (gst_pulsesink_pad_dump_handler), pulsesink);
+ }
+#endif
+ memset(pulsesink->buffer_time, 0, sizeof(pulsesink->buffer_time));
+ memset(pulsesink->latency_time, 0, sizeof(pulsesink->latency_time));
+ gst_pulsesink_get_configuation(pulsesink);
+#endif
#ifdef HAVE_PULSE_1_0
g_atomic_int_set (&pulsesink->format_lost, FALSE);
(GstAudioClockGetTimeFunc) gst_pulsesink_get_time, pulsesink);
#ifdef HAVE_PULSE_1_0
+ /* FIXME: disable this which causes delay in VT call */
gst_pad_set_acceptcaps_function (GST_BASE_SINK (pulsesink)->sinkpad,
GST_DEBUG_FUNCPTR (gst_pulsesink_pad_acceptcaps));
#endif
no_mainloop:
{
psink->mute = mute;
- psink->mute_set = TRUE;
+ psink->mute_set = FALSE;
+
+ GST_DEBUG_OBJECT (psink, "we have no mainloop");
+ return;
+ }
+no_buffer:
+ {
+ psink->mute = mute;
+ psink->mute_set = FALSE;
+
+ GST_DEBUG_OBJECT (psink, "we have no ringbuffer");
+ goto unlock;
+ }
+no_index:
+ {
+ GST_DEBUG_OBJECT (psink, "we don't have a stream index");
+ goto unlock;
+ }
+mute_failed:
+ {
+ GST_ELEMENT_ERROR (psink, RESOURCE, FAILED,
+ ("pa_stream_set_sink_input_mute() failed: %s",
+ pa_strerror (pa_context_errno (pbuf->context))), (NULL));
+ goto unlock;
+ }
+}
+
+#ifdef __TIZEN__
+static void success_context_cb(pa_context *c, int success, void *userdata) {
+ pa_threaded_mainloop_signal(mainloop, 0);
+}
+
+static void
+gst_pulsesink_set_mute_sync (GstPulseSink * psink, gboolean mute)
+{
+ pa_operation *o = NULL;
+ GstPulseRingBuffer *pbuf;
+ uint32_t idx;
+
+ GST_WARNING_OBJECT (psink, "setting mute state to mute(%d)", mute);
+
+ pbuf = GST_PULSERING_BUFFER_CAST (GST_BASE_AUDIO_SINK (psink)->ringbuffer);
+ if (pbuf == NULL || pbuf->stream == NULL)
+ goto no_buffer;
+
+ if ((idx = pa_stream_get_index (pbuf->stream)) == PA_INVALID_INDEX)
+ goto no_index;
+
+ if (!(o = pa_context_set_sink_input_mute (pbuf->context, idx,
+ mute, success_context_cb, NULL)))
+ goto mute_failed;
+
+ while (pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
+ pa_threaded_mainloop_wait (mainloop);
+ if (gst_pulsering_is_dead (psink, pbuf, TRUE))
+ goto mute_failed;
+ }
+
+ /* We don't really care about the result of this call */
+unlock:
+
+ if (o)
+ pa_operation_unref (o);
+
+ /* wait for fading time */
+ usleep(PULSESINK_WAIT_TIME_FADING);
+
+ return;
+
+ /* ERRORS */
+no_mainloop:
+ {
+ psink->mute = mute;
+ psink->mute_set = FALSE;
GST_DEBUG_OBJECT (psink, "we have no mainloop");
return;
no_buffer:
{
psink->mute = mute;
- psink->mute_set = TRUE;
+ psink->mute_set = FALSE;
GST_DEBUG_OBJECT (psink, "we have no ringbuffer");
goto unlock;
}
}
+#ifdef FADE_FEATURE
+static void
+gst_pulsesink_set_mute_with_fade (GstPulseSink * psink, int fade)
+{
+ pa_operation *o = NULL;
+ GstPulseRingBuffer *pbuf;
+ uint32_t idx;
+
+ if (!mainloop)
+ goto no_mainloop;
+
+ pa_threaded_mainloop_lock (mainloop);
+
+ GST_WARNING_OBJECT(psink, "mute(fade) is comming mute(%d), fade_stat(%d), fade_duration(%d msec)",
+ fade, psink->fade_stat, psink->fade_duration);
+
+ pbuf = GST_PULSERING_BUFFER_CAST (GST_BASE_AUDIO_SINK (psink)->ringbuffer);
+ if (pbuf == NULL || pbuf->stream == NULL)
+ goto no_buffer;
+
+ if ((idx = pa_stream_get_index (pbuf->stream)) == PA_INVALID_INDEX)
+ goto no_index;
+
+ GST_INFO_OBJECT(psink, "context(%p) idx(%d)", pbuf->context, idx);
+ if (!(o = pa_ext_policy_volume_fade (pbuf->context, idx,
+ fade==PULSESINK_AUDIO_MUTE_WITH_FADEDOWN_EFFECT ? PA_TIZEN_FADEDOWN_REQUEST : PA_TIZEN_FADEUP_REQUEST, psink->fade_duration, NULL, NULL))) {
+ GST_ERROR_OBJECT(psink, "pa_ext_policy_volume_fade failed. stream(%d), fade(%d)", idx, fade);
+ goto mute_failed;
+ } else {
+ psink->fade_stat = fade;
+ }
+ GST_INFO_OBJECT(psink, "end (%d)", psink->fade_stat);
+
+ /* We don't really care about the result of this call */
+unlock:
+
+ if (o)
+ pa_operation_unref (o);
+
+ pa_threaded_mainloop_unlock (mainloop);
+
+ /* should be returned quickly. */
+ //sleep(duration);
+
+ return;
+
+ /* ERRORS */
+no_mainloop:
+ {
+ GST_DEBUG_OBJECT (psink, "we have no mainloop");
+ return;
+ }
+no_buffer:
+ {
+ GST_DEBUG_OBJECT (psink, "we have no ringbuffer");
+ goto unlock;
+ }
+no_index:
+ {
+ GST_DEBUG_OBJECT (psink, "we don't have a stream index");
+ goto unlock;
+ }
+mute_failed:
+ {
+ GST_ELEMENT_ERROR (psink, RESOURCE, FAILED,
+ ("pa_ext_policy_volume_fade() failed: %s",
+ pa_strerror (pa_context_errno (pbuf->context))), (NULL));
+ goto unlock;
+ }
+}
+#endif
+#endif
+
static void
gst_pulsesink_sink_input_info_cb (pa_context * c, const pa_sink_input_info * i,
int eol, void *userdata)
case PROP_VOLUME:
gst_pulsesink_set_volume (pulsesink, g_value_get_double (value));
break;
+#ifdef __TIZEN__
+ case PROP_MUTE: {
+ int nvalue = g_value_get_enum(value);
+#ifdef FADE_FEATURE
+ if(nvalue == PULSESINK_AUDIO_MUTE_WITH_FADEDOWN_EFFECT && pulsesink->fade_stat == PULSESINK_AUDIO_UNMUTE)
+ gst_pulsesink_set_mute_with_fade(pulsesink, PULSESINK_AUDIO_MUTE_WITH_FADEDOWN_EFFECT);
+ else
+#endif
+ gst_pulsesink_set_mute(pulsesink, nvalue==TRUE ? 1 : 0);
+
+ GST_WARNING_OBJECT(pulsesink, "mute end");
+ break;
+ }
+#else
case PROP_MUTE:
gst_pulsesink_set_mute (pulsesink, g_value_get_boolean (value));
break;
+#endif
case PROP_CLIENT:
g_free (pulsesink->client_name);
if (!g_value_get_string (value)) {
pa_proplist_free (pulsesink->proplist);
pulsesink->proplist = gst_pulse_make_proplist (pulsesink->properties);
break;
+
+#ifdef __TIZEN__
+ case PROP_AUDIO_VOLUME_TYPE: {
+ char vol = 0;
+ char gain = 0;
+ gint nvalue = g_value_get_int(value);
+
+ pulsesink->volume_type = nvalue;
+ break;
+ }
+ case PROP_AUDIO_ROUTE_POLICY:
+ break;
+ case PROP_AUDIO_PRIORITY:
+ break;
+ case PROP_AUDIO_USER_ROUTE: {
+ gint nvalue = g_value_get_enum(value);
+ pulsesink->user_route = nvalue;
+ break;
+ }
+ case PROP_AUDIO_LATENCY: {
+ gint nvalue = 0;
+ char policy[32] = { '\0', };
+
+ if (!pulsesink->proplist)
+ pulsesink->proplist = pa_proplist_new();
+
+ nvalue = g_value_get_enum(value);
+
+ if(nvalue >= PULSESINK_LATENCY_HIGH) {
+ sprintf(policy, "%s", "high-latency");
+ pa_proplist_sets(pulsesink->proplist, PA_PROP_MEDIA_POLICY, policy);
+
+ if(pulsesink->buffer_time[PULSESINK_LOCAL_CONFIGURATION_HIGH])
+ GST_BASE_AUDIO_SINK (pulsesink)->buffer_time = pulsesink->buffer_time[PULSESINK_LOCAL_CONFIGURATION_HIGH];
+ if(pulsesink->latency_time[PULSESINK_LOCAL_CONFIGURATION_HIGH])
+ GST_BASE_AUDIO_SINK (pulsesink)->latency_time = pulsesink->latency_time[PULSESINK_LOCAL_CONFIGURATION_HIGH];
+ } else {
+ if(pulsesink->buffer_time[PULSESINK_LOCAL_CONFIGURATION_MID])
+ GST_BASE_AUDIO_SINK (pulsesink)->buffer_time = pulsesink->buffer_time[PULSESINK_LOCAL_CONFIGURATION_MID];
+ if(pulsesink->latency_time[PULSESINK_LOCAL_CONFIGURATION_MID])
+ GST_BASE_AUDIO_SINK (pulsesink)->latency_time = pulsesink->latency_time[PULSESINK_LOCAL_CONFIGURATION_MID];
+ }
+
+ GST_WARNING_OBJECT(pulsesink,"buffer information : latency(%d), buffer_time(%lld), latency_time(%lld)", nvalue,
+ GST_BASE_AUDIO_SINK (pulsesink)->buffer_time, GST_BASE_AUDIO_SINK (pulsesink)->latency_time);
+
+ pulsesink->latency = nvalue;
+ break;
+ }
+ case PROP_AUDIO_CLOSE_HANDLE_ON_PREPARE:
+ // deprecated.
+ break;
+ case PROP_AUDIO_SUPPORT_TYPE:
+ pulsesink->support_type = g_value_get_int(value);
+ if(pulsesink->support_type == 4) { // FIXME: call volume (VT call)
+ pa_proplist_sets(pulsesink->proplist, PA_PROP_MEDIA_POLICY, "voip");
+ GST_WARNING_OBJECT(pulsesink, "support type(%d), use voip(videocall) policy", pulsesink->support_type);
+ }
+ break;
+ case PROP_AUDIO_FADE_DURATION:
+ pulsesink->fade_duration = g_value_get_int(value);
+ break;
+#endif
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
g_value_set_double (value, gst_pulsesink_get_volume (pulsesink));
break;
case PROP_MUTE:
+#ifdef __TIZEN__
+ g_value_set_enum(value, pulsesink->mute);
+#else
g_value_set_boolean (value, gst_pulsesink_get_mute (pulsesink));
+#endif
break;
case PROP_CLIENT:
g_value_set_string (value, pulsesink->client_name);
case PROP_STREAM_PROPERTIES:
gst_value_set_structure (value, pulsesink->properties);
break;
+
+#ifdef __TIZEN__
+ case PROP_AUDIO_VOLUME_TYPE:
+ g_value_set_int (value, pulsesink->volume_type);
+ break;
+ case PROP_AUDIO_ROUTE_POLICY:
+ break;
+ case PROP_AUDIO_PRIORITY:
+ break;
+ case PROP_AUDIO_USER_ROUTE:
+ GST_ERROR("get property user rouyte");
+ g_value_set_enum (value, pulsesink->user_route);
+ break;
+ case PROP_AUDIO_LATENCY:
+ g_value_set_enum (value, pulsesink->latency);
+ break;
+ case PROP_AUDIO_CLOSE_HANDLE_ON_PREPARE:
+ g_value_set_boolean(value, FALSE);
+ break;
+ case PROP_AUDIO_SUPPORT_TYPE:
+ g_value_set_int(value, pulsesink->support_type);
+ break;
+ case PROP_AUDIO_FADE_DURATION:
+ g_value_set_int(value, pulsesink->fade_duration);
+ break;
+#endif
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
gst_message_new_clock_provide (GST_OBJECT_CAST (element),
GST_BASE_AUDIO_SINK (pulsesink)->provided_clock, TRUE));
break;
-
default:
break;
}
if (ret == GST_STATE_CHANGE_FAILURE)
goto state_failure;
+#ifdef __TIZEN__
+ if(transition == GST_STATE_CHANGE_PAUSED_TO_PLAYING) {
+#ifdef FADE_FEATURE
+ if(pulsesink->fade_stat == PULSESINK_AUDIO_MUTE_WITH_FADEDOWN_EFFECT) {
+ gst_pulsesink_set_mute_with_fade(pulsesink, PULSESINK_AUDIO_UNMUTE);
+ }
+#endif
+ }
+#endif
+
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
/* format_lost is reset in release() in baseaudiosink */
#define GST_PULSESINK_CAST(obj) \
((GstPulseSink *)(obj))
+#ifdef __TIZEN__
+enum {
+ PULSESINK_LOCAL_CONFIGURATION_LOW,
+ PULSESINK_LOCAL_CONFIGURATION_MID,
+ PULSESINK_LOCAL_CONFIGURATION_HIGH,
+ PULSESINK_LOCAL_CONFIGURATION_VERY_HIGH,
+ PULSESINK_LOCAL_CONFIGURATION_MAX,
+};
+#endif
+
typedef struct _GstPulseSink GstPulseSink;
typedef struct _GstPulseSinkClass GstPulseSinkClass;
gchar *server, *device, *stream_name, *client_name;
gchar *device_description;
+ gint volume_type;
+ gint latency;
+ gint user_route;
+
GstPulseProbe *probe;
gdouble volume;
gboolean volume_set:1;
+#ifdef __TIZEN__
+ gint mute;
+#else
gboolean mute:1;
+#endif
gboolean mute_set:1;
guint defer_pending;
gint notify; /* atomic */
+#ifdef __TIZEN__
+ gint fade_stat;
+ gint fade_duration;
+ gint support_type;
+ gint buffer_time[PULSESINK_LOCAL_CONFIGURATION_MAX];
+ gint latency_time[PULSESINK_LOCAL_CONFIGURATION_MAX];
+#ifdef PCM_DUMP_ENABLE
+ gint need_dump_input;
+ FILE *dump_fd_input;
+#endif
+#endif
+
const gchar *pa_version;
GstStructure *properties;
"depth = (int) 8, "
"rate = (int) [ 1, MAX ], "
"channels = (int) [ 1, 32 ];"
+ "audio/x-lpcm, "
+ "endianness = (int) { " ENDIANNESS " }, "
+ "signed = (boolean) TRUE, "
+ "width = (int) 16, "
+ "depth = (int) 16, "
+ "rate = (int) [ 1, MAX ], "
+ "channels = (int) [ 1, 32 ];"
"audio/x-alaw, "
"rate = (int) [ 1, MAX], "
"channels = (int) [ 1, 32 ];"
*
* This plugin reads data from a remote location specified by a URI.
* Supported protocols are 'http', 'https'.
- *
+ *
* An HTTP proxy must be specified by its URL.
* If the "http_proxy" environment variable is set, its value is used.
* If built with libsoup's GNOME integration features, the GNOME proxy
#include <gst/tag/tag.h>
#define SEEK_CHANGES
+
GST_DEBUG_CATEGORY_STATIC (souphttpsrc_debug);
#define GST_CAT_DEFAULT souphttpsrc_debug
PROP_IRADIO_URL,
PROP_IRADIO_TITLE,
PROP_TIMEOUT,
+#ifdef GST_EXT_SOUP_MODIFICATION
+ PROP_CONTENT_SIZE,
+ PROP_AHS_STREAMING,
+#endif
#ifdef SEEK_CHANGES
PROP_EXTRA_HEADERS,
- PROP_BLOCKSIZE,
+ PROP_RANGESIZE,
#else
- PROP_EXTRA_HEADERS
+ PROP_EXTRA_HEADERS,
#endif
+ PROP_SOUPCOOKIES
};
-#define DEFAULT_USER_AGENT "GStreamer souphttpsrc "
+#define DEFAULT_USER_AGENT "Gstreamer souphttpsrc "
+
+#ifdef GST_EXT_SOUP_MODIFICATION
+#define DEFAULT_RETRY_TIMEOUT -1
+#define DEFAULT_SESSION_TIMEOUT 20
+#define MIN_SESSION_TIMEOUT 5
+
+#define DLNA_OP_TIMED_SEEK 0x02
+#define DLNA_OP_BYTE_SEEK 0x01
+#endif
static void gst_soup_http_src_uri_handler_init (gpointer g_iface,
gpointer iface_data);
static gboolean gst_soup_http_src_do_seek (GstBaseSrc * bsrc,
GstSegment * segment);
static gboolean gst_soup_http_src_query (GstBaseSrc * bsrc, GstQuery * query);
+#ifdef USE_SAMSUNG_LINK
+static gboolean gst_soup_http_src_event (GstBaseSrc * bsrc, GstEvent *event);
+#endif
+
static gboolean gst_soup_http_src_unlock (GstBaseSrc * bsrc);
static gboolean gst_soup_http_src_unlock_stop (GstBaseSrc * bsrc);
static gboolean gst_soup_http_src_set_location (GstSoupHTTPSrc * src,
static gboolean gst_soup_http_src_build_message (GstSoupHTTPSrc * src);
static void gst_soup_http_src_cancel_message (GstSoupHTTPSrc * src);
static void gst_soup_http_src_queue_message (GstSoupHTTPSrc * src);
+#ifdef GST_EXT_SOUP_MODIFICATION
static gboolean gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src,
- guint64 offset);
+ guint64 offset , gchar *range);
+#else
+static gboolean gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src,
+ guint64 offset)
+#endif
static void gst_soup_http_src_session_unpause_message (GstSoupHTTPSrc * src);
static void gst_soup_http_src_session_pause_message (GstSoupHTTPSrc * src);
static void gst_soup_http_src_session_close (GstSoupHTTPSrc * src);
"HTTP proxy URI user password for authentication", "",
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_COOKIES,
- g_param_spec_boxed ("cookies", "Cookies", "HTTP request cookies",
+ g_param_spec_boxed("cookies", "Cookies", "HTTP request cookies",
G_TYPE_STRV, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class, PROP_SOUPCOOKIES,
+ g_param_spec_pointer("soupcookies", "soupcookies", "web HTTP request cookies",
+ G_PARAM_WRITABLE));
g_object_class_install_property (gobject_class, PROP_IS_LIVE,
g_param_spec_boolean ("is-live", "is-live", "Act like a live source",
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#ifdef GST_EXT_SOUP_MODIFICATION
+ g_object_class_install_property (gobject_class, PROP_TIMEOUT,
+ g_param_spec_int ("timeout", "timeout",
+ "Value in seconds to timeout (0 = No timeout, -1 = default timeout + infinite retry).", -1,
+ G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#else
g_object_class_install_property (gobject_class, PROP_TIMEOUT,
g_param_spec_uint ("timeout", "timeout",
"Value in seconds to timeout a blocking I/O (0 = No timeout).", 0,
3600, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#endif
g_object_class_install_property (gobject_class, PROP_EXTRA_HEADERS,
g_param_spec_boxed ("extra-headers", "Extra Headers",
"Extra headers to append to the HTTP request",
GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
#ifdef SEEK_CHANGES
- g_object_class_install_property (gobject_class, PROP_BLOCKSIZE,
- g_param_spec_int64 ("blocksize", "blocksize",
+ g_object_class_install_property (gobject_class, PROP_RANGESIZE,
+ g_param_spec_int64 ("rangesize", "rangesize",
"Size of each buffer downloaded from libsoup",
-1, G_MAXUINT, 4096, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
#endif
+
+#ifdef GST_EXT_SOUP_MODIFICATION
+ g_object_class_install_property (gobject_class, PROP_CONTENT_SIZE,
+ g_param_spec_uint64 ("content-size", "contentsize",
+ "Total size of the content under download",
+ 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class,
+ PROP_AHS_STREAMING,
+ g_param_spec_boolean ("ahs-streaming", "adaptive HTTP Streaming",
+ "set whether adaptive HTTP streaming(HLS/SS/DASH) or not ",
+ FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#endif
+
/* icecast stuff */
g_object_class_install_property (gobject_class,
PROP_IRADIO_MODE,
gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_soup_http_src_get_size);
gstbasesrc_class->is_seekable =
GST_DEBUG_FUNCPTR (gst_soup_http_src_is_seekable);
+#ifdef USE_SAMSUNG_LINK
+ gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_soup_http_src_event);
+#endif
gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_soup_http_src_do_seek);
gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_soup_http_src_query);
src->read_position = 0;
src->request_position = 0;
src->content_size = 0;
-
+ src->have_body = FALSE;
+#ifdef GST_EXT_SOUP_MODIFICATION
+ src->content_len = 0;
+ src->timeout = DEFAULT_RETRY_TIMEOUT;
+ src->session_timeout = DEFAULT_SESSION_TIMEOUT;
+ src->op_code = 0;
+#endif
#ifdef SEEK_CHANGES
src->file_size = 0;
#endif
"The proxy in the http_proxy env var (\"%s\") cannot be parsed.",
proxy);
}
+#ifdef GST_EXT_SOUP_MODIFICATION
+ src->is_ahs_streaming = FALSE;
+ src->cookie_jar = NULL;
+ src->cookie_list = NULL;
+ src->op_code = 0;
+#endif
gst_soup_http_src_reset (src);
}
g_free (src->user_pw);
g_free (src->proxy_id);
g_free (src->proxy_pw);
+ if (src->cookie_list != NULL) {
+ g_slist_free (src->cookie_list);
+ src->cookie_list = NULL;
+ }
+
g_strfreev (src->cookies);
G_OBJECT_CLASS (parent_class)->finalize (gobject);
{
#ifdef GST_EXT_SOUP_MODIFICATION
char **array;
+ SoupURI *base_uri;
+
#endif
g_strfreev (src->cookies);
src->cookies = g_strdupv (g_value_get_boxed (value));
#ifdef GST_EXT_SOUP_MODIFICATION
- if ((array = src->cookies) != NULL) {
+ if (src->cookie_jar && ((array = src->cookies) != NULL)) {
+ base_uri = soup_uri_new (src->location);
+ GST_INFO_OBJECT (src, "request to set cookies...");
while (*array != NULL) {
soup_cookie_jar_add_cookie (src->cookie_jar,
- soup_cookie_parse (*array++, NULL));
+ soup_cookie_parse (*array++, base_uri));
}
+ soup_uri_free (base_uri);
+ } else {
+ GST_INFO_OBJECT (src, "set cookies after session creation");
}
#endif
break;
}
+#ifdef GST_EXT_SOUP_MODIFICATION
+ case PROP_SOUPCOOKIES:
+ {
+ GSList *walk, *new_cookies;
+ SoupCookie* cookie;
+
+ new_cookies = (GSList*)g_value_get_pointer(value);
+
+ /* remove old list and alloc new */
+ if (src->cookie_list)
+ {
+ g_slist_free (src->cookie_list);
+ }
+ src->cookie_list = g_slist_alloc();
+
+ if ( new_cookies )
+ {
+ walk = new_cookies;
+
+ /* check if cookie_jar is created */
+ if ( src->cookie_jar )
+ {
+ GST_INFO_OBJECT (src, "cookie jar exist. adding it to jar");
+ for ( ; walk; walk = walk->next)
+ {
+ cookie = (SoupCookie*)walk->data;
+ if ( cookie )
+ {
+ GST_INFO_OBJECT (src, "adding cookie to jar : %s - %s",
+ cookie->name, cookie->value);
+ soup_cookie_jar_add_cookie (src->cookie_jar, cookie);
+ }
+ }
+ }
+ else
+ /* cooke jar not created yet. new cookies will be stored in temporal list
+ * and will be added to jar when start() has called */
+ {
+ GST_INFO_OBJECT (src, "cookie jar not yet created. adding to temporal list");
+ for ( ; walk; walk = walk->next )
+ {
+ src->cookie_list = g_slist_prepend(src->cookie_list,
+ soup_cookie_copy(walk->data));
+ }
+ }
+ }
+ break;
+ }
+#endif
+#ifdef GST_EXT_SOUP_MODIFICATION
+ case PROP_AHS_STREAMING:
+ src->is_ahs_streaming = g_value_get_boolean (value);
+ break;
+#endif
case PROP_IS_LIVE:
gst_base_src_set_live (GST_BASE_SRC (src), g_value_get_boolean (value));
break;
src->proxy_pw = g_value_dup_string (value);
break;
case PROP_TIMEOUT:
+#ifdef GST_EXT_SOUP_MODIFICATION
+ src->timeout = g_value_get_int (value);
+#else
src->timeout = g_value_get_uint (value);
+#endif
break;
case PROP_EXTRA_HEADERS:{
const GstStructure *s = gst_value_get_structure (value);
break;
}
#ifdef SEEK_CHANGES
- case PROP_BLOCKSIZE:{
- src->range_size = g_value_get_int64 (value);
- break;
- }
+ case PROP_RANGESIZE:{
+ src->range_size = g_value_get_int64 (value);
+ break;
+ }
#endif
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
case PROP_IRADIO_MODE:
g_value_set_boolean (value, src->iradio_mode);
break;
+#ifdef GST_EXT_SOUP_MODIFICATION
+ case PROP_AHS_STREAMING:
+ g_value_set_boolean (value, src->is_ahs_streaming);
+ break;
+#endif
case PROP_IRADIO_NAME:
g_value_set_string (value, src->iradio_name);
break;
g_value_set_string (value, src->proxy_pw);
break;
case PROP_TIMEOUT:
+#ifdef GST_EXT_SOUP_MODIFICATION
+ g_value_set_int (value, src->timeout);
+#else
g_value_set_uint (value, src->timeout);
+#endif
break;
case PROP_EXTRA_HEADERS:
gst_value_set_structure (value, src->extra_headers);
break;
#ifdef SEEK_CHANGES
- case PROP_BLOCKSIZE:
+ case PROP_RANGESIZE:
g_value_set_int64 (value, src->range_size);
break;
#endif
+#ifdef GST_EXT_SOUP_MODIFICATION
+ case PROP_CONTENT_SIZE:
+ g_value_set_uint64 (value, src->content_len);
+ break;
+#endif
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
static void
gst_soup_http_src_queue_message (GstSoupHTTPSrc * src)
{
+
soup_session_queue_message (src->session, src->msg,
(SoupSessionCallback) gst_soup_http_src_response_cb, src);
src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_QUEUED;
}
+#ifdef GST_EXT_SOUP_MODIFICATION
+static gboolean
+gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src, guint64 offset ,gchar *range)
+#else
static gboolean
gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src, guint64 offset)
+#endif
{
gchar buf[64];
soup_message_headers_remove (src->msg->request_headers, "Range");
#ifdef GST_EXT_SOUP_MODIFICATION
- /* Note : Some http server could not handle Range header in the middle of playing.
- * Need to add Range header at first for seeking properly.
- */
- rc = g_snprintf (buf, sizeof (buf), "bytes=%" G_GUINT64_FORMAT "-", offset);
- if (rc > sizeof (buf) || rc < 0)
- return FALSE;
- soup_message_headers_append (src->msg->request_headers, "Range", buf);
+
+ if ((range != NULL) && src->is_ahs_streaming) {
+ guint len, pos;
+ gchar *str = NULL;
+ guint64 first_byte_pos = 0, last_byte_pos = 0;
+
+ str = range +6 ;//remove 'range='
+ len = strlen(range);
+ GST_DEBUG(" mediaRange : %s", str);
+
+ /* read "-" */
+ pos = strcspn (str, "-");
+ if (pos >= len) {
+ GST_TRACE ("pos %d >= len %d", pos, len);
+ GST_WARNING ("pos >= len !");
+ return FALSE;
+ }
+ /* read first_byte_pos */
+ if (pos != 0) {
+ if (sscanf (str, "%llu", &first_byte_pos) != 1) {
+ GST_WARNING ("can not get first_byte_pos");
+ return FALSE;
+ }
+ }
+ /* read last_byte_pos */
+ if (pos < (len - 1)) {
+ if (sscanf (str + pos + 1, "%llu", &last_byte_pos) != 1) {
+ GST_WARNING ("can not get last_byte_pos");
+ return FALSE;
+ }
+ }
+
+ GST_DEBUG_OBJECT(src,"- requestRange: %" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT,
+ first_byte_pos, last_byte_pos);
+
+ rc = g_snprintf (buf, sizeof (buf), "bytes=%" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT, first_byte_pos, last_byte_pos);
+ if (rc > sizeof (buf) || rc < 0)
+ return FALSE;
+ soup_message_headers_append (src->msg->request_headers, "Range", buf);
+ }
+
+ else {
+
+ if (!src->is_ahs_streaming) {
+ /* Note : Some http server could not handle Range header in the middle of playing.
+ * Need to add Range header at first for seeking properly.
+ */
+ rc = g_snprintf (buf, sizeof (buf), "bytes=%" G_GUINT64_FORMAT "-", offset);
+ if (rc > sizeof (buf) || rc < 0)
+ return FALSE;
+ soup_message_headers_append (src->msg->request_headers, "Range", buf);
+ } else {
+ if (offset) {
+ rc = g_snprintf (buf, sizeof (buf), "bytes=%" G_GUINT64_FORMAT "-", offset);
+ if (rc > sizeof (buf) || rc < 0)
+ return FALSE;
+ soup_message_headers_append (src->msg->request_headers, "Range", buf);
+ }
+ }
+}
#else
if (offset) {
rc = g_snprintf (buf, sizeof (buf), "bytes=%" G_GUINT64_FORMAT "-", offset);
field_content);
soup_message_headers_append (src->msg->request_headers, field_name,
field_content);
+#ifdef GST_EXT_SOUP_MODIFICATION
+ if (!g_ascii_strcasecmp(field_name, "Cookie")) {
+ SoupURI *uri = NULL;
+ SoupCookie *cookie_parsed = NULL;
+
+ if (strlen(field_content) > 0) {
+ gchar *tmp_field = NULL;
+
+ uri = soup_uri_new (src->location);
+
+ tmp_field = strtok (field_content, ";");
+
+ while (tmp_field != NULL) {
+ GST_DEBUG_OBJECT (src, "field_content = %s", tmp_field);
+
+ cookie_parsed = soup_cookie_parse(tmp_field, uri);
+ GST_DEBUG_OBJECT (src, "cookie parsed = %p", cookie_parsed);
+
+ if (src->cookie_jar)
+ soup_cookie_jar_add_cookie (src->cookie_jar, cookie_parsed);
+
+ tmp_field = strtok (NULL, ";");
+ }
+ soup_uri_free (uri);
+ }
+ }
+#endif
g_free (field_content);
gpointer src)
{
GST_DEBUG_OBJECT (src, " %s: %s", name, val);
+
+#ifdef GST_EXT_SOUP_MODIFICATION
+ if (g_ascii_strcasecmp (name, "Set-Cookie") == 0)
+ {
+ if (val)
+ {
+ gboolean bret = FALSE;
+ GstStructure *s = NULL;
+ GstSoupHTTPSrc * tmp = src;
+ SoupURI *uri;
+
+ uri = soup_uri_new (tmp->location);
+
+ /* post current bandwith & uri to application */
+ s = gst_structure_new ("cookies",
+ "updated-cookie", G_TYPE_STRING, val,
+ "updated-url", G_TYPE_STRING, tmp->location, NULL);
+ bret = gst_element_post_message (GST_ELEMENT_CAST (src), gst_message_new_element (GST_OBJECT_CAST (src), s));
+ soup_cookie_jar_set_cookie (tmp->cookie_jar, uri, val);
+ soup_uri_free (uri);
+
+ GST_INFO_OBJECT (src, "request url [%s], posted cookies [%s] msg and returned = %d", tmp->location, val, bret);
+ }
+ }
+#endif
}
static void
GHashTable *params = NULL;
GST_DEBUG_OBJECT (src, "got headers:");
+
+#ifdef GST_EXT_SOUP_MODIFICATION
+ src->retry_timestamp = 0;
+#endif
+
soup_message_headers_foreach (msg->response_headers,
gst_soup_http_src_headers_foreach, src);
src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING;
+#ifdef GST_EXT_SOUP_MODIFICATION
+ if ((value =
+ soup_message_headers_get (msg->response_headers,
+ "contentFeatures.dlna.org")) != NULL) {
+ gchar **token = NULL;
+ gchar **ptr = NULL;
+
+ token = g_strsplit (value, ";", 0);
+
+ for (ptr = token ; *ptr ; ptr++)
+ {
+ if (strlen(*ptr) > 0)
+ {
+ if (strstr(g_ascii_strup(*ptr, strlen(*ptr)), "DLNA.ORG_OP") != NULL)
+ {
+ gchar *op_code = NULL;
+
+ op_code = strchr(*ptr, '=');
+ if (op_code)
+ {
+ op_code++;
+
+ src->op_code = (atoi(op_code)/10 <<1) | (atoi(op_code)%10);
+ GST_DEBUG_OBJECT (src, "dlna op code: %s (0x%X)", op_code, src->op_code);
+ }
+ }
+ }
+ }
+ g_strfreev (token);
+ }
+
+ if ((value = soup_message_headers_get (msg->response_headers, "Content-Duration")) != NULL) {
+ src->duration = atoi(value) * G_GINT64_CONSTANT (1000000);
+ GST_DEBUG_OBJECT (src, "Content Duration is %"GST_TIME_FORMAT, GST_TIME_ARGS (src->duration));
+ }
+
+#endif
+
+#ifdef USE_SAMSUNG_LINK
+ if ((value = soup_message_headers_get (msg->response_headers, "X-ASP-DURATION-TIME")) != NULL) {
+ src->is_x_asp = TRUE;
+ src->duration = atoi(value) * G_GINT64_CONSTANT (1000000);
+ GST_DEBUG_OBJECT (src, "X-ASP-DURATION-TIME is %"GST_TIME_FORMAT, GST_TIME_ARGS (src->duration));
+ }
+#endif
+
/* Parse Content-Length. */
if (soup_message_headers_get_encoding (msg->response_headers) ==
SOUP_ENCODING_CONTENT_LENGTH) {
#endif
src->have_size = TRUE;
src->seekable = TRUE;
- GST_DEBUG_OBJECT (src, "size = %" G_GUINT64_FORMAT, src->content_size);
-
+ GST_DEBUG_OBJECT (src, "content size = %" G_GUINT64_FORMAT, src->content_size);
+#ifdef GST_EXT_SOUP_MODIFICATION
+ src->content_len = src->content_size;
+#endif
basesrc = GST_BASE_SRC_CAST (src);
gst_segment_set_duration (&basesrc->segment, GST_FORMAT_BYTES,
src->content_size);
if(total_length > 0)
{
src->file_size = total_length;
- GST_DEBUG_OBJECT (src, "size = %" G_GUINT64_FORMAT, src->file_size);
+ GST_DEBUG_OBJECT (src, "total size = %" G_GUINT64_FORMAT, src->file_size);
basesrc = GST_BASE_SRC_CAST (src);
gst_segment_set_duration (&basesrc->segment, GST_FORMAT_BYTES,
src->file_size);
}
#endif
}
+#ifdef GST_EXT_SOUP_MODIFICATION
+ else if ((soup_message_headers_get_encoding (msg->response_headers) ==
+ SOUP_ENCODING_CHUNKED) && (src->op_code & DLNA_OP_BYTE_SEEK)) {
+ soup_message_headers_get_content_range(msg->response_headers, &start, &end, &total_length);
+ if(total_length > 0)
+ {
+ src->have_size = TRUE;
+ src->seekable = TRUE;
+
+ src->content_size = src->content_len = src->file_size = total_length;
+
+ GST_DEBUG_OBJECT (src, "total size = %" G_GUINT64_FORMAT, src->file_size);
+ basesrc = GST_BASE_SRC_CAST (src);
+ gst_segment_set_duration (&basesrc->segment, GST_FORMAT_BYTES,
+ src->file_size);
+ gst_element_post_message (GST_ELEMENT (src),
+ gst_message_new_duration (GST_OBJECT (src), GST_FORMAT_BYTES,
+ src->file_size));
+ }
+ }
+#endif
/* Icecast stuff */
tag_list = gst_tag_list_new ();
"metadata-interval", G_TYPE_INT, icy_metaint, NULL);
}
}
+#ifdef SEEK_CHANGES
+ if ((msg->status_code == 200) && (value=soup_message_headers_get (msg->response_headers,"Accept-Ranges")) != NULL) {
+ GST_DEBUG_OBJECT (src, "Accept-ranges: %s ", value);
+ if(strstr(value,"none")!=NULL) {
+ src->seekable=FALSE;
+ GST_DEBUG_OBJECT (src, "server is not seekable");
+ }
+ else
+ src->seekable=TRUE;
+ }
+ else if(msg->status_code == 200) {
+ src->seekable=FALSE;
+ GST_DEBUG_OBJECT (src, "server is not seekable");
+ }
+#endif
if ((value =
soup_message_headers_get_content_type (msg->response_headers,
¶ms)) != NULL) {
GST_DEBUG_OBJECT (src, "Content-Type: %s", value);
+
if (g_ascii_strcasecmp (value, "audio/L16") == 0) {
gint channels = 2;
gint rate = 44100;
/* Handle HTTP errors. */
gst_soup_http_src_parse_status (msg, src);
+#ifdef GST_EXT_SOUP_MODIFICATION
+ if(src->is_ahs_streaming && msg->status_code == 404){
+ GST_WARNING_OBJECT (src,"Received 404 error while HLS streaming.");
+ src->content_len = 0;
+ }
+#endif
/* Check if Range header was respected. */
if (src->ret == GST_FLOW_CUSTOM_ERROR &&
src->read_position && msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
}
GST_DEBUG_OBJECT (src, "got body");
src->ret = GST_FLOW_UNEXPECTED;
- if (src->loop)
- g_main_loop_quit (src->loop);
- gst_soup_http_src_session_pause_message (src);
+ src->have_body = TRUE;
+
+ /* no need to interrupt the message here, we do it on the
+ * finished_cb anyway if needed. And getting the body might mean
+ * that the connection was hang up before finished. This happens when
+ * the pipeline is stalled for too long (long pauses during playback).
+ * Best to let it continue from here and pause because it reached the
+ * final bytes based on content_size or received an out of range error */
}
/* Finished. Signal EOS. */
GST_DEBUG_OBJECT (src, "finished, but not for current message");
return;
}
- GST_DEBUG_OBJECT (src, "finished");
+
+ GST_WARNING_OBJECT (src, "finished : %d", src->session_io_status);
src->ret = GST_FLOW_UNEXPECTED;
if (src->session_io_status == GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_CANCELLED) {
/* gst_soup_http_src_cancel_message() triggered this; probably a seek
* that occurred in the QUEUEING state; i.e. before the connection setup
* was complete. Do nothing */
} else if (src->session_io_status ==
- GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING && src->read_position > 0) {
+ GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING && src->read_position > 0 &&
+ (src->have_size || src->read_position < src->content_size)) {
/* The server disconnected while streaming. Reconnect and seeking to the
* last location. */
src->retry = TRUE;
src->ret = GST_FLOW_CUSTOM_ERROR;
} else if (G_UNLIKELY (src->session_io_status !=
GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING)) {
- /* FIXME: reason_phrase is not translated, add proper error message */
- GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
- ("%s", msg->reason_phrase),
- ("libsoup status code %d", msg->status_code));
+#ifdef GST_EXT_SOUP_MODIFICATION
+ if (msg->status_code != SOUP_STATUS_OK) {
+ if ((src->read_position <= 0) || (src->timeout == 0)) {
+ /* FIXME: reason_phrase is not translated, add proper error message */
+ GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
+ ("%s", msg->reason_phrase),
+ ("libsoup status code %d", msg->status_code));
+ } else {
+ GstClockTime current = 0, elapsed = 0;
+
+ GST_WARNING_OBJECT(src, "session timout:%d, timeout:%d", src->session_timeout, src->timeout);
+
+ /* Assumption : soup error will be occurred after session timeout. */
+ if (src->retry_timestamp == 0) {
+ GstClockTime retry_timestamp = 0;
+ retry_timestamp = gst_util_get_timestamp();
+ if (retry_timestamp > (src->session_timeout*GST_SECOND))
+ src->retry_timestamp = gst_util_get_timestamp() - (src->session_timeout*GST_SECOND);
+ else
+ src->retry_timestamp = 0;
+ }
+
+ current = gst_util_get_timestamp ();
+ elapsed = GST_CLOCK_DIFF (src->retry_timestamp, current);
+
+ if ((src->timeout == DEFAULT_RETRY_TIMEOUT) || (elapsed <= (src->timeout*GST_SECOND))) {
+
+ if (src->timeout != DEFAULT_RETRY_TIMEOUT) {
+ guint new_session_timeout = src->timeout - (elapsed/GST_SECOND);
+
+ if (new_session_timeout < MIN_SESSION_TIMEOUT)
+ new_session_timeout = MIN_SESSION_TIMEOUT;
+ else if (new_session_timeout > DEFAULT_SESSION_TIMEOUT)
+ new_session_timeout = DEFAULT_SESSION_TIMEOUT;
+
+ if (new_session_timeout != src->session_timeout) {
+ src->session_timeout = new_session_timeout;
+ g_object_set(src->session, SOUP_SESSION_TIMEOUT, src->session_timeout, NULL);
+ }
+ }
+
+ GST_WARNING_OBJECT(src, "retry. session timeout : %d", src->session_timeout);
+
+ src->retry = TRUE;
+ src->ret = GST_FLOW_CUSTOM_ERROR;
+ } else {
+ src->timeout = 0;
+ GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
+ ("%s", msg->reason_phrase),
+ ("libsoup status code %d", msg->status_code));
+ }
+ }
+ }
+#else
+ if (msg->status_code != SOUP_STATUS_OK){
+ /* FIXME: reason_phrase is not translated, add proper error message */
+ GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND,
+ ("%s", msg->reason_phrase),
+ ("libsoup status code %d", msg->status_code));
+ }
+#endif
}
if (src->loop)
g_main_loop_quit (src->loop);
GST_DEBUG_OBJECT (src, "got chunk, but not for current message");
return;
}
+
+ src->have_body = FALSE;
if (G_UNLIKELY (src->session_io_status !=
GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING)) {
/* Probably a redirect. */
/* Ignore redirections. */
return;
}
- GST_DEBUG_OBJECT (src, "got response %d: %s", msg->status_code,
+ GST_WARNING_OBJECT (src, "got response %d: %s", msg->status_code,
msg->reason_phrase);
if (src->session_io_status == GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING &&
src->read_position > 0) {
switch (msg->status_code) {
case SOUP_STATUS_CANT_RESOLVE:
case SOUP_STATUS_CANT_RESOLVE_PROXY:
- SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, NOT_FOUND,
- _("Could not resolve server name."));
- src->ret = GST_FLOW_ERROR;
+#ifdef GST_EXT_SOUP_MODIFICATION
+ if (src->retry != TRUE)
+#endif
+ {
+ SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, NOT_FOUND,
+ _("Could not resolve server name."));
+ src->ret = GST_FLOW_ERROR;
+ }
break;
case SOUP_STATUS_CANT_CONNECT:
case SOUP_STATUS_CANT_CONNECT_PROXY:
- SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, OPEN_READ,
- _("Could not establish connection to server."));
- src->ret = GST_FLOW_ERROR;
+#ifdef GST_EXT_SOUP_MODIFICATION
+ if (src->retry != TRUE)
+#endif
+ {
+ SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, OPEN_READ,
+ _("Could not establish connection to server."));
+ src->ret = GST_FLOW_ERROR;
+ }
break;
case SOUP_STATUS_SSL_FAILED:
SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, OPEN_READ,
src->ret = GST_FLOW_ERROR;
break;
case SOUP_STATUS_IO_ERROR:
- SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, READ,
- _("A network error occured, or the server closed the connection "
- "unexpectedly."));
- src->ret = GST_FLOW_ERROR;
+#ifdef GST_EXT_SOUP_MODIFICATION
+ if (src->retry != TRUE)
+#endif
+ {
+ SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, READ,
+ _("A network error occured, or the server closed the connection "
+ "unexpectedly."));
+ src->ret = GST_FLOW_ERROR;
+ }
break;
case SOUP_STATUS_MALFORMED:
SOUP_HTTP_SRC_ERROR (src, msg, RESOURCE, READ,
SOUP_STATUS_IS_REDIRECTION (msg->status_code) ||
SOUP_STATUS_IS_SERVER_ERROR (msg->status_code)) {
/* Report HTTP error. */
+ /* when content_size is unknown and we have just finished receiving
+ * a body message, requests that go beyond the content limits will result
+ * in an error. Here we convert those to EOS */
+ if (msg->status_code == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE &&
+ src->have_body && src->have_size) {
+ GST_WARNING_OBJECT (src, "Requested range out of limits and received full "
+ "body, returning EOS");
+ src->ret = GST_FLOW_UNEXPECTED;
+ return;
+ }
+
/* FIXME: reason_phrase is not translated and not suitable for user
* error dialog according to libsoup documentation.
* FIXME: error code (OPEN_READ vs. READ) should depend on http status? */
static gboolean
gst_soup_http_src_build_message (GstSoupHTTPSrc * src)
{
+ gchar *user_agent = DEFAULT_USER_AGENT;
+
src->msg = soup_message_new (SOUP_METHOD_GET, src->location);
if (!src->msg) {
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
soup_message_headers_append (src->msg->request_headers, "icy-metadata",
"1");
}
- if (src->cookies) {
- gchar **cookie;
+
+ if (src->user_agent) {
+ user_agent = src->user_agent;
+ GST_DEBUG_OBJECT(src, "Set User-Agent = %s", user_agent);
+ }
+
+ soup_message_headers_append (src->msg->request_headers, "User-Agent",
+ user_agent);
+
+#ifdef SEEK_CHANGES
+ soup_message_headers_append (src->msg->request_headers, "Accept-Ranges","bytes");
+#endif
+
+
#ifdef GST_EXT_SOUP_MODIFICATION
- SoupURI *uri;
- SoupCookie *cookie_parsed;
+ if (src->cookie_jar) {
+ GSList *cookie_list, *c;
+ gchar **cookies;
gchar *header;
+ SoupURI *uri = NULL;
+ SoupCookie *cookie;
uri = soup_uri_new (src->location);
- for (cookie = src->cookies; *cookie != NULL; cookie++) {
- if ((cookie_parsed = soup_cookie_parse (*cookie, uri)) != NULL) {
- header = soup_cookie_to_cookie_header (cookie_parsed);
- soup_message_headers_append (src->msg->request_headers, "Cookie",
+
+ if ((cookie_list = soup_cookie_jar_all_cookies (src->cookie_jar)) != NULL) {
+ cookies = g_new0 (gchar *, g_slist_length(cookie_list) + 1);
+ for (c = cookie_list; c; c = c->next) {
+ cookie = (SoupCookie *)c->data;
+ if (soup_cookie_applies_to_uri(cookie, uri)) {
+ header = soup_cookie_to_cookie_header (cookie);
+ soup_message_headers_append (src->msg->request_headers, "Cookie",
header);
- g_free (header);
- soup_cookie_free (cookie_parsed);
+ g_free (header);
+ }
}
}
- soup_uri_free (uri);
+ soup_cookies_free (cookie_list);
+ }
#else
+ gchar **cookie;
+ if (src->cookies) {
for (cookie = src->cookies; *cookie != NULL; cookie++) {
soup_message_headers_append (src->msg->request_headers, "Cookie",
- *cookie);
+ *cookie);
}
-#endif
}
+#endif
+
soup_message_headers_append (src->msg->request_headers,
"transferMode.dlna.org", "Streaming");
src->retry = FALSE;
(src->automatic_redirect ? 0 : SOUP_MESSAGE_NO_REDIRECT));
soup_message_set_chunk_allocator (src->msg,
gst_soup_http_src_chunk_allocator, src, NULL);
+
+#ifdef USE_SAMSUNG_LINK
+ if(src->is_x_asp && src->seek_time_position >= 0)
+ {
+ gchar seek_time[64];
+ gint rc = 0;
+ rc = g_snprintf(seek_time, 64, "%"G_GINT64_FORMAT, src->seek_time_position);
+ if (rc > 0)
+ {
+ soup_message_headers_append (src->msg->request_headers,
+ "X-ASP-SEEK-TIME", seek_time);
+ }
+ src->seek_time_position = (gint64)-1;
+ }
+#endif
+
#ifdef SEEK_CHANGES
//gst_soup_http_src_add_range_header (src, src->request_position);
if(src->range_size > 0)
soup_message_headers_set_range(src->msg->request_headers, src->request_position, (src->request_position+src->range_size-1));
else {
- gst_soup_http_src_add_range_header (src, src->request_position);
+#ifdef GST_EXT_SOUP_MODIFICATION
+ gchar *dash_mediaRange = NULL;
+ dash_mediaRange = strstr (src->location, "range=");
+ if (dash_mediaRange)
+ gst_soup_http_src_add_range_header (src, src->request_position,dash_mediaRange);
+ else
+ gst_soup_http_src_add_range_header (src, src->request_position,NULL);
+#else
+ gst_soup_http_src_add_range_header (src, src->request_position);
+#endif
gst_soup_http_src_add_extra_headers (src);
GST_DEBUG_OBJECT (src, "request headers:");
soup_message_headers_foreach (src->msg->request_headers,gst_soup_http_src_headers_foreach, src);
}
#else
+
+#ifdef GST_EXT_SOUP_MODIFICATION
+ gst_soup_http_src_add_range_header (src, src->request_position,NULL);
+#else
gst_soup_http_src_add_range_header (src, src->request_position);
+#endif
gst_soup_http_src_add_extra_headers (src);
return GST_FLOW_UNEXPECTED;
} else if (src->session_io_status ==
GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_IDLE) {
+ #ifdef GST_EXT_SOUP_MODIFICATION
+ gst_soup_http_src_add_range_header (src, src->request_position,NULL);
+ #else
gst_soup_http_src_add_range_header (src, src->request_position);
+ #endif
} else {
GST_DEBUG_OBJECT (src, "Seek from position %" G_GUINT64_FORMAT
" to %" G_GUINT64_FORMAT ": requeueing connection request",
break;
}
if (src->retry) {
- GST_DEBUG_OBJECT (src, "Reconnecting");
+ GST_WARNING_OBJECT (src, "Reconnecting");
+#ifdef GST_EXT_SOUP_MODIFICATION
+ g_usleep (1000000);
+#endif
if (!gst_soup_http_src_build_message (src))
return GST_FLOW_ERROR;
src->retry = FALSE;
gst_soup_http_src_start (GstBaseSrc * bsrc)
{
GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc);
+#ifdef GST_EXT_SOUP_MODIFICATION
+ char **array = NULL;
+ SoupURI *base_uri;
+#endif
GST_DEBUG_OBJECT (src, "start(\"%s\")", src->location);
+#ifdef GST_EXT_SOUP_MODIFICATION
+/* For videohub DRM dash uri which has custom data */
+ gchar *has_custom_data = strstr(src->location,"[]<>");
+ gchar *has_mpd= strstr(src->location,".mpd");
+
+ if (src->videohub_dash_uri){
+ g_free(src->videohub_dash_uri);
+ src->videohub_dash_uri = NULL;
+ }
+
+ if ( has_custom_data !=NULL && has_mpd !=NULL ) {
+ GST_DEBUG_OBJECT(src,"The uri has custom data..");
+
+ /* keep uri which includes custom data for forwarding it to qtdemux */
+ src->videohub_dash_uri = g_strdup(src->location);
+
+ /* make normal uri without custom data for requesting to server */
+ gchar *videohub_dash_normal_uri = NULL;
+ videohub_dash_normal_uri = strtok(src->location,"[]<>");
+ src->location = NULL;
+ src->location = videohub_dash_normal_uri;
+ GST_DEBUG_OBJECT (src, "Videohub normal uri(\"%s\")", src->location);
+ }
+#endif
+
if (!src->location) {
GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (_("No URL set.")),
("Missing location property"));
return FALSE;
}
+#ifdef GST_EXT_SOUP_MODIFICATION
+ if (src->timeout == 0)
+ src->session_timeout = 0;
+ else if ((src->timeout > 0) && (src->timeout < DEFAULT_SESSION_TIMEOUT))
+ src->session_timeout = src->timeout;
+
+ GST_DEBUG_OBJECT (src, "session timeout: %d", src->session_timeout);
+
+ src->duration = GST_CLOCK_TIME_NONE;
+#endif
+
if (src->proxy == NULL) {
src->session =
soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT,
src->context, SOUP_SESSION_USER_AGENT, src->user_agent,
+#ifdef GST_EXT_SOUP_MODIFICATION
+ SOUP_SESSION_TIMEOUT, src->session_timeout,
+#else
SOUP_SESSION_TIMEOUT, src->timeout,
+#endif
#ifdef HAVE_LIBSOUP_GNOME
SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_GNOME,
#endif
src->session =
soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT,
src->context, SOUP_SESSION_PROXY_URI, src->proxy,
+#ifdef GST_EXT_SOUP_MODIFICATION
+ SOUP_SESSION_TIMEOUT, src->session_timeout,
+#else
SOUP_SESSION_TIMEOUT, src->timeout,
+#endif
SOUP_SESSION_USER_AGENT, src->user_agent, NULL);
}
}
#ifdef GST_EXT_SOUP_MODIFICATION
+ SoupCookie *soup_cookie = NULL;
+ GSList *c_list = NULL;
+
soup_session_add_feature_by_type (src->session, SOUP_TYPE_COOKIE_JAR);
src->cookie_jar = SOUP_COOKIE_JAR (soup_session_get_feature (src->session, SOUP_TYPE_COOKIE_JAR));
+ if ((array = src->cookies) != NULL) {
+ base_uri = soup_uri_new (src->location);
+ while (*array != NULL) {
+ soup_cookie = soup_cookie_parse (*array++, base_uri);
+ if (soup_cookie != NULL) {
+ GST_INFO_OBJECT (src, "adding cookies..");
+ soup_cookie_jar_add_cookie (src->cookie_jar, soup_cookie);
+ }
+ }
+ soup_uri_free (base_uri);
+ }
+
+ /* check if temporal cookie store exist. add them to jar */
+ if ( src->cookie_list ) {
+ for ( c_list = src->cookie_list; c_list; c_list = c_list->next) {
+ soup_cookie = (SoupCookie *)c_list->data;
+ if ( soup_cookie ) {
+ soup_cookie_jar_add_cookie (src->cookie_jar, soup_cookie);
+ c_list->data = NULL;
+ GST_INFO_OBJECT (src, "adding cookies..");
+ }
+ }
+ /* freeing list since all cookies are stolen by jar */
+ g_slist_free(src->cookie_list);
+ src->cookie_list = NULL;
+ }
+
#endif
g_signal_connect (src->session, "authenticate",
#endif
if (src->read_position == segment->start) {
GST_DEBUG_OBJECT (src, "Seeking to current read position");
+
+#ifdef SEEK_CHANGES
+ if (src->request_position >= src->file_size)
+ src->request_position = segment->start;
+#endif
return TRUE;
}
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_URI:
+#ifdef GST_EXT_SOUP_MODIFICATION
+ if(src->videohub_dash_uri)
+ gst_query_set_uri (query, src->videohub_dash_uri);
+ else
+#endif
gst_query_set_uri (query, src->location);
ret = TRUE;
break;
+#ifdef GST_EXT_SOUP_MODIFICATION
+ case GST_QUERY_CUSTOM: {
+ GstStructure *s;
+ s = gst_query_get_structure (query);
+ if (gst_structure_has_name (s, "HTTPCookies")) {
+ GSList *cookie_list, *c;
+ gchar **cookies, **array;
+ GValue value = { 0, { { 0 } } };
+
+ g_value_init (&value, G_TYPE_STRV);
+ cookies = NULL;
+ if ((cookie_list = soup_cookie_jar_all_cookies (src->cookie_jar)) != NULL) {
+ cookies = g_new0 (gchar *, g_slist_length(cookie_list) + 1);
+ array = cookies;
+ for (c = cookie_list; c; c = c->next) {
+ *array++ = soup_cookie_to_set_cookie_header ((SoupCookie *)(c->data));
+ }
+ soup_cookies_free (cookie_list);
+ }
+ g_value_set_boxed (&value, cookies);
+
+ GST_INFO_OBJECT (src, "Received supported custom query");
+ gst_structure_set_value (s, "cookies", &value);
+ ret = TRUE;
+ }
+#ifdef USE_SAMSUNG_LINK
+ else if (gst_structure_has_name (s, "X-ASP")){
+ GST_INFO_OBJECT (src, "Received X-ASP custom query, X-ASP is %d", src->is_x_asp);
+ GValue value = { 0, };
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, src->is_x_asp);
+ gst_structure_set_value (s, "x-asp", &value);
+ ret = TRUE;
+ }
+#endif
+ else if (gst_structure_has_name(s, "HTTPUserAgent")) {
+ GValue value = { 0, };
+ g_value_init (&value, G_TYPE_STRING);
+ g_value_set_string (&value, src->user_agent);
+ GST_INFO_OBJECT (src, "Received HTTPUserAgent custom query");
+ gst_structure_set_value (s, "user-agent", &value);
+ ret = TRUE;
+ }
+ else {
+ GST_WARNING_OBJECT (src,"Unsupported query");
+ ret = FALSE;
+ }
+ }
+ break;
+ case GST_QUERY_DURATION: {
+
+ GstFormat format;
+
+ gst_query_parse_duration (query, &format, NULL);
+
+ if (format == GST_FORMAT_TIME) {
+ if (src->duration != GST_CLOCK_TIME_NONE) {
+ gst_query_set_duration (query, GST_FORMAT_TIME, src->duration);
+ GST_DEBUG_OBJECT (src,"query duration time is %" GST_TIME_FORMAT,
+ GST_TIME_ARGS(src->duration));
+ ret = TRUE;
+ } else {
+ GST_WARNING_OBJECT (src,"Unsupported query");
+ ret = FALSE;
+ }
+
+ } else if (format == GST_FORMAT_BYTES) {
+ gst_query_set_duration (query, GST_FORMAT_BYTES, src->file_size);
+ GST_DEBUG_OBJECT (src,"query duration bytes is %" G_GUINT64_FORMAT, src->file_size);
+ ret = TRUE;
+ } else {
+ GST_WARNING_OBJECT (src,"Unsupported query");
+ ret = FALSE;
+ }
+ }
+ break;
+#endif
+
default:
ret = FALSE;
break;
return ret;
}
+#ifdef USE_SAMSUNG_LINK
+static gboolean
+gst_soup_http_src_event (GstBaseSrc * bsrc, GstEvent *event)
+{
+ GstSoupHTTPSrc *src = GST_SOUP_HTTP_SRC (bsrc);
+ gboolean ret;
+
+ GST_DEBUG_OBJECT (src, "handling %s event", GST_EVENT_TYPE_NAME (event));
+
+ switch (GST_EVENT_TYPE (event)){
+ case GST_EVENT_SEEK:
+ if (src->is_x_asp) {
+ gdouble rate;
+ GstFormat format;
+ GstSeekFlags flags;
+ GstSeekType start_type, stop_type;
+ gint64 start, stop;
+
+ gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
+ &stop_type, &stop);
+ if (format == GST_FORMAT_TIME) {
+ src->seek_time_position = start / G_GINT64_CONSTANT (1000000);
+ GST_DEBUG_OBJECT (src,"seek position time is %" G_GINT64_FORMAT"(ms)", src->seek_time_position);
+ }
+ }
+ ret = GST_BASE_SRC_CLASS (parent_class)->event (bsrc, event);
+ break;
+ default:
+ ret = GST_BASE_SRC_CLASS (parent_class)->event (bsrc, event);
+ break;
+ }
+
+ return ret;
+}
+#endif
+
+
static gboolean
gst_soup_http_src_set_location (GstSoupHTTPSrc * src, const gchar * uri)
{
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more
+ * Library General Public License for more
*/
#ifndef __GST_SOUP_HTTP_SRC_H__
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SOUP_HTTP_SRC))
#define GST_IS_SOUP_HTTP_SRC_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SOUP_HTTP_SRC))
+#define USE_SAMSUNG_LINK
typedef struct _GstSoupHTTPSrc GstSoupHTTPSrc;
typedef struct _GstSoupHTTPSrcClass GstSoupHTTPSrcClass;
+#ifdef GST_EXT_SOUP_MODIFICATION
+typedef struct _GstSoupHTTPSrcRequestRange GstSoupHTTPSrcRequestRange;
+#endif
typedef enum {
GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_IDLE,
gboolean interrupted; /* Signal unlock(). */
gboolean retry; /* Should attempt to reconnect. */
+#ifdef GST_EXT_SOUP_MODIFICATION
+ guint op_code; /* DLNA server seek setting */
+#endif
+
gboolean have_size; /* Received and parsed Content-Length
header. */
guint64 file_size;
Range. */
guint64 request_position; /* Seek to this position. */
gboolean seeked;
+ gboolean have_body; /* Indicates if it has just been signaled the
+ * end of the message body. This is used to
+ * decide if an out of range request should be
+ * handled as an error or EOS when the content
+ * size is unknown */
/* Shoutcast/icecast metadata extraction handling. */
gboolean iradio_mode;
GstStructure *extra_headers;
- guint timeout;
#ifdef GST_EXT_SOUP_MODIFICATION
SoupCookieJar *cookie_jar;
+ guint64 content_len;
+ gboolean is_ahs_streaming; /* adaptive HTTP Streaming : HLS/SS/DASH */
+ gchar *videohub_dash_uri;/* includes custom data */
+ GSList *cookie_list;
+
+ gint timeout;
+ guint session_timeout;
+ GstClockTime retry_timestamp;
+
+ GstClockTime duration;
+#else
+ guint timeout;
#endif
+
+#ifdef USE_SAMSUNG_LINK
+ gint64 seek_time_position;
+ gboolean is_x_asp;
+#endif
+
};
struct _GstSoupHTTPSrcClass {
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_" />
+ </request>
+</manifest>
@USE_ESD_TRUE@Provides: gstreamer-audiosink
@USE_FLAC_TRUE@BuildRequires: flac-devel >= 1.0.3
@USE_GCONF_TRUE@BuildRequires: GConf2-devel
-@USE_JPEG_TRUE@BuildRequires: libjpeg-devel
+@USE_JPEG_TRUE@BuildRequires: libjpeg-turbo-devel
@USE_LIBCACA_TRUE@BuildRequires: libcaca-devel
@USE_LIBDV_TRUE@BuildRequires: libdv-devel
@USE_LIBPNG_TRUE@BuildRequires: libpng-devel >= 1.2.0
#define ADIF_MAX_SIZE 40 /* Should be enough */
#define ADTS_MAX_SIZE 10 /* Should be enough */
+#ifdef GST_EXT_AACPARSER_MODIFICATION /* to get more accurate duration */
+#define AAC_MAX_ESTIMATE_DURATION_BUF (1024 * 1024) /* use first 1 Mbyte */
+#define AAC_SAMPLE_PER_FRAME 1024
+#endif
#define AAC_FRAME_DURATION(parse) (GST_SECOND/parse->frames_per_sec)
static GstFlowReturn gst_aac_parse_parse_frame (GstBaseParse * parse,
GstBaseParseFrame * frame);
+#ifdef GST_EXT_AACPARSER_MODIFICATION /* make full aac(adts) index table when seek */
+ static guint gst_aac_parse_adts_get_fast_frame_len (const guint8 * data);
+ static gboolean gst_aac_parse_src_eventfunc(GstBaseParse * parse, GstEvent * event); // evil
+ static gboolean gst_aac_parse_adts_src_eventfunc (GstBaseParse * parse, GstEvent * event);
+ static gboolean gst_aac_parse_adif_src_eventfunc (GstBaseParse * parse, GstEvent * event); // evil
+ #define AAC_MAX_PULL_RANGE_BUF (1 * 1024 * 1024) /* 1 MByte */
+ #define AAC_LARGE_FILE_SIZE (2 * 1024 * 1024) /* 2 MByte */
+#endif
+
#define _do_init(bla) \
GST_DEBUG_CATEGORY_INIT (aacparse_debug, "aacparse", 0, \
"AAC audio stream parser");
parse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_aac_parse_parse_frame);
parse_class->check_valid_frame =
GST_DEBUG_FUNCPTR (gst_aac_parse_check_valid_frame);
+
+#ifdef GST_EXT_AACPARSER_MODIFICATION /* make full aac(adts) index table when seek */
+ parse_class->src_event = GST_DEBUG_FUNCPTR (gst_aac_parse_src_eventfunc);
+#endif
}
gst_aac_parse_init (GstAacParse * aacparse, GstAacParseClass * klass)
{
GST_DEBUG ("initialized");
+#ifdef GST_EXT_AACPARSER_MODIFICATION /* to get more correct duration */
+ aacparse->first_frame = TRUE;
+#endif
}
if ((data[*framesize] == 0xff) && ((data[*framesize + 1] & 0xf6) == 0xf0)) {
guint nextlen = gst_aac_parse_adts_get_frame_len (data + (*framesize));
+ aacparse->frame_byte = ((*framesize) + nextlen) / 2;
GST_LOG ("ADTS frame found, len: %d bytes", *framesize);
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (aacparse),
nextlen + ADTS_MAX_SIZE);
if (object)
*object = (data[2] & 0xc0) >> 6;
}
+static inline guint aac_get_bits(guint8* data, guint* bit_pos, guint num)
+{
+ guint ret_val;
+ guint byte_pos = (*bit_pos) >> 3;
+ guint cur_bit = (*bit_pos) & 0x7;
+ guint bit_buffer = ((guint)(data[byte_pos]) << 8) | data[byte_pos + 1];
+ guint r_shift_val = 16 + cur_bit;
+ guint l_shift_val = 32 - num;
+
+ (*bit_pos) = (*bit_pos) + num;
+ ret_val = ((bit_buffer << r_shift_val) >> l_shift_val);
+
+ GST_INFO("curr byte %i, bit %i :", byte_pos, cur_bit);
+ GST_INFO("getbits num %i, ret %i\n", num, ret_val);
+ return ret_val;
+}
+
+static inline void aac_skip_bits(guint* bit_pos, guint num)
+{
+ guint byte_pos = (*bit_pos) >> 3;
+ guint cur_bit = (*bit_pos) & 0x7;
+
+ GST_INFO("curr byte %i, bit %i :", byte_pos, cur_bit);
+ GST_INFO("skipbits num %i\n", num);
+ (*bit_pos) = (*bit_pos) + num;
+}
+
+static inline void aac_byte_alignment(guint* bit_pos)
+{
+ guint byte_pos = (*bit_pos) >> 3;
+ guint cur_bit = (*bit_pos) & 0x7;
+
+ GST_INFO("curr byte %i, bit %i :", byte_pos, cur_bit);
+ GST_INFO("byte_alignment\n");
+ (*bit_pos) = (*bit_pos) + 7;
+ (*bit_pos) = (*bit_pos) & 0xFFffFFf8;
+}
+
+static guint skip_aac_read_adif_header(guint8* data)
+{
+ guint val, num_pce;
+ guint bit_pos = 0;
+ guint read_bytes;
+
+ /* adif_id; 32 bslbf */
+ aac_skip_bits(&bit_pos, 32);
+
+ /* copyright_id_present; 1 bslbf */
+ val = aac_get_bits(data, &bit_pos, 1);
+ if(val)
+ {
+ /* copyright_id; 72 bslbf */
+ aac_skip_bits(&bit_pos, 72);
+ }
+
+ /* original_copy; 1 bslbf */
+ aac_skip_bits(&bit_pos, 1);
+ /* home; 1 bslbf */
+ aac_skip_bits(&bit_pos, 1);
+
+ /* bitstream_type; 1 bslbf */
+ val = aac_get_bits(data, &bit_pos, 1);
+
+ /* bitrate; 23 uimsbf */
+ aac_get_bits(data, &bit_pos, 23);
+
+ /* num_program_config_elements; 4 bslbf */
+ num_pce = aac_get_bits(data, &bit_pos, 4) + 1;
+
+ if(val == 0)
+ {
+ /* adif_buffer_fullness; 20 uimsbf */
+ aac_skip_bits(&bit_pos, 20);
+ }
+
+ for(; num_pce; num_pce--)
+ {
+ gint sr_index;
+ guint num_fce, num_sce, num_bce, num_lce, num_ade, num_vce, i;
+
+ /* element_instance_tag; 4 uimsbf */
+ aac_skip_bits(&bit_pos, 4);
+
+ /* profile; 2 uimsbf */
+ aac_skip_bits(&bit_pos, 2);
+
+ /* sampling_frequency_index; 4 uimsbf */
+ sr_index = aac_get_bits(data, &bit_pos, 4);
+
+ /* num_front_channel_elements; 4 uimsbf */
+ num_fce = aac_get_bits(data, &bit_pos, 4);
+
+ /* num_side_channel_elements; 4 uimsbf */
+ num_sce = aac_get_bits(data, &bit_pos, 4);
+
+ /* num_back_channel_elements; 4 uimsbf */
+ num_bce = aac_get_bits(data, &bit_pos, 4);
+
+ /* num_lfe_channel_elements; 2 uimsbf */
+ num_lce = aac_get_bits(data, &bit_pos, 2);
+
+ /* num_assoc_data_elements; 3 uimsbf */
+ num_ade = aac_get_bits(data, &bit_pos, 3);
+
+ /* num_valid_cc_elements; 4 uimsbf */
+ num_vce = aac_get_bits(data, &bit_pos, 4);
+
+ /* mono_mixdown_present; 1 uimsbf */
+ val = aac_get_bits(data, &bit_pos, 1);
+ if(val == 1)
+ {
+ /* mono_mixdown_element_number; 4 uimsbf */
+ aac_skip_bits(&bit_pos, 4);
+ }
+
+ /* stereo_mixdown_present; 1 uimsbf */
+ val = aac_get_bits(data, &bit_pos, 1);
+ if(val == 1)
+ {
+ /* stereo_mixdown_element_number; 4 uimsbf */
+ aac_skip_bits(&bit_pos, 4);
+ }
+
+ /* matrix_mixdown_idx_present; 1 uimsbf */
+ val = aac_get_bits(data, &bit_pos, 1);
+ if(val == 1)
+ {
+ /* matrix_mixdown_idx ; 2 uimsbf */
+ aac_skip_bits(&bit_pos, 2);
+
+ /* pseudo_surround_enable; 1 uimsbf */
+ aac_skip_bits(&bit_pos, 1);
+ }
+
+ for(i = 0; i < num_fce; i++)
+ {
+ /* front_element_is_cpe[i]; 1 bslbf */
+ aac_skip_bits(&bit_pos, 1);
+
+ /* front_element_tag_select[i]; 4 uimsbf */
+ aac_skip_bits(&bit_pos, 4);
+ }
+
+ for(i = 0; i < num_sce; i++)
+ {
+ /* side_element_is_cpe[i]; 1 bslbf */
+ aac_skip_bits(&bit_pos, 1);
+
+ /* side_element_tag_select[i]; 4 uimsbf */
+ aac_skip_bits(&bit_pos, 4);
+ }
+
+ for(i = 0; i < num_bce; i++)
+ {
+ /* back_element_is_cpe[i]; 1 bslbf */
+ aac_skip_bits(&bit_pos, 1);
+
+ /* back_element_tag_select[i]; 4 uimsbf */
+ aac_skip_bits(&bit_pos, 4);
+ }
+
+ for(i = 0; i < num_lce; i++)
+ {
+ /* lfe_element_tag_select[i]; 4 uimsbf */
+ aac_skip_bits(&bit_pos, 4);
+ }
+
+ for(i = 0; i < num_ade; i++)
+ {
+ /* assoc_data_element_tag_select[i]; 4 uimsbf */
+ aac_skip_bits(&bit_pos, 4);
+ }
+
+ for(i = 0; i < num_vce; i++)
+ {
+ /* cc_element_is_ind_sw[i]; 1 uimsbf */
+ aac_skip_bits(&bit_pos, 1);
+
+ /* valid_cc_element_tag_select[i]; 4 uimsbf */
+ aac_skip_bits(&bit_pos, 4);
+ }
+
+ aac_byte_alignment(&bit_pos);
+
+ /* comment_field_bytes; 8 uimsbf */
+ val = aac_get_bits(data, &bit_pos, 8);
+ for(i = 0; i < val; i++)
+ {
+ /* comment_field_data[i]; 8 uimsbf */
+ aac_skip_bits(&bit_pos, 8);
+ }
+ }
+
+ /* aac_byte_alignment(&bit_pos); */
+ read_bytes = bit_pos >> 3;
+
+ GST_DEBUG("bytes_read %i", read_bytes);
+ return read_bytes;
+}
/**
* gst_aac_parse_detect_stream:
* @aacparse: #GstAacParse.
return FALSE;
if (memcmp (data + i, "ADIF", 4) == 0) {
- const guint8 *adif;
+ const guint8 *adif, *tmp_data;
int skip_size = 0;
int bitstream_type;
int sr_idx;
+ GstBuffer* buffer;
aacparse->header_type = DSPAAC_HEADER_ADIF;
aacparse->mpegversion = 4;
if (adif[0] & 0x80)
skip_size += 9; /* skip 9 bytes */
- bitstream_type = adif[0 + skip_size] & 0x10;
+ aacparse->bitstream_type = bitstream_type = adif[0 + skip_size] & 0x10; // Added bitstream_type to the struct.
aacparse->bitrate =
((unsigned int) (adif[0 + skip_size] & 0x0f) << 19) |
((unsigned int) adif[1 + skip_size] << 11) |
gst_aac_parse_set_src_caps (aacparse,
GST_PAD_CAPS (GST_BASE_PARSE_SINK_PAD (aacparse)));
- /* not syncable, not easily seekable (unless we push data from start */
- gst_base_parse_set_syncable (GST_BASE_PARSE_CAST (aacparse), FALSE);
- gst_base_parse_set_passthrough (GST_BASE_PARSE_CAST (aacparse), TRUE);
- gst_base_parse_set_average_bitrate (GST_BASE_PARSE_CAST (aacparse), 0);
+ gst_base_parse_set_syncable (GST_BASE_PARSE_CAST (aacparse), TRUE);
+ gst_base_parse_set_passthrough (GST_BASE_PARSE_CAST (aacparse), FALSE);
+ gst_base_parse_set_average_bitrate (GST_BASE_PARSE_CAST (aacparse), aacparse->bitrate);
- *framesize = avail;
+ aacparse->read_bytes = skip_aac_read_adif_header(data);
+
+ tmp_data = data + aacparse->read_bytes;
+
+ *framesize = ((int)tmp_data [2] << 8) + tmp_data [3] + 4;
return TRUE;
}
if (aacparse->header_type == DSPAAC_HEADER_ADIF ||
aacparse->header_type == DSPAAC_HEADER_NONE) {
- /* There is nothing to parse */
- *framesize = GST_BUFFER_SIZE (buffer);
+ *framesize = ((int)data [2] << 8) + data [3] + 4;
ret = TRUE;
} else if (aacparse->header_type == DSPAAC_HEADER_NOT_PARSED || lost_sync) {
}
+#ifdef GST_EXT_AACPARSER_MODIFICATION /* to get more correct duration */
+/**
+ * get_aac_parse_get_adts_framelength:
+ * @data: #GstBufferData.
+ * @offset: #GstBufferData offset
+ *
+ * Implementation to get adts framelength by using first some frame.
+ *
+ * Returns: frame size
+ */
+int get_aac_parse_get_adts_frame_length (const unsigned char* data, gint64 offset)
+{
+ const gint adts_header_length_no_crc = 7;
+ const gint adts_header_length_with_crc = 9;
+ gint frame_size = 0;
+ gint protection_absent;
+ gint head_size;
+
+ /* check of syncword */
+ if ((data[offset+0] != 0xff) || ((data[offset+1] & 0xf6) != 0xf0)) {
+ GST_ERROR("check sync word is fail\n");
+ return -1;
+ }
+
+ /* check of protection absent */
+ protection_absent = (data[offset+1] & 0x01);
+
+ /*check of frame length */
+ frame_size = (data[offset+3] & 0x3) << 11 | data[offset+4] << 3 | data[offset+5] >> 5;
+
+ /* check of header size */
+ /* protectionAbsent is 0 if there is CRC */
+ head_size = protection_absent ? adts_header_length_no_crc : adts_header_length_with_crc;
+ if (head_size > frame_size) {
+ GST_ERROR("return frame length as 0 (frameSize %u < headSize %u)", frame_size, head_size);
+ return 0;
+ }
+
+ return frame_size;
+}
+
+/**
+ * gst_aac_parse_estimate_duration:
+ * @parse: #GstBaseParse.
+ *
+ * Implementation to get estimated total duration by using first some frame.
+ *
+ * Returns: TRUE if we can get estimated total duraion
+ */
+static gboolean
+gst_aac_parse_estimate_duration (GstBaseParse * parse)
+{
+ GstFlowReturn res = GST_FLOW_OK;
+ gint64 pull_size = 0, file_size = 0, offset = 0, num_frames=0, duration=0;
+ guint profile = 0, sample_rate_index = 0, sample_rate = 0, channel = 0;
+ guint frame_size = 0, frame_duration_us = 0, estimated_bitrate = 0;
+ guint lost_sync_count=0;
+ GstClockTime estimated_duration = GST_CLOCK_TIME_NONE;
+ GstBuffer *buffer = NULL;
+ guint8 *buf = NULL;
+ gint i = 0;
+ GstActivateMode pad_mode = GST_ACTIVATE_NONE;
+ GstAacParse *aacparse;
+
+ aacparse = GST_AAC_PARSE (parse);
+ GST_LOG_OBJECT (aacparse, "gst_aac_parse_estimate_duration enter");
+
+#ifdef GST_EXT_BASEPARSER_MODIFICATION /* check baseparse define these fuction */
+ gst_base_parse_get_pad_mode(parse, &pad_mode);
+ if (pad_mode != GST_ACTIVATE_PULL) {
+ GST_INFO_OBJECT (aacparse, "aac parser is not pull mode. can not estimate duration");
+ return FALSE;
+ }
+
+ gst_base_parse_get_upstream_size (parse, &file_size);
+#else
+ GST_WARNING_OBJECT (aacparse, "baseparser does not define get private param functions");
+ return FALSE;
+#endif
+
+ if (file_size < ADIF_MAX_SIZE) {
+ GST_ERROR_OBJECT (aacparse, "file size is too short");
+ return FALSE;
+ }
+
+ pull_size = MIN(file_size, AAC_MAX_ESTIMATE_DURATION_BUF);
+
+ res = gst_pad_pull_range (parse->sinkpad, 0, pull_size, &buffer);
+ if (res != GST_FLOW_OK) {
+ GST_ERROR_OBJECT (aacparse, "gst_pad_pull_range failed!");
+ return FALSE;
+ }
+
+ buf = GST_BUFFER_DATA(buffer);
+
+ for (i = 0; i < pull_size; i ++) {
+ if ((buf[i] == 0xff) && ((buf[i+1] & 0xf6) == 0xf0)) { /* aac sync word */
+ profile = (buf[i+2] >> 6) & 0x3;
+ sample_rate_index = (buf[i+2] >> 2) & 0xf;
+ sample_rate = gst_aac_parse_get_sample_rate_from_index(sample_rate_index);
+ if (sample_rate == 0) {
+ GST_WARNING_OBJECT (aacparse, "Invalid sample rate index (0)");
+ return FALSE;
+ }
+ channel = (buf[i+2] & 0x1) << 2 | (buf[i+3] >> 6);
+
+ GST_INFO_OBJECT (aacparse, "found sync. aac sample_rate=%d, channel=%d", sample_rate, channel);
+
+ /* count number of frames */
+ while (offset < pull_size) {
+ frame_size = get_aac_parse_get_adts_frame_length(buf, i + offset);
+ if (frame_size == 0) {
+ GST_ERROR_OBJECT (aacparse, "framesize error at offset %"G_GINT64_FORMAT, offset);
+ break;
+ } else if (frame_size == -1) {
+ offset++;
+ lost_sync_count++; // lost sync count limmitation 2K Bytes
+ if (lost_sync_count > (1024*2))
+ return FALSE;
+ } else {
+ offset += frame_size;
+ num_frames++;
+ lost_sync_count=0;
+ }
+ } /* while */
+
+ /* if we can got full file, we can calculate the accurate duration */
+ if (pull_size == file_size) {
+ gfloat duration_for_one_frame = 0;
+ GstClockTime calculated_duration = GST_CLOCK_TIME_NONE;
+
+ GST_INFO_OBJECT (aacparse, "we got total file (%d bytes). do not estimate but make Accurate total duration.", pull_size);
+
+ duration_for_one_frame = (gfloat)AAC_SAMPLE_PER_FRAME / (gfloat)sample_rate;
+ calculated_duration = num_frames * duration_for_one_frame * 1000 * 1000 * 1000;
+
+ GST_INFO_OBJECT (aacparse, "duration_for_one_frame %f ms", duration_for_one_frame);
+ GST_INFO_OBJECT (aacparse, "calculated duration = %"GST_TIME_FORMAT, GST_TIME_ARGS(calculated_duration));
+ gst_base_parse_set_duration (parse, GST_FORMAT_TIME, calculated_duration, 0); /* 0 means disable estimate */
+
+ } else {
+ GST_INFO_OBJECT (aacparse, "we got %d bytes in total file (%"G_GINT64_FORMAT
+ "). can not make accurate duration but Estimate.", pull_size, file_size);
+ frame_duration_us = (1024 * 1000000ll + (sample_rate - 1)) / sample_rate;
+ duration = num_frames * frame_duration_us;
+
+ estimated_bitrate = (gint)((gfloat)(offset * 8) / (gfloat)(duration / 1000));
+ estimated_duration = (GstClockTime)((file_size * 8) / (estimated_bitrate * 1000)) * GST_SECOND;
+
+ GST_INFO_OBJECT (aacparse, "number of frame = %"G_GINT64_FORMAT, num_frames);
+ GST_INFO_OBJECT (aacparse, "duration = %"G_GINT64_FORMAT, duration / 1000000);
+ GST_INFO_OBJECT (aacparse, "byte = %"G_GINT64_FORMAT, offset);
+ GST_INFO_OBJECT (aacparse, "estimated bitrate = %d bps", estimated_bitrate);
+ GST_INFO_OBJECT (aacparse, "estimated duration = %"GST_TIME_FORMAT, GST_TIME_ARGS(estimated_duration));
+
+ gst_base_parse_set_average_bitrate (parse, estimated_bitrate * 1000);
+ /* set update_interval as duration(sec)/2 */
+ gst_base_parse_set_duration (parse, GST_FORMAT_TIME, estimated_duration, (gint)(duration/2));
+ }
+
+ break;
+ }
+ }
+
+ gst_buffer_unref (buffer);
+ return TRUE;
+}
+#endif
+
+
/**
* gst_aac_parse_parse_frame:
* @parse: #GstBaseParse.
aacparse = GST_AAC_PARSE (parse);
buffer = frame->buffer;
- if (G_UNLIKELY (aacparse->header_type != DSPAAC_HEADER_ADTS))
- return ret;
-
- /* see above */
- frame->overhead = 7;
+ if(aacparse->header_type == DSPAAC_HEADER_ADTS)
+ {
+ /* see above */
+ frame->overhead = 7;
gst_aac_parse_parse_adts_header (aacparse, GST_BUFFER_DATA (buffer),
&rate, &channels, NULL, NULL);
aacparse->sample_rate, aacparse->frame_samples, 2, 2);
}
+#ifdef GST_EXT_AACPARSER_MODIFICATION /* to get more correct duration */
+ if (aacparse->first_frame == TRUE) {
+ gboolean ret = FALSE;
+ aacparse->first_frame = FALSE;
+
+ ret = gst_aac_parse_estimate_duration(parse);
+ if (!ret) {
+ GST_WARNING_OBJECT (aacparse, "can not estimate total duration");
+ ret = GST_FLOW_NOT_SUPPORTED;
+ }
+ }
+#endif
+ }
+ else if(aacparse->header_type == DSPAAC_HEADER_ADIF /* DSPAAC_HEADER_NOT_PARSED */)
+ {
+ int file_size = 0;
+ float estimated_duration = 0;
+ GstBuffer *buffer = NULL;
+ gint64 total_file_size;
+ const guint8 *data;
+ data = GST_BUFFER_DATA (buffer);
+ if (G_UNLIKELY (rate != aacparse->sample_rate || channels != aacparse->channels)) {
+ GST_DEBUG("ADIF: Sampling Rate = %d", aacparse->sample_rate);
+ if (!gst_aac_parse_set_src_caps (aacparse, GST_PAD_CAPS (GST_BASE_PARSE (aacparse)->sinkpad))) {
+ /* If linking fails, we need to return appropriate error */
+ ret = GST_FLOW_NOT_LINKED;
+ }
+ }
+ gst_base_parse_get_upstream_size(parse, &total_file_size);
+
+ estimated_duration = ((total_file_size * 8) / (float)(aacparse->bitrate * 1000))* GST_SECOND;
+ aacparse->file_size = total_file_size;
+
+ gst_base_parse_set_average_bitrate (parse, aacparse->bitrate);
+ gst_base_parse_set_duration (parse, GST_FORMAT_TIME, estimated_duration * 1000, 0);
+
+ return GST_FLOW_OK;
+ }
return ret;
}
return res;
}
+
+
+#ifdef GST_EXT_AACPARSER_MODIFICATION
+/* perform seek in push based mode:
+ find BYTE position to move to based on time and delegate to upstream
+*/
+static gboolean
+gst_aac_audio_parse_do_push_seek (GstBaseParse * parse, GstPad * pad, GstEvent * event)
+{
+ GstAacParse *aacparse;
+ aacparse = GST_AAC_PARSE(parse);
+
+ gdouble rate;
+ GstFormat format;
+ GstSeekFlags flags;
+ GstSeekType cur_type, stop_type;
+ gint64 cur, stop;
+ gboolean res;
+ gint64 byte_cur;
+ gint64 esimate_byte;
+ gint32 frame_dur;
+ gint64 upstream_total_bytes = 0;
+ GstFormat fmt = GST_FORMAT_BYTES;
+
+ GST_INFO_OBJECT (parse, "doing aac push-based seek");
+
+ gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop);
+
+ /* FIXME, always play to the end */
+ stop = -1;
+
+ /* only forward streaming and seeking is possible */
+ if (rate <= 0)
+ goto unsupported_seek;
+
+ if ( cur == 0 ) {
+ /* handle rewind only */
+ cur_type = GST_SEEK_TYPE_SET;
+ byte_cur = 0;
+ stop_type = GST_SEEK_TYPE_NONE;
+ stop = -1;
+ flags |= GST_SEEK_FLAG_FLUSH;
+ } else {
+ /* handle normal seek */
+ cur_type = GST_SEEK_TYPE_SET;
+ stop_type = GST_SEEK_TYPE_NONE;
+ stop = -1;
+ flags |= GST_SEEK_FLAG_FLUSH;
+
+ esimate_byte = (cur / (1000 * 1000)) * aacparse->frame_byte;
+ if (aacparse->sample_rate> 0)
+ frame_dur = (aacparse->spf * 1000) / aacparse->sample_rate;
+ else
+ goto unsupported_seek;
+ if (frame_dur > 0)
+ byte_cur = esimate_byte / (frame_dur);
+ else
+ goto unsupported_seek;
+
+ GST_INFO_OBJECT(parse, "frame_byte(%d) spf(%d) rate (%d) ", aacparse->frame_byte, aacparse->spf, aacparse->sample_rate);
+ GST_INFO_OBJECT(parse, "seek cur (%"G_GINT64_FORMAT") = (%"GST_TIME_FORMAT") ", cur, GST_TIME_ARGS (cur));
+ GST_INFO_OBJECT(parse, "esimate_byte(%"G_GINT64_FORMAT") esimate_byte (%d)", esimate_byte, frame_dur );
+ }
+
+ /* obtain real upstream total bytes */
+ if (!gst_pad_query_peer_duration (GST_BASE_PARSE_SINK_PAD (GST_BASE_PARSE
+ (aacparse)), &fmt, &upstream_total_bytes))
+ upstream_total_bytes = 0;
+ GST_INFO_OBJECT (aacparse, "gst_pad_query_peer_duration -upstream_total_bytes (%"G_GUINT64_FORMAT")", upstream_total_bytes);
+ aacparse->file_size = upstream_total_bytes;
+
+ if ( (byte_cur == -1) || (byte_cur > aacparse->file_size))
+ {
+ GST_INFO_OBJECT(parse, "[WEB-ERROR] seek cur (%"G_GINT64_FORMAT") > file_size (%"G_GINT64_FORMAT") ", cur, aacparse->file_size );
+ goto abort_seek;
+ }
+
+ GST_INFO_OBJECT (parse, "Pushing BYTE seek rate %g, " "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur, stop);
+
+ if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
+ GST_INFO_OBJECT (parse, "Requested seek time: %" GST_TIME_FORMAT ", calculated seek offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (cur), byte_cur);
+ }
+
+ /* BYTE seek event */
+ event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur, stop_type, stop);
+ res = gst_pad_push_event (parse->sinkpad, event);
+
+ return res;
+
+ /* ERRORS */
+
+abort_seek:
+ {
+ GST_DEBUG_OBJECT (parse, "could not determine byte position to seek to, " "seek aborted.");
+ return FALSE;
+ }
+
+unsupported_seek:
+ {
+ GST_DEBUG_OBJECT (parse, "unsupported seek, seek aborted.");
+ return FALSE;
+ }
+}
+
+
+static guint
+gst_aac_parse_adts_get_fast_frame_len (const guint8 * data)
+{
+ int length;
+ if ((data[0] == 0xff) && ((data[1] & 0xf6) == 0xf0)) {
+ length = ((data[3] & 0x03) << 11) | (data[4] << 3) | ((data[5] & 0xe0) >> 5);
+ } else {
+ length = 0;
+ }
+ return length;
+}
+
+// evil
+static gboolean
+gst_aac_parse_src_eventfunc(GstBaseParse * parse, GstEvent * event)
+{
+ gboolean handled = FALSE;
+ GstAacParse *aacparse;
+ aacparse = GST_AAC_PARSE(parse);
+
+ GST_DEBUG("Entering gst_aac_parse_src_eventfunc header type = %d", aacparse->header_type);
+ if(aacparse->header_type == DSPAAC_HEADER_ADIF)
+ return gst_aac_parse_adif_src_eventfunc(parse, event);
+ else if(aacparse->header_type == DSPAAC_HEADER_ADTS)
+ return gst_aac_parse_adts_src_eventfunc (parse, event);
+ else
+ goto aac_seek_null_exit;
+aac_seek_null_exit:
+
+ /* call baseparse src_event function to handle event */
+ handled = GST_BASE_PARSE_CLASS (parent_class)->src_event (parse, event);
+ return handled;
+}
+
+/**
+ * gst_aac_parse_adts_src_eventfunc:
+ * @parse: #GstBaseParse. #event
+ *
+ * before baseparse handles seek event, make full amr index table.
+ *
+ * Returns: TRUE on success.
+ */
+static gboolean
+gst_aac_parse_adts_src_eventfunc (GstBaseParse * parse, GstEvent * event)
+{
+ gboolean handled = FALSE;
+ GstAacParse *aacparse;
+ aacparse = GST_AAC_PARSE (parse);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ {
+ GstFlowReturn res = GST_FLOW_OK;
+ gint64 base_offset = 0, sync_offset = 0, cur = 0;
+ gint32 frame_count = 1; /* do not add first frame because it is already in index table */
+ gint64 second_count = 0; /* initial 1 second */
+ gint64 total_file_size = 0, start_offset = 0;
+ GstClockTime current_ts = GST_CLOCK_TIME_NONE;
+ GstActivateMode pad_mode = GST_ACTIVATE_NONE;
+ gboolean large_file_flag = FALSE;
+
+#ifdef GST_EXT_BASEPARSER_MODIFICATION /* check baseparse define these fuction */
+ gst_base_parse_get_pad_mode(parse, &pad_mode);
+ if (pad_mode != GST_ACTIVATE_PULL) {
+ gboolean ret = FALSE;
+ GST_INFO_OBJECT (aacparse, "aac parser is PUSH MODE.");
+ GstPad* srcpad = gst_element_get_pad(parse, "src");
+ /* check NULL */
+ ret = gst_aac_audio_parse_do_push_seek(parse, srcpad, event);
+ gst_object_unref(srcpad);
+ return ret;
+ }
+ gst_base_parse_get_upstream_size(parse, &total_file_size);
+ gst_base_parse_get_index_last_offset(parse, &start_offset);
+ gst_base_parse_get_index_last_ts(parse, ¤t_ts);
+#else
+ GST_ERROR("baseparser does not define get private param functions. can not make index table here.");
+ break;
+#endif
+
+ if (total_file_size > AAC_LARGE_FILE_SIZE ) {
+ large_file_flag = TRUE;
+ gst_base_parse_set_seek_mode(parse, 0);
+ GST_INFO_OBJECT (aacparse, "larger than big size (2MB).");
+ goto aac_seek_null_exit;
+ }
+
+ GST_DEBUG("gst_aac_parse_adts_src_eventfunc GST_EVENT_SEEK enter");
+
+ if (total_file_size == 0 || start_offset >= total_file_size) {
+ GST_ERROR("last index offset %d is larger than file size %d", start_offset, total_file_size);
+ break;
+ }
+
+ gst_event_parse_seek (event, NULL, NULL, NULL, NULL, &cur, NULL, NULL);
+ if (cur <= current_ts) {
+ GST_INFO("seek to %"GST_TIME_FORMAT" within index table %"GST_TIME_FORMAT". do not make index table",
+ GST_TIME_ARGS(cur), GST_TIME_ARGS(current_ts));
+ break;
+ } else {
+ GST_INFO("seek to %"GST_TIME_FORMAT" without index table %"GST_TIME_FORMAT". make index table",
+ GST_TIME_ARGS(cur), GST_TIME_ARGS(current_ts));
+ }
+
+ GST_INFO("make AAC(ADTS) Index Table. file_size = %"G_GINT64_FORMAT" last idx offset=%"G_GINT64_FORMAT
+ ", last idx ts=%"GST_TIME_FORMAT, total_file_size, start_offset, GST_TIME_ARGS(current_ts));
+
+ base_offset = start_offset; /* set base by start offset */
+ second_count = current_ts + GST_SECOND; /* 1sec */
+
+ /************************************/
+ /* STEP 0: Setting parse information */
+ /************************************/
+ aacparse->spf = aacparse->frame_samples;
+ aacparse->frame_duration = (aacparse->spf * 1000 * 100) / aacparse->sample_rate; /* duration per frame (msec) */
+ aacparse->frame_per_sec = (aacparse->sample_rate) / aacparse->spf; /* frames per second (ea) */
+
+ /************************************/
+ /* STEP 1: MAX_PULL_RANGE_BUF cycle */
+ /************************************/
+ while (total_file_size - base_offset >= AAC_MAX_PULL_RANGE_BUF) {
+ gint64 offset = 0;
+ GstBuffer *buffer = NULL;
+ guint8 *buf = NULL;
+
+ GST_INFO("gst_pad_pull_range %d bytes (from %"G_GINT64_FORMAT") use max size", AAC_MAX_PULL_RANGE_BUF, base_offset);
+ res = gst_pad_pull_range (parse->sinkpad, base_offset, base_offset + AAC_MAX_PULL_RANGE_BUF, &buffer);
+ if (res != GST_FLOW_OK) {
+ GST_ERROR ("gst_pad_pull_range failed!");
+ break;
+ }
+
+ buf = GST_BUFFER_DATA(buffer);
+ if (buf == NULL) {
+ GST_WARNING("buffer is NULL in make aac seek table's STEP1");
+ gst_buffer_unref (buffer);
+ goto aac_seek_null_exit;
+ }
+
+ while (offset <= AAC_MAX_PULL_RANGE_BUF) {
+ gint frame_size = 0;
+ guint32 header;
+
+ /* make sure the values in the frame header look sane */
+ frame_size = gst_aac_parse_adts_get_fast_frame_len (buf);
+
+ if ((frame_size > 0) && (frame_size < (AAC_MAX_PULL_RANGE_BUF - offset))) {
+ if (current_ts > second_count) { /* 1 sec == xx frames. we make idx per sec */
+ gst_base_parse_add_index_entry (parse, base_offset +offset, current_ts, TRUE, TRUE); /* force */
+ GST_DEBUG("Adding index ts=%"GST_TIME_FORMAT" offset %"G_GINT64_FORMAT,
+ GST_TIME_ARGS(current_ts), base_offset + offset);
+ second_count += GST_SECOND; /* 1sec */
+ }
+
+ current_ts += (aacparse->frame_duration * GST_MSECOND) / 100; /* each frame is (frame_duration) ms */
+ offset += frame_size;
+ buf += frame_size;
+ frame_count++;
+ } else if (frame_size >= (AAC_MAX_PULL_RANGE_BUF - offset)) {
+ GST_DEBUG("we need refill buffer");
+ break;
+ } else {
+ GST_WARNING("we lost sync");
+ buf++;
+ offset++;
+ }
+ } /* while */
+ base_offset = base_offset + offset;
+ gst_buffer_unref (buffer);
+ } /* end MAX buffer cycle */
+
+ /*******************************/
+ /* STEP 2: Remain Buffer cycle */
+ /*******************************/
+ if (total_file_size - base_offset > 0) {
+ gint64 offset = 0;
+ GstBuffer *buffer = NULL;
+ guint8 *buf = NULL;
+
+ GST_INFO("gst_pad_pull_range %"G_GINT64_FORMAT" bytes (from %"G_GINT64_FORMAT") use remain_buf size",
+ total_file_size - base_offset, base_offset);
+ res = gst_pad_pull_range (parse->sinkpad, base_offset, total_file_size, &buffer);
+ if (res != GST_FLOW_OK) {
+ GST_ERROR ("gst_pad_pull_range failed!");
+ break;
+ }
+
+ buf = GST_BUFFER_DATA(buffer);
+ if (buf == NULL) {
+ GST_WARNING("buffer is NULL in make aac seek table's STEP2");
+ gst_buffer_unref (buffer);
+ goto aac_seek_null_exit;
+ }
+
+ while (base_offset + offset < total_file_size) {
+ gint frame_size = 0;
+ guint32 header;
+
+ /* make sure the values in the frame header look sane */
+ frame_size = gst_aac_parse_adts_get_fast_frame_len (buf);
+
+ if ((frame_size > 0) && (frame_size <= (total_file_size - (base_offset + offset)))) {
+ if (current_ts > second_count) { /* 1 sec == xx frames. we make idx per sec */
+ gst_base_parse_add_index_entry (parse, base_offset +offset, current_ts, TRUE, TRUE); /* force */
+ GST_DEBUG("Adding index ts=%"GST_TIME_FORMAT" offset %"G_GINT64_FORMAT,
+ GST_TIME_ARGS(current_ts), base_offset + offset);
+ second_count += GST_SECOND; /* 1sec */
+ }
+
+ current_ts += (aacparse->frame_duration * GST_MSECOND) / 100; /* each frame is (frame_duration) ms */
+ offset += frame_size;
+ buf += frame_size;
+ frame_count++;
+ } else if (frame_size == 0) {
+ GST_DEBUG("Frame size is 0 so, Decoding end..");
+ break;
+ } else {
+ GST_WARNING("we lost sync");
+ buf++;
+ offset++;
+ }
+ } /* while */
+
+ gst_buffer_unref (buffer);
+ } /* end remain_buf buffer cycle */
+
+ GST_DEBUG("gst_aac_parse_adts_src_eventfunc GST_EVENT_SEEK leave");
+ }
+ break;
+
+ default:
+ break;
+ }
+
+aac_seek_null_exit:
+
+ /* call baseparse src_event function to handle event */
+ handled = GST_BASE_PARSE_CLASS (parent_class)->src_event (parse, event);
+
+ return handled;
+}
+
+// evil (ADIF SEEK)
+// Enter this function if the detected format is adif.
+static gboolean
+gst_aac_parse_adif_src_eventfunc (GstBaseParse * parse, GstEvent * event)
+{
+ // Case 1: Constant bit rate => Find the byte boundary using bit rate. Add these values as indices using gst_base_parse_add_index_entry.
+ // Case 2: Variable bit rate => Need to parse the whole file here itself to find frame boundaries.
+ //gboolean handled = FALSE;
+ gint64 cur = 0;
+ int offset = 0;
+ gboolean handled = FALSE;
+ GstBuffer* buffer;
+ const guint8 * data;
+
+ GstAacParse *aacparse;
+ aacparse = GST_AAC_PARSE(parse);
+
+ GST_DEBUG("gst_aac_parse_adif_src_eventfunc enter");
+
+ aacparse->frame_duration = (aacparse->frame_samples* 1000 * 100) / aacparse->sample_rate; /* duration per frame (msec) */
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ {
+ int base_offset = aacparse->read_bytes;
+ int framesize = 0, frame_duration_us = 0, duration = 0, estimated_bitrate, estimated_duration;
+ GstFlowReturn res = GST_FLOW_OK;
+ static int framecount = 0;
+ GstClockTime current_ts = GST_CLOCK_TIME_NONE;
+
+ GST_LOG_OBJECT(aacparse, "ADIF: Let us Handle seek event");
+
+ // Parse the event and get the timestamp where to seek.
+ //gst_event_parse_seek (event, NULL, NULL, NULL, NULL, &cur, NULL, NULL);
+
+ res = gst_pad_pull_range (parse->sinkpad, base_offset, aacparse->file_size, &buffer);
+
+ if (res != GST_FLOW_OK) {
+ GST_ERROR ("gst_pad_pull_range failed!");
+ break;
+ }
+
+ data = GST_BUFFER_DATA(buffer);
+ if (data == NULL) {
+ GST_WARNING("buffer is NULL in make aac seek table's STEP2");
+ gst_buffer_unref (buffer);
+ goto aac_seek_null_exit;
+ }
+ offset = base_offset;
+ while(offset <= aacparse->file_size) {
+ gst_base_parse_add_index_entry (parse, offset, current_ts, TRUE, TRUE); /* force */
+ framesize = ((int)data[2] << 8) + data[3] + 4;
+ offset += framesize;
+ data += framesize;
+ framecount++;
+ current_ts += (aacparse->frame_duration * GST_MSECOND) / 100; /* each frame is (frame_duration) ms */
+ }
+ gst_base_parse_set_frame_rate (GST_BASE_PARSE (aacparse), aacparse->sample_rate, 1024, 2, 2);
+ gst_buffer_unref (buffer);
+ break;
+ }
+ default:
+ break;
+ }
+aac_seek_null_exit:
+
+ /* call baseparse src_event function to handle event */
+ handled = GST_BASE_PARSE_CLASS (parent_class)->src_event (parse, event);
+
+ return handled;
+}
+
+#endif //end of #ifdef GST_EXT_AACPARSER_MODIFICATION
#define GST_IS_AAC_PARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_AAC_PARSE))
+typedef enum
+{
+ DECAAC_RET_SUCCESS = 0,
+ DECAAC_RET_ERR_NOT_SUFF_MEM = -2,
+ DECAAC_RET_ERR_NOT_SUPPORT = -3,
+ DECAAC_RET_ERR_INVALID_ARG = -4,
+ DECAAC_RET_ERR_INVALID_BS = -5,
+ DECAAC_RET_ERR_NOT_EXPECTED = -8,
+ DECAAC_RET_ERR_NOT_SUFF_BS = -9,
+ DECAAC_RET_ERR_BAD_CRC = -10,
+ DECAAC_RET_ERR_INVALID_HCB = -11,
+ DECAAC_RET_ERR_UNKNOWN = -0xFF,
+} eDECAACRET;
/**
* GstAacHeaderType:
gint mpegversion;
gint frame_samples;
+#ifdef GST_EXT_AACPARSER_MODIFICATION
+ gboolean first_frame; /* estimate duration once at the first time */
+
+ guint hdr_bitrate; /* added - estimated bitrate (bps) */
+ guint spf; /* added - samples per frame = frame_samples */
+ guint frame_duration; /* added - duration per frame (msec) */
+ guint frame_per_sec; /* added - frames per second (ea) */
+ guint bitstream_type; /* added- bitstream type - constant or variable */ //evil
+ guint adif_header_length;
+ guint num_program_config_elements;
+ guint read_bytes;
+ gint64 file_size;
+ guint frame_byte;
+#endif
GstAacHeaderType header_type;
};
/* AMR has a "hardcoded" framerate of 50fps */
#define AMR_FRAMES_PER_SECOND 50
#define AMR_FRAME_DURATION (GST_SECOND/AMR_FRAMES_PER_SECOND)
-#define AMR_MIME_HEADER_SIZE 9
+#define AMR_MIME_HEADER_SIZE 6
static gboolean gst_amr_parse_start (GstBaseParse * parse);
static gboolean gst_amr_parse_stop (GstBaseParse * parse);
static GstFlowReturn gst_amr_parse_parse_frame (GstBaseParse * parse,
GstBaseParseFrame * frame);
+#ifdef GST_EXT_AMRPARSER_MODIFICATION /* make full amr index table when seek */
+ #define AMR_MAX_PULL_RANGE_BUF (5 * 1024 * 1024) /* 5 mbyte */
+ static gboolean gst_amr_parse_src_eventfunc (GstBaseParse * parse, GstEvent * event);
+#endif
+
#define _do_init(bla) \
GST_DEBUG_CATEGORY_INIT (amrparse_debug, "amrparse", 0, \
"AMR-NB audio stream parser");
parse_class->parse_frame = GST_DEBUG_FUNCPTR (gst_amr_parse_parse_frame);
parse_class->check_valid_frame =
GST_DEBUG_FUNCPTR (gst_amr_parse_check_valid_frame);
+
+#ifdef GST_EXT_AMRPARSER_MODIFICATION /* make full amr index table when seek */
+ parse_class->src_event = gst_amr_parse_src_eventfunc;
+#endif
}
return FALSE;
}
- amrparse->need_header = FALSE;
+#ifdef GST_EXT_AMRPARSER_MODIFICATION
+ if (amrparse->pad_mode != GST_ACTIVATE_PULL)
+ amrparse->need_header = FALSE;
+#endif
+
gst_base_parse_set_frame_rate (GST_BASE_PARSE (amrparse), 50, 1, 2, 2);
gst_amr_parse_set_src_caps (amrparse);
return TRUE;
GST_DEBUG_OBJECT (amrparse, "Parsing header data");
if (!memcmp (data, "#!AMR-WB\n", 9)) {
- GST_DEBUG_OBJECT (amrparse, "AMR-WB detected");
+ GST_WARNING_OBJECT (amrparse, "AMR-WB detected");
amrparse->block_size = block_size_wb;
amrparse->wide = TRUE;
*skipsize = amrparse->header = 9;
} else if (!memcmp (data, "#!AMR\n", 6)) {
- GST_DEBUG_OBJECT (amrparse, "AMR-NB detected");
+ GST_WARNING_OBJECT (amrparse, "AMR-NB detected");
amrparse->block_size = block_size_nb;
amrparse->wide = FALSE;
*skipsize = amrparse->header = 6;
+#ifdef GST_EXT_AMRPARSER_MODIFICATION
+ } else {
+ GST_ERROR_OBJECT (amrparse, "AMR HEADER don't detected");
+ return FALSE;
+ }
+#else
} else
return FALSE;
+#endif
gst_amr_parse_set_src_caps (amrparse);
return TRUE;
gst_base_parse_set_frame_rate (GST_BASE_PARSE (amrparse), 50, 1, 2, 2);
} else {
GST_WARNING ("media doesn't look like a AMR format");
+#ifdef GST_EXT_AMRPARSER_MODIFICATION
+ if (amrparse->pad_mode == GST_ACTIVATE_PULL) {
+ amrparse->need_header = FALSE;
+ *framesize = 0;
+ *skipsize = -2;
+ GST_ERROR_OBJECT (amrparse, "Invalid AMR Header Format");
+ return FALSE;
+ }
+#endif
}
/* We return FALSE, so this frame won't get pushed forward. Instead,
the "skip" value is set, so next time we will receive a valid frame. */
mode = (data[0] >> 3) & 0x0F;
fsize = amrparse->block_size[mode] + 1; /* +1 for the header byte */
+#ifdef GST_EXT_AMRPARSER_MODIFICATION
+ if (amrparse->pad_mode == GST_ACTIVATE_PULL) {
+ if (amrparse->sync_check) {
+ GST_DEBUG_OBJECT (amrparse, "AMR Next sync check : fsize (%d)", fsize);
+ amrparse->sync_check = FALSE;
+ if ((fsize > 0) && ((data[fsize] & 0x83) == 0)) {
+ gint next_fsize,next_mode;
+ next_mode = (data[fsize] >> 3) & 0x0F;
+ next_fsize = amrparse->block_size[next_mode] + 1;
+ if(fsize != next_fsize) {
+ *framesize = 0;
+ *skipsize = -2;
+ GST_ERROR_OBJECT (amrparse, "Invalid mode bit");
+ return FALSE;
+ }
+ } else {
+ *framesize = 0;
+ *skipsize = -2;
+ GST_ERROR_OBJECT (amrparse, "Invalid mode bit");
+ return FALSE;
+ }
+ }
+ }
+#endif
+
/* We recognize this data as a valid frame when:
* - We are in sync. There is no need for extra checks then
* - We are in EOS. There might not be enough data to check next frame
GST_DEBUG ("start");
amrparse->need_header = TRUE;
amrparse->header = 0;
+#ifdef GST_EXT_AMRPARSER_MODIFICATION
+ amrparse->sync_check = TRUE;
+ gst_base_parse_get_pad_mode(parse, &amrparse->pad_mode);
+ if (amrparse->pad_mode == GST_ACTIVATE_PULL) {
+ GST_WARNING_OBJECT (amrparse, "pad_mode : GST_ACTIVATE_PULL MODE.");
+ } else if (amrparse->pad_mode == GST_ACTIVATE_PUSH) {
+ GST_WARNING_OBJECT (amrparse, " pad_mode : GST_ACTIVATE_PUSH MODE.");
+ } else
+ GST_WARNING_OBJECT (amrparse, "pad_mode : GST_ACTIVATE_NONE MODE.");
+#endif
return TRUE;
}
return res;
}
+
+
+#ifdef GST_EXT_AMRPARSER_MODIFICATION /* make full amr index table when seek */
+/**
+ * gst_amr_parse_src_eventfunc:
+ * @parse: #GstBaseParse. #event
+ *
+ * before baseparse handles seek event, make full amr index table.
+ *
+ * Returns: TRUE on success.
+ */
+static gboolean
+gst_amr_parse_src_eventfunc (GstBaseParse * parse, GstEvent * event)
+{
+ gboolean handled = FALSE;
+ GstAmrParse *amrparse;
+ amrparse = GST_AMR_PARSE (parse);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ {
+ GstFlowReturn res = GST_FLOW_OK;
+ gint64 base_offset = 0, sync_offset = 0, cur = 0;
+ gint32 frame_count = 1; /* do not add first frame because it is already in index table */
+ gint64 total_file_size = 0, start_offset = 0;
+ GstClockTime current_ts = GST_CLOCK_TIME_NONE;
+ GstActivateMode pad_mode = GST_ACTIVATE_NONE;
+
+#ifdef GST_EXT_BASEPARSER_MODIFICATION /* check baseparse define these fuction */
+ gst_base_parse_get_pad_mode(parse, &pad_mode);
+ if (pad_mode != GST_ACTIVATE_PULL) {
+ GST_INFO_OBJECT (amrparse, "arm parser is not pull mode. amr parser can not make index table.");
+ return FALSE;
+ }
+ gst_base_parse_get_upstream_size(parse, &total_file_size);
+ gst_base_parse_get_index_last_offset(parse, &start_offset);
+ gst_base_parse_get_index_last_ts(parse, ¤t_ts);
+#else
+ GST_WARNING_OBJECT (amrparse, "baseparser does not define get private param functions. can not make index table here.");
+ break;
+#endif
+
+ GST_LOG_OBJECT (amrparse, "gst_amr_parse_src_eventfunc GST_EVENT_SEEK enter");
+
+ if (total_file_size == 0 || start_offset >= total_file_size) {
+ GST_ERROR("last index offset %d is larger than file size %d", start_offset, total_file_size);
+ break;
+ }
+
+ gst_event_parse_seek (event, NULL, NULL, NULL, NULL, &cur, NULL, NULL);
+ if (cur <= current_ts) {
+ GST_INFO_OBJECT (amrparse, "seek to %"GST_TIME_FORMAT" within index table %"GST_TIME_FORMAT". do not make index table",
+ GST_TIME_ARGS(cur), GST_TIME_ARGS(current_ts));
+ break;
+ } else {
+ GST_INFO_OBJECT (amrparse, "seek to %"GST_TIME_FORMAT" without index table %"GST_TIME_FORMAT". make index table",
+ GST_TIME_ARGS(cur), GST_TIME_ARGS(current_ts));
+ }
+
+ GST_INFO_OBJECT (amrparse, "make AMR Index Table. file_size = %"G_GINT64_FORMAT" last idx offset=%"G_GINT64_FORMAT
+ ", last idx ts=%"GST_TIME_FORMAT, total_file_size, start_offset, GST_TIME_ARGS(current_ts));
+
+ base_offset = start_offset; /* set base by start offset */
+
+
+ /************************************/
+ /* STEP 1: MAX_PULL_RANGE_BUF cycle */
+ /************************************/
+ while (total_file_size - base_offset >= AMR_MAX_PULL_RANGE_BUF) {
+ gint64 offset = 0;
+ GstBuffer *buffer = NULL;
+ guint8 *buf = NULL;
+
+ GST_INFO_OBJECT (amrparse, "gst_pad_pull_range %d bytes (from %"G_GINT64_FORMAT") use max size", AMR_MAX_PULL_RANGE_BUF, base_offset);
+ res = gst_pad_pull_range (parse->sinkpad, base_offset,
+ base_offset + AMR_MAX_PULL_RANGE_BUF, &buffer);
+ if (res != GST_FLOW_OK) {
+ GST_ERROR_OBJECT (amrparse, "gst_pad_pull_range failed!");
+ break;
+ }
+
+ buf = GST_BUFFER_DATA(buffer);
+ if (buf == NULL) {
+ GST_WARNING("buffer is NULL in make amr seek table's STEP1");
+ gst_buffer_unref (buffer);
+ goto amr_seek_null_exit;
+ }
+
+ while (offset <= AMR_MAX_PULL_RANGE_BUF) {
+ gint mode = 0, frame_size = 0;
+
+ if ((buf[offset] & 0x83) == 0) {
+ mode = (buf[offset] >> 3) & 0x0F;
+ frame_size = amrparse->block_size[mode] + 1; /* +1 for the header byte */
+ if (frame_size < 13) {
+ GST_WARNING_OBJECT (amrparse, "frame_size is Invalid (%d) - seek event END at offset %"G_GINT64_FORMAT"", frame_size, base_offset + offset);
+ break;
+ }
+
+ if (frame_count % 50 == 0) { /* 1 sec == 50 frames. we make idx per sec */
+ gst_base_parse_add_index_entry (parse, base_offset +offset, current_ts, TRUE, TRUE); /* force */
+ GST_DEBUG_OBJECT (amrparse, "Adding index ts=%"GST_TIME_FORMAT" offset %"G_GINT64_FORMAT,
+ GST_TIME_ARGS(current_ts), base_offset + offset);
+ }
+
+ current_ts += 20 * 1000 * 1000; /* each frame is 20ms */
+ offset += frame_size;
+ frame_count++;
+ } else {
+ GST_WARNING_OBJECT (amrparse, "we lost sync");
+ offset++;
+ }
+ } /* while */
+ base_offset = base_offset + offset;
+ gst_buffer_unref (buffer);
+ } /* end MAX buffer cycle */
+
+
+ /*******************************/
+ /* STEP 2: Remain Buffer cycle */
+ /*******************************/
+ if (total_file_size - base_offset > 0) {
+ gint64 offset = 0;
+ GstBuffer *buffer = NULL;
+ guint8 *buf = NULL;
+
+ GST_INFO_OBJECT (amrparse, "gst_pad_pull_range %"G_GINT64_FORMAT" bytes (from %"G_GINT64_FORMAT") use remain_buf size",
+ total_file_size - base_offset, base_offset);
+ res = gst_pad_pull_range (parse->sinkpad, base_offset,
+ total_file_size, &buffer);
+ if (res != GST_FLOW_OK) {
+ GST_ERROR ("gst_pad_pull_range failed!");
+ break;
+ }
+
+ buf = GST_BUFFER_DATA(buffer);
+ if (buf == NULL) {
+ GST_WARNING("buffer is NULL in make amr seek table's STEP2");
+ gst_buffer_unref (buffer);
+ goto amr_seek_null_exit;
+ }
+
+ while (base_offset + offset < total_file_size) {
+ gint mode = 0, frame_size = 0;
+
+ if ((buf[offset] & 0x83) == 0) {
+ mode = (buf[offset] >> 3) & 0x0F;
+ frame_size = amrparse->block_size[mode] + 1; /* +1 for the header byte */
+ if (frame_size < 13) {
+ GST_WARNING_OBJECT (amrparse, "frame_size is Invalid (%d) - seek event END at offset %"G_GINT64_FORMAT"", frame_size, base_offset + offset);
+ break;
+ }
+
+ if (frame_count % 50 == 0) { /* 1 sec == 50 frames. we make idx per sec */
+ gst_base_parse_add_index_entry (parse, base_offset +offset, current_ts, TRUE, TRUE); /* force */
+ GST_DEBUG_OBJECT (amrparse, "Adding index ts=%"GST_TIME_FORMAT" offset %"G_GINT64_FORMAT,
+ GST_TIME_ARGS(current_ts), base_offset + offset);
+ }
+
+ current_ts += 20 * 1000 * 1000; /* each frame is 20ms */
+ offset += frame_size;
+ frame_count++;
+ } else {
+ GST_WARNING_OBJECT (amrparse, "we lost sync");
+ offset++;
+ }
+ } /* while */
+
+ gst_buffer_unref (buffer);
+ } /* end remain_buf buffer cycle */
+
+ GST_LOG_OBJECT (amrparse, "gst_amr_parse_src_eventfunc GST_EVENT_SEEK leave");
+ }
+ break;
+
+ default:
+ break;
+ }
+
+amr_seek_null_exit:
+
+ /* call baseparse src_event function to handle event */
+ handled = GST_BASE_PARSE_CLASS (parent_class)->src_event (parse, event);
+
+ return handled;
+}
+#endif
gboolean need_header;
gint header;
gboolean wide;
+#ifdef GST_EXT_AMRPARSER_MODIFICATION
+ gboolean sync_check;
+ GstActivateMode pad_mode;
+#endif
};
/**
GstFormat src_format, gint64 src_value, GstFormat dest_format,
gint64 * dest_value);
static GstCaps *gst_flac_parse_get_sink_caps (GstBaseParse * parse);
+static gboolean gst_flac_parse_src_eventfunc (GstBaseParse * parse, GstEvent * event);
+static gboolean gst_flac_parse_do_seek (GstBaseParse * parse, GstPad * pad, GstEvent * event);
GST_BOILERPLATE (GstFlacParse, gst_flac_parse, GstBaseParse,
GST_TYPE_BASE_PARSE);
baseparse_class->convert = GST_DEBUG_FUNCPTR (gst_flac_parse_convert);
baseparse_class->get_sink_caps =
GST_DEBUG_FUNCPTR (gst_flac_parse_get_sink_caps);
+ baseparse_class->src_event = GST_DEBUG_FUNCPTR (gst_flac_parse_src_eventfunc);
}
static void
flacparse->check_frame_checksums = DEFAULT_CHECK_FRAME_CHECKSUMS;
}
+static gboolean
+gst_flac_parse_src_eventfunc (GstBaseParse * parse, GstEvent * event)
+{
+ GstFlacParse *flacparse = GST_FLAC_PARSE (parse);
+ gboolean handled = FALSE;
+
+ GST_LOG_OBJECT (flacparse, "handling %s event", GST_EVENT_TYPE_NAME (event));
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ {
+ GstPad* srcpad = gst_element_get_pad(parse, "src");
+ handled = gst_flac_parse_do_seek(parse, srcpad, event);
+ gst_object_unref(srcpad);
+ if(!handled)
+ {
+ handled = GST_BASE_PARSE_CLASS (parent_class)->src_event (parse, event);
+ }
+ break;
+ }
+ default:
+ handled = GST_BASE_PARSE_CLASS (parent_class)->src_event (parse, event);
+ break;
+ }
+ return handled;
+}
+
+static gboolean
+gst_flac_parse_do_seek (GstBaseParse * parse, GstPad * pad, GstEvent * event)
+{
+ GstFlacParse *flacparse = GST_FLAC_PARSE (parse);
+
+ gdouble rate;
+ GstFormat format;
+ GstSeekFlags flags;
+ GstSeekType cur_type, stop_type;
+ gint64 cur, stop;
+ gboolean res = FALSE;
+
+ gint64 total_file_size = 0, start_offset = 0;
+ gint64 duration = 0;
+ gint64 seeked_time = 0;
+
+ gint64 estimate_sample = 0;
+ gint64 estimate_byte = 0;
+ GstClockTime current_ts = GST_CLOCK_TIME_NONE;
+
+
+ gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop);
+
+ if (rate <= 0)
+ goto unsupported_seek;
+
+ cur_type = GST_SEEK_TYPE_SET;
+ stop_type = GST_SEEK_TYPE_NONE;
+ stop = -1;
+ flags |= GST_SEEK_FLAG_FLUSH;
+
+ gst_base_parse_get_upstream_size(parse, &total_file_size);
+ gst_base_parse_get_index_last_ts(parse, ¤t_ts);
+
+ if (cur <= current_ts) {
+ GST_INFO("seek to %"GST_TIME_FORMAT" within index table %"GST_TIME_FORMAT". do not make index table", GST_TIME_ARGS(cur), GST_TIME_ARGS(current_ts));
+ return res;
+ } else {
+ GST_INFO("seek to %"GST_TIME_FORMAT" without index table %"GST_TIME_FORMAT". make index table", GST_TIME_ARGS(cur), GST_TIME_ARGS(current_ts));
+ }
+ duration = flacparse->total_samples / flacparse->samplerate;
+ seeked_time = GST_TIME_AS_SECONDS(cur);
+
+ estimate_byte = (seeked_time * total_file_size) / duration;
+ GST_DEBUG_OBJECT(flacparse, "Initial estimate position %"G_GINT64_FORMAT, estimate_byte);
+ while(1)
+ {
+ GstBuffer* buffer = NULL;
+ const guint8 * data;
+ res = gst_pad_pull_range (parse->sinkpad, estimate_byte, flacparse->max_framesize, &buffer);
+ if (res != GST_FLOW_OK) {
+ GST_ERROR ("gst_pad_pull_range failed!");
+ break;
+ }
+ GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buffer);
+ gint off = 0;
+ off = gst_byte_reader_masked_scan_uint32 (&reader, 0xfffc0000, 0xfff80000, 0, GST_BUFFER_SIZE (buffer));
+ if (off > 0) {
+ GST_DEBUG_OBJECT (parse, "Possible sync at buffer offset %d", off);
+ estimate_byte += off;
+ gst_base_parse_add_index_entry (parse, estimate_byte, cur, TRUE, TRUE);
+ gst_buffer_unref (buffer);
+ break;
+ } else {
+ GST_DEBUG_OBJECT (flacparse, "Sync code not found");
+ estimate_byte += (GST_BUFFER_SIZE (buffer) - 3);
+ gst_buffer_unref (buffer);
+ continue;
+ }
+ }
+estimate:
+ GST_DEBUG_OBJECT(flacparse, "seeking to offset %"G_GINT64_FORMAT, estimate_byte);
+ return FALSE;
+
+unsupported_seek:
+ {
+ GST_DEBUG_OBJECT (flacparse, "unsupported seek, seek aborted.");
+ return FALSE;
+ }
+}
+
static void
gst_flac_parse_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
} else if (flacparse->state == GST_FLAC_PARSE_STATE_HEADERS) {
gboolean is_last = ((data[0] & 0x80) == 0x80);
guint type = (data[0] & 0x7F);
+ gboolean hdr_ok = TRUE;
if (type == 127) {
GST_WARNING_OBJECT (flacparse, "Invalid metadata block type");
switch (type) {
case 0: /* STREAMINFO */
- if (!gst_flac_parse_handle_streaminfo (flacparse, buffer))
- return GST_FLOW_ERROR;
+ hdr_ok = gst_flac_parse_handle_streaminfo (flacparse, buffer);
break;
case 3: /* SEEKTABLE */
- if (!gst_flac_parse_handle_seektable (flacparse, buffer))
- return GST_FLOW_ERROR;
+ hdr_ok = gst_flac_parse_handle_seektable (flacparse, buffer);
break;
case 4: /* VORBIS_COMMENT */
- if (!gst_flac_parse_handle_vorbiscomment (flacparse, buffer))
- return GST_FLOW_ERROR;
+ hdr_ok = gst_flac_parse_handle_vorbiscomment (flacparse, buffer);
break;
case 6: /* PICTURE */
- if (!gst_flac_parse_handle_picture (flacparse, buffer))
- return GST_FLOW_ERROR;
+ hdr_ok = gst_flac_parse_handle_picture (flacparse, buffer);
break;
case 1: /* PADDING */
case 2: /* APPLICATION */
break;
}
- GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE;
- GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
- GST_BUFFER_OFFSET (buffer) = 0;
- GST_BUFFER_OFFSET_END (buffer) = 0;
+ if (hdr_ok) {
+ GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_OFFSET (buffer) = 0;
+ GST_BUFFER_OFFSET_END (buffer) = 0;
- flacparse->headers =
- g_list_append (flacparse->headers, gst_buffer_ref (buffer));
+ if(type != 6) {
+ flacparse->headers =
+ g_list_append (flacparse->headers, gst_buffer_ref (buffer));
+ }
+ } else {
+ GST_WARNING_OBJECT (flacparse, "failed to parse header of type %u", type);
+
+ /* error out unless we have a STREAMINFO header */
+ if (flacparse->samplerate == 0 || flacparse->bps == 0)
+ return GST_FLOW_ERROR;
+
+ /* .. in which case just stop header parsing and try to find audio */
+ is_last = TRUE;
+ }
if (is_last) {
if (!gst_flac_parse_handle_headers (flacparse))
#define MIN_FRAME_SIZE 6
+#ifdef GST_MP3PARSE_ALP_EXYNOS
+#define ALP_MPEGAUDIOPARSE_BUFFER_SIZE (32 * 1024)
+#define DEFAULT_CHECK_ALP_MP3DEC FALSE
+#endif
+
+#ifdef GST_EXT_MP3PARSER_MODIFICATION
+#define DEFAULT_CHECK_HTTP_SEEK FALSE
+#endif
+
+/* Property */
+enum
+{
+ PROP_0,
+ PROP_CHECK_ALP_MP3DEC,
+ PROP_CHECK_HTTP_SEEK
+};
+
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/mpeg, mpegversion = (int) 1")
);
-static void gst_mpeg_audio_parse_finalize (GObject * object);
+#ifdef GST_EXT_MP3PARSER_MODIFICATION
+ /* make full mp3 index table when seek */
+ #define MP3_MAX_PULL_RANGE_BUF ( 5 * 1024 * 1024) /* 5 mbyte */
+ #define MP3_LARGE_FILE_SIZE ( 50 * 1024 * 1024) /* 50 mbyte */
+ static guint mp3_type_frame_length_calculation (GstMpegAudioParse *mp3parse, guint32 header);
+ static gboolean gst_mpeg_audio_parse_src_eventfunc (GstBaseParse * parse, GstEvent * event);
+#endif
+static void gst_mpeg_audio_parse_finalize (GObject * object);
static gboolean gst_mpeg_audio_parse_start (GstBaseParse * parse);
static gboolean gst_mpeg_audio_parse_stop (GstBaseParse * parse);
static gboolean gst_mpeg_audio_parse_check_valid_frame (GstBaseParse * parse,
GstBaseParseFrame * frame, guint * size, gint * skipsize);
+static void gst_mpeg_audio_parse_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_mpeg_audio_parse_get_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+#ifdef GST_MP3PARSE_ALP_EXYNOS
+static gboolean gst_mpeg_audio_parse_check_valid_frame_alp (GstBaseParse * parse,
+ GstBaseParseFrame * frame, guint * size, gint * skipsize);
+#endif
static GstFlowReturn gst_mpeg_audio_parse_parse_frame (GstBaseParse * parse,
GstBaseParseFrame * frame);
static GstFlowReturn gst_mpeg_audio_parse_pre_push_frame (GstBaseParse * parse,
object_class->finalize = gst_mpeg_audio_parse_finalize;
+ object_class->set_property = gst_mpeg_audio_parse_set_property;
+ object_class->get_property = gst_mpeg_audio_parse_get_property;
+
+#ifdef GST_MP3PARSE_ALP_EXYNOS
+ g_object_class_install_property (object_class, PROP_CHECK_ALP_MP3DEC,
+ g_param_spec_boolean ("alp-mp3dec", "enable/disable",
+ "enable/disable alp mp3dec",
+ DEFAULT_CHECK_ALP_MP3DEC,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#endif
+
+#ifdef GST_EXT_MP3PARSER_MODIFICATION
+ g_object_class_install_property (object_class, PROP_CHECK_HTTP_SEEK,
+ g_param_spec_boolean ("http-pull-mp3dec", "enable/disable",
+ "enable/disable mp3dec http seek pull mode",
+ DEFAULT_CHECK_HTTP_SEEK,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+#endif
+
parse_class->start = GST_DEBUG_FUNCPTR (gst_mpeg_audio_parse_start);
parse_class->stop = GST_DEBUG_FUNCPTR (gst_mpeg_audio_parse_stop);
parse_class->check_valid_frame =
parse_class->get_sink_caps =
GST_DEBUG_FUNCPTR (gst_mpeg_audio_parse_get_sink_caps);
+#ifdef GST_EXT_MP3PARSER_MODIFICATION
+ /* make full mp3 index table when seek */
+ parse_class->src_event = gst_mpeg_audio_parse_src_eventfunc;
+#endif
+
/* register tags */
#define GST_TAG_CRC "has-crc"
#define GST_TAG_MODE "channel-mode"
mp3parse->encoder_delay = 0;
mp3parse->encoder_padding = 0;
+ mp3parse->encoded_file_size = 0;
+#ifdef GST_MP3PARSE_ALP_EXYNOS
+ if (mp3parse->alp_mode_flag) {
+ mp3parse->mp3alp_initialized = 0;
+ mp3parse->mp3alp_frame_1st = 0;
+ mp3parse->mp3alp_frame_count = 0;
+ mp3parse->mp3alp_buffer_count = 0;
+ mp3parse->mp3alp_frame_duration = 0;
+ mp3parse->mp3alp_buffer_duration = 0;
+ mp3parse->mp3alp_frame_duration_float = 0.0;
+ mp3parse->mp3alp_frame_1st_bitrate= 0;
+ }
+#endif
}
static void
gst_mpeg_audio_parse_init (GstMpegAudioParse * mp3parse,
GstMpegAudioParseClass * klass)
{
+#ifdef GST_MP3PARSE_ALP_EXYNOS
+ GST_INFO_OBJECT (klass, "Not Ready : mp3parse->alp_mode_flag (%d)", mp3parse->alp_mode_flag);
+#endif
gst_mpeg_audio_parse_reset (mp3parse);
}
static void
+gst_mpeg_audio_parse_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+ GstMpegAudioParse *mp3parse = GST_MPEG_AUDIO_PARSE (object);
+ GST_INFO_OBJECT (mp3parse, "set_property() START- prop_id(%d)",prop_id);
+ switch (prop_id) {
+#ifdef GST_MP3PARSE_ALP_EXYNOS
+ case PROP_CHECK_ALP_MP3DEC:
+ mp3parse->alp_mp3dec = g_value_get_boolean (value);
+ GST_INFO_OBJECT (mp3parse, "alp_mode_flag(%d)", mp3parse->alp_mp3dec);
+ break;
+#endif
+#ifdef GST_EXT_MP3PARSER_MODIFICATION
+ case PROP_CHECK_HTTP_SEEK:
+ mp3parse->http_seek_flag = g_value_get_boolean (value);
+ GST_INFO_OBJECT (mp3parse, "http_seek_flag(%d)", mp3parse->http_seek_flag);
+ break;
+#endif
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_mpeg_audio_parse_get_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+ GstMpegAudioParse *mp3parse = GST_MPEG_AUDIO_PARSE (object);
+ GST_INFO_OBJECT (mp3parse, "get_property() START- prop_id(%d)",prop_id);
+ switch (prop_id) {
+#ifdef GST_MP3PARSE_ALP_EXYNOS
+ case PROP_CHECK_ALP_MP3DEC:
+ g_value_set_boolean (value, mp3parse->alp_mp3dec);
+ GST_INFO_OBJECT (mp3parse, "alp_mode_flag(%d)", mp3parse->alp_mp3dec);
+ break;
+#endif
+#ifdef GST_EXT_MP3PARSER_MODIFICATION
+ case PROP_CHECK_HTTP_SEEK:
+ g_value_set_boolean (value, mp3parse->http_seek_flag);
+ GST_INFO_OBJECT (mp3parse, "http_seek_flag(%d)", mp3parse->http_seek_flag);
+ break;
+#endif
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
gst_mpeg_audio_parse_finalize (GObject * object)
{
G_OBJECT_CLASS (parent_class)->finalize (object);
gst_base_parse_set_min_frame_size (GST_BASE_PARSE (mp3parse), MIN_FRAME_SIZE);
GST_DEBUG_OBJECT (parse, "starting");
+#ifdef GST_MP3PARSE_ALP_EXYNOS
+ mp3parse->alp_mode_flag = mp3parse->alp_mp3dec;
+#endif
+
gst_mpeg_audio_parse_reset (mp3parse);
+#ifdef GST_MP3PARSE_ALP_EXYNOS
+ if(mp3parse->alp_mode_flag) {
+ gst_base_parse_set_alp_mode(parse, mp3parse->alp_mode_flag);
+ GST_INFO_OBJECT (parse, "=============================");
+ GST_INFO_OBJECT (parse, "starting - ALP MODE");
+ GST_INFO_OBJECT (parse, "=============================");
+ } else {
+ GST_INFO_OBJECT (parse, "-----------------------------");
+ GST_INFO_OBJECT (parse, "starting - Normal MODE");
+ GST_INFO_OBJECT (parse, "-----------------------------");
+ }
+#else
+ GST_INFO_OBJECT (parse, "-----------------------------");
+ GST_INFO_OBJECT (parse, "starting - Normal MODE / No ALP");
+ GST_INFO_OBJECT (parse, "-----------------------------");
+#endif
+
+#ifdef GST_EXT_MP3PARSER_MODIFICATION
+ if (mp3parse->http_seek_flag)
+ GST_INFO_OBJECT (parse, "starting - No Accurate Seek table (in http pull mode)");
+#endif
+
return TRUE;
}
GstMpegAudioParse *mp3parse = GST_MPEG_AUDIO_PARSE (parse);
GST_DEBUG_OBJECT (parse, "stopping");
-
gst_mpeg_audio_parse_reset (mp3parse);
return TRUE;
if (put_crc)
*put_crc = crc;
+#ifdef GST_MP3PARSE_ALP_EXYNOS
+ if (mp3parse->alp_mode_flag) {
+ if (layer == 1)
+ mp3parse->spf = 384;
+ else if (layer == 2)
+ mp3parse->spf = 1152;
+ else if (version == 1) {
+ mp3parse->spf = 1152;
+ } else {
+ /* MPEG-2 or "2.5" */
+ mp3parse->spf = 576;
+ }
+ mp3parse->rate = samplerate;
+ }
+#endif
+
return length;
}
return TRUE;
}
+#ifdef GST_MP3PARSE_ALP_EXYNOS
+static gboolean
+gst_mpeg_audio_parse_check_valid_frame_alp (GstBaseParse * parse,
+ GstBaseParseFrame * frame, guint * framesize, gint * skipsize)
+{
+ GstMpegAudioParse *mp3parse = GST_MPEG_AUDIO_PARSE (parse);
+ GstBuffer *buf = frame->buffer;
+ GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buf);
+ gint off, bpf;
+ gboolean lost_sync, draining, valid, caps_change;
+ guint32 header;
+ guint bitrate, layer, rate, channels, version, mode, crc;
+
+ guint framecount = 0;
+ gint32 offset = 0;
+ guint8 *data = NULL;
+ gint64 buf_szie;
+
+ if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 6))
+ return FALSE;
+
+ gst_base_parse_get_initial_frame(parse, &mp3parse->mp3alp_initialized); //for NEXT Play, I need initial indicatior
+ if(!mp3parse->mp3alp_initialized)
+ GST_INFO_OBJECT (parse, "[FILE_OPEN] baseparse ->framecount is 0");
+
+ off = gst_byte_reader_masked_scan_uint32 (&reader, 0xffe00000, 0xffe00000,
+ 0, GST_BUFFER_SIZE (buf));
+
+ GST_LOG_OBJECT (parse, "possible sync at buffer offset %d", off);
+
+ /* didn't find anything that looks like a sync word, skip */
+ if (off < 0) {
+ *skipsize = GST_BUFFER_SIZE (buf) - 3;
+ return FALSE;
+ }
+
+ /* possible frame header, but not at offset 0? skip bytes before sync */
+ if (off > 0) {
+ *skipsize = off;
+ return FALSE;
+ }
+
+ data= GST_BUFFER_DATA(buf);
+ *framesize = 0;
+
+ while (offset <= ALP_MPEGAUDIOPARSE_BUFFER_SIZE) {
+ /* make sure the values in the frame header look sane */
+ header = GST_READ_UINT32_BE (data);
+ if (!gst_mpeg_audio_parse_head_check (mp3parse, header)) {
+ buf_szie = GST_BUFFER_SIZE (buf) + GST_BUFFER_OFFSET (buf);
+ if (mp3parse->encoded_file_size > 0) {
+ if (buf_szie >= mp3parse->encoded_file_size) {
+ GST_INFO_OBJECT(parse, "BUF SIZE(%" G_GUINT64_FORMAT ") >= TOTAL(%" G_GUINT64_FORMAT ") ", buf_szie, mp3parse->encoded_file_size);
+ break;
+ }
+ } else {
+ GST_WARNING_OBJECT(parse, "filesize is under zero");
+ }
+
+ *skipsize = 1;
+ return FALSE;
+ }
+
+ bpf = mp3_type_frame_length_from_header (mp3parse, header,
+ &version, &layer, &channels, &bitrate, &rate, &mode, &crc);
+ g_assert (bpf != 0);
+
+ if (!mp3parse->mp3alp_initialized) {
+ if (channels != mp3parse->channels || rate != mp3parse->rate ||
+ layer != mp3parse->layer || version != mp3parse->version)
+ caps_change = TRUE;
+ else
+ caps_change = FALSE;
+
+ lost_sync = GST_BASE_PARSE_LOST_SYNC (parse);
+ draining = GST_BASE_PARSE_DRAINING (parse);
+
+ if (!draining && (lost_sync || caps_change)) {
+ if (!gst_mp3parse_validate_extended (mp3parse, buf, header, bpf, draining, &valid)) {
+ /* not enough data */
+ gst_base_parse_set_min_frame_size (parse, valid);
+ *skipsize = 0;
+ return FALSE;
+ } else {
+ if (!valid) {
+ *skipsize = off + 2;
+ return FALSE;
+ }
+ }
+ } else if (draining && lost_sync && caps_change && mp3parse->rate > 0) {
+ /* avoid caps jitter that we can't be sure of */
+ *skipsize = off + 2;
+ return FALSE;
+ }
+
+ mp3parse->mp3alp_frame_1st = 1;
+ if (mp3parse->rate > 0) {
+ mp3parse->mp3alp_frame_duration_float = (gdouble)(mp3parse->spf * 1000 * 1000 ) / (gdouble)mp3parse->rate;
+ }
+ mp3parse->mp3alp_frame_duration = (gint64)(mp3parse->mp3alp_frame_duration_float * 1000); /* duration per frame (msec) 1152/44.1=26122448 */
+
+ gst_base_parse_set_1st_frame(parse, mp3parse->mp3alp_frame_1st, bpf);
+ GST_INFO_OBJECT (parse, "[ONE_TIME] alp_mode_flag(%d) frame_1st(%d) frame_duration (%"GST_TIME_FORMAT")",
+ mp3parse->alp_mode_flag, mp3parse->mp3alp_frame_1st, GST_TIME_ARGS(mp3parse->mp3alp_frame_duration));
+ mp3parse->mp3alp_initialized = TRUE; /*only 1st time */
+ }
+
+ if ((bpf > 0) && (bpf < (ALP_MPEGAUDIOPARSE_BUFFER_SIZE - offset))) {
+ offset += bpf;
+ data += bpf;
+ framecount++;
+ if (mp3parse->mp3alp_frame_1st) {
+ if ((framecount == 10) && (mp3parse->mp3alp_frame_duration > 0)) {
+ mp3parse->mp3alp_frame_1st_bitrate = (8 * offset * GST_SECOND) /(mp3parse->mp3alp_frame_duration * 10);
+ GST_INFO_OBJECT (parse, "[TEST] mp3alp_frame_1st_bitrate (%d)", mp3parse->mp3alp_frame_1st_bitrate);
+ }
+ }
+ } else if (bpf >= (ALP_MPEGAUDIOPARSE_BUFFER_SIZE - offset)) {
+ break;
+ } else {
+ GST_WARNING_OBJECT(parse, "[4F] we lost sync");
+ data++;
+ offset++;
+ }
+ }
+
+ mp3parse->mp3alp_frame_1st = 0;
+ mp3parse->mp3alp_frame_count = framecount;
+ mp3parse->mp3alp_buffer_count++;
+ mp3parse->mp3alp_buffer_duration = mp3parse->mp3alp_frame_duration * framecount;
+ gst_base_parse_set_frame_info(parse, mp3parse->mp3alp_frame_count, mp3parse->mp3alp_frame_duration, mp3parse->mp3alp_frame_1st_bitrate);
+ gst_base_parse_set_buffer_info(parse, mp3parse->mp3alp_buffer_count, mp3parse->mp3alp_buffer_duration);
+ gst_base_parse_set_buffer_size(parse, offset);
+
+ /* restore default minimum */
+ gst_base_parse_set_min_frame_size (parse, MIN_FRAME_SIZE);
+
+ *framesize = offset;
+ return TRUE;
+}
+#endif
+
static gboolean
gst_mpeg_audio_parse_check_valid_frame (GstBaseParse * parse,
GstBaseParseFrame * frame, guint * framesize, gint * skipsize)
guint32 header;
guint bitrate, layer, rate, channels, version, mode, crc;
+#ifdef GST_MP3PARSE_ALP_EXYNOS
+ if (mp3parse->alp_mode_flag) {
+ gboolean ret;
+ ret = gst_mpeg_audio_parse_check_valid_frame_alp(parse, frame, framesize, skipsize);
+ return ret;
+ }
+#endif
if (G_UNLIKELY (GST_BUFFER_SIZE (buf) < 6))
return FALSE;
gst_base_parse_set_min_frame_size (parse, MIN_FRAME_SIZE);
*framesize = bpf;
+ mp3parse->frame_byte = bpf;
return TRUE;
}
if (!gst_pad_query_peer_duration (GST_BASE_PARSE_SINK_PAD (GST_BASE_PARSE
(mp3parse)), &fmt, &upstream_total_bytes))
upstream_total_bytes = 0;
+ GST_INFO_OBJECT (mp3parse, "gst_pad_query_peer_duration -upstream_total_bytes (%"G_GUINT64_FORMAT")", upstream_total_bytes);
+ mp3parse->encoded_file_size = upstream_total_bytes;
if (read_id_xing == xing_id || read_id_xing == info_id) {
guint32 xing_flags;
guint bitrate, layer, rate, channels, version, mode, crc;
g_return_val_if_fail (GST_BUFFER_SIZE (buf) >= 4, GST_FLOW_ERROR);
+ if(GST_BUFFER_SIZE (buf) < 4) {
+ GST_INFO_OBJECT (parse, "_parse_parse_frame() : buffer->size(%d)",GST_BUFFER_SIZE (buf));
+ }
if (!mp3_type_frame_length_from_header (mp3parse,
GST_READ_UINT32_BE (GST_BUFFER_DATA (buf)),
&version, &layer, &channels, &bitrate, &rate, &mode, &crc))
goto broken_header;
+#ifdef GST_EXT_MPEGAUDIO_MODIFICATION
+ /* For Layer-2 there are some combinations of bitrate and mode which are not allowed. */
+ if (version == 1 && layer == 2) {
+ if ((channels == 1 && bitrate >= 224 * 1000) ||
+ (channels == 2 && (bitrate == 32 * 1000 || bitrate == 48 * 1000 || bitrate == 56 * 1000 || bitrate == 80 * 1000))) {
+ GST_ERROR_OBJECT (mp3parse,
+ "Mpeg version %d layer %d channel:%d bitrate:%d spec out!", version, layer, channels, bitrate);
+ goto broken_header;
+ }
+ }
+#endif
+
if (G_UNLIKELY (channels != mp3parse->channels || rate != mp3parse->rate ||
layer != mp3parse->layer || version != mp3parse->version)) {
GstCaps *caps = gst_caps_new_simple ("audio/mpeg",
return res;
}
+
+#ifdef GST_EXT_MP3PARSER_MODIFICATION
+
+/**
+ * gst_mpeg_audio_parse_src_eventfunc:
+ * @parse: #GstBaseParse. #event
+ *
+ * Fast calculation of frame length
+ *
+ * Returns: frame length on success.
+ */
+static guint
+ mp3_type_frame_length_calculation (GstMpegAudioParse *mp3parse, guint32 header)
+ {
+ guint length;
+ guint padding, bitrate, lsf, layer;
+ guint mpg25;
+
+ /* The caller has ensured we have a valid header, so bitrate can't be zero here. */
+ if(header==0) /*case which buffer was filled out.*/
+ {
+ length=0;
+ return length;
+ }
+
+ /* For VBR Xing play seek - GST_MP3PARSE_ALP_SEEK */
+ /* if it's an invalid MPEG version */
+ if (((header >> 19) & 3) == 0x1) {
+ GST_ERROR ("[ERROR] invalid MPEG version: 0x%lx \n", (header >> 19) & 3);
+ return -1;
+ } else {
+ if (header & (1 << 20)) {
+ lsf = (header & (1 << 19)) ? 0 : 1;
+ } else {
+ lsf = 1;
+ }
+ }
+
+ /* if it's an invalid layer */
+ if (!((header >> 17) & 3)) {
+ GST_ERROR ("[ERROR] invalid layer: 0x%lx \n", (header >> 17) & 3);
+ return -2;
+ } else {
+ layer = 4 - ((header >> 17) & 0x3);
+ }
+
+ /* if it's an invalid bitrate */
+ if (((header >> 12) & 0xf) == 0xf) {
+ GST_ERROR ("[ERROR] invalid bitrate: 0x%lx \n", (header >> 12) & 0xf);
+ return -3;
+ } else {
+ bitrate = (header >> 12) & 0xF;
+ mp3parse->hdr_bitrate = mp3types_bitrates[lsf][layer - 1][bitrate] * 1000;
+ /* The caller has ensured we have a valid header, so bitrate can't be zero here. */
+ // g_assert (bitrate != 0);
+ if(mp3parse->hdr_bitrate == 0)
+ return -3;
+ }
+
+ padding = (header >> 9) & 0x1;
+
+ switch (mp3parse->layer) {
+ case 1:
+ length = 4 * ((mp3parse->hdr_bitrate * 12) / mp3parse->rate + padding);
+ break;
+ case 2:
+ length = (mp3parse->hdr_bitrate * 144) / mp3parse->rate + padding;
+ break;
+ default:
+ case 3:
+ if (header & (1 << 20)) {
+ mp3parse->lsf = (header & (1 << 19)) ? 0 : 1;
+ } else {
+ mp3parse->lsf = 1;
+ }
+ length = (mp3parse->hdr_bitrate * 144) / (mp3parse->rate << mp3parse->lsf) + padding;
+ break;
+ }
+
+ return length;
+ }
+
+#ifdef GST_EXT_BASEPARSER_MODIFICATION
+/* perform seek in push based mode:
+ find BYTE position to move to based on time and delegate to upstream
+*/
+static gboolean
+gst_mpeg_audio_parse_do_push_seek (GstBaseParse * parse, GstPad * pad, GstEvent * event)
+{
+ GstMpegAudioParse *mp3parse;
+ mp3parse = GST_MPEG_AUDIO_PARSE (parse);
+
+ gdouble rate;
+ GstFormat format;
+ GstSeekFlags flags;
+ GstSeekType cur_type, stop_type;
+ gint64 cur, stop;
+ gboolean res;
+ gint64 byte_cur;
+ gint64 esimate_byte;
+ gint32 frame_dur;
+
+ GST_INFO_OBJECT (parse, "doing push-based seek");
+
+ gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop);
+
+ /* FIXME, always play to the end */
+ stop = -1;
+ byte_cur = 0;
+
+ /* only forward streaming and seeking is possible */
+ if (rate <= 0)
+ goto unsupported_seek;
+
+ if ( cur == 0 ) {
+ /* handle rewind only */
+ cur_type = GST_SEEK_TYPE_SET;
+ stop_type = GST_SEEK_TYPE_NONE;
+ stop = -1;
+ flags |= GST_SEEK_FLAG_FLUSH;
+ } else {
+ /* handle normal seek */
+ cur_type = GST_SEEK_TYPE_SET;
+ stop_type = GST_SEEK_TYPE_NONE;
+ stop = -1;
+ flags |= GST_SEEK_FLAG_FLUSH;
+
+ esimate_byte = (cur / (1000 * 1000)) * mp3parse->frame_byte;
+ if (mp3parse->rate > 0)
+ frame_dur = (mp3parse->spf * 1000) / mp3parse->rate;
+ else
+ goto unsupported_seek;
+ if (frame_dur > 0)
+ byte_cur = esimate_byte / (frame_dur);
+ else
+ goto unsupported_seek;
+
+ if ( (byte_cur == -1) || (byte_cur > mp3parse->encoded_file_size))
+ {
+ GST_INFO_OBJECT(parse, "[WEB-ERROR] seek cur (%"G_GINT64_FORMAT") > file_size (%"G_GINT64_FORMAT") ", cur, mp3parse->encoded_file_size );
+ goto abort_seek;
+ }
+
+ GST_INFO_OBJECT(parse, "frame_byte(%d) spf(%d) rate (%d) ", mp3parse->frame_byte, mp3parse->spf, mp3parse->rate);
+ GST_INFO_OBJECT(parse, "seek cur (%"G_GINT64_FORMAT") = (%"GST_TIME_FORMAT") ", cur, GST_TIME_ARGS (cur));
+ GST_INFO_OBJECT(parse, "esimate_byte(%"G_GINT64_FORMAT") esimate_byte (%d)", esimate_byte, frame_dur );
+ }
+
+
+ GST_INFO_OBJECT (parse, "Pushing BYTE seek rate %g, " "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur, stop);
+
+ if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
+ GST_INFO_OBJECT (parse, "Requested seek time: %" GST_TIME_FORMAT ", calculated seek offset: %" G_GUINT64_FORMAT, GST_TIME_ARGS (cur), byte_cur);
+ }
+
+ /* BYTE seek event */
+ event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur, stop_type, stop);
+ res = gst_pad_push_event (parse->sinkpad, event);
+
+ return res;
+
+ /* ERRORS */
+abort_seek:
+ {
+ GST_DEBUG_OBJECT (parse, "could not determine byte position to seek to, " "seek aborted.");
+ return FALSE;
+ }
+unsupported_seek:
+ {
+ GST_DEBUG_OBJECT (parse, "unsupported seek, seek aborted.");
+ return FALSE;
+ }
+}
+#endif
+
+/**
+ * gst_mpeg_audio_parse_src_eventfunc:
+ * @parse: #GstBaseParse. #event
+ *
+ * before baseparse handles seek event, make full mp3 index table.
+ *
+ * Returns: TRUE on success.
+ */
+static gboolean
+gst_mpeg_audio_parse_src_eventfunc (GstBaseParse * parse, GstEvent * event)
+{
+ gboolean handled = FALSE;
+ GstMpegAudioParse *mp3parse;
+ mp3parse = GST_MPEG_AUDIO_PARSE (parse);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ {
+ GstFlowReturn res = GST_FLOW_OK;
+ gint64 base_offset = 0, sync_offset = 0, cur = 0;
+ gint32 frame_count = 1; /* do not add first frame because it is already in index table */
+ guint64 second_count = 0; /* initial 1 second */
+ gint64 total_file_size = 0, start_offset = 0;
+ GstClockTime current_ts = GST_CLOCK_TIME_NONE;
+ GstActivateMode pad_mode = GST_ACTIVATE_NONE;
+ gboolean large_file_flag = FALSE;
+
+ GST_DEBUG("gst_mpeg_audio_parse_src_eventfunc GST_EVENT_SEEK enter");
+
+#ifdef GST_EXT_BASEPARSER_MODIFICATION /* check baseparse define these fuction */
+ gst_base_parse_get_pad_mode(parse, &pad_mode);
+ if (pad_mode != GST_ACTIVATE_PULL) {
+ gboolean ret = FALSE;
+ GST_INFO_OBJECT (mp3parse, "mp3 parser is PUSH MODE.");
+ GstPad* srcpad = gst_element_get_pad(parse, "src");
+ /* check NULL */
+ ret = gst_mpeg_audio_parse_do_push_seek(parse, srcpad, event);
+ gst_object_unref(srcpad);
+ return ret;
+ }
+ gst_base_parse_get_upstream_size(parse, &total_file_size);
+ gst_base_parse_get_index_last_offset(parse, &start_offset);
+ gst_base_parse_get_index_last_ts(parse, ¤t_ts);
+
+ if (mp3parse->http_seek_flag) {
+ GST_INFO_OBJECT (mp3parse, "souphttpsrc is PULL MODE (so accurate seek mode is OFF)");
+ gst_base_parse_set_seek_mode(parse, 0);
+ goto mp3_seek_null_exit;
+ }
+
+ if (total_file_size > MP3_LARGE_FILE_SIZE ) {
+ large_file_flag = TRUE;
+ gst_base_parse_set_seek_mode(parse, 0);
+ GST_INFO_OBJECT (mp3parse, "larger than big size (50MB)");
+ goto mp3_seek_null_exit;
+ }
+
+ if (mp3parse->encoded_file_size > 0) {
+ GST_INFO_OBJECT(parse, "[SEEK] total_file_size (%"G_GINT64_FORMAT")= encoded_file_size(%"G_GINT64_FORMAT")+ ID3(%"G_GINT64_FORMAT")",
+ total_file_size, mp3parse->encoded_file_size, (total_file_size - mp3parse->encoded_file_size) );
+ total_file_size = mp3parse->encoded_file_size;
+ } else {
+ GST_INFO_OBJECT(parse, "[SEEK] encoded_file_siz (%"G_GINT64_FORMAT") is WRONG", mp3parse->encoded_file_size );
+ }
+#else
+ GST_ERROR("baseparser does not define get private param functions. can not make index table here.");
+ break;
+#endif
+
+ if (total_file_size == 0 || start_offset >= total_file_size) {
+ GST_ERROR("last index offset (%"G_GUINT64_FORMAT") is larger than file size (%"G_GUINT64_FORMAT")", start_offset, total_file_size);
+ break;
+ }
+
+ gst_event_parse_seek (event, NULL, NULL, NULL, NULL, &cur, NULL, NULL);
+ if (cur <= current_ts) {
+ GST_INFO("seek to %"GST_TIME_FORMAT" within index table %"GST_TIME_FORMAT". do not make index table",
+ GST_TIME_ARGS(cur), GST_TIME_ARGS(current_ts));
+ break;
+ } else {
+ GST_INFO("seek to %"GST_TIME_FORMAT" without index table %"GST_TIME_FORMAT". make index table",
+ GST_TIME_ARGS(cur), GST_TIME_ARGS(current_ts));
+ }
+
+ GST_INFO("make MP3 Index Table. file_size = %"G_GINT64_FORMAT" last idx offset=%"G_GINT64_FORMAT
+ ", last idx ts=%"GST_TIME_FORMAT, total_file_size, start_offset, GST_TIME_ARGS(current_ts));
+
+ base_offset = start_offset; /* set base by start offset */
+ second_count = current_ts + GST_SECOND; /* 1sec = (1000*1000*1000) */
+
+ /************************************/
+ /* STEP 0: Initialize parse informaion */
+ /************************************/
+ if ((mp3parse->layer > 0) && (mp3parse->rate > 0) &&
+ (mp3parse->hdr_bitrate > 0) &&(mp3parse->channels > 0)) {
+ if (mp3parse->layer == 1)
+ mp3parse->spf = 384;
+ else if (mp3parse->layer == 2)
+ mp3parse->spf = 1152;
+ else if (mp3parse->version == 1) {
+ mp3parse->spf = 1152;
+ } else {
+ /* MPEG-2 or "2.5" */
+ mp3parse->spf = 576;
+ }
+
+ mp3parse->frame_duration = (mp3parse->spf * GST_MSECOND) / mp3parse->rate; /* duration per frame (msec) */
+ mp3parse->frame_per_sec = (mp3parse->rate) / mp3parse->spf; /* frames per second (ea) */
+ } else {
+ GST_WARNING("[CEHCK UP] we must need 'mp3parse->xxxx' information ");
+ }
+
+ /************************************/
+ /* STEP 1: MAX_PULL_RANGE_BUF cycle */
+ /************************************/
+ while (total_file_size - base_offset >= MP3_MAX_PULL_RANGE_BUF) {
+ gint64 offset = 0;
+ GstBuffer *buffer = NULL;
+ guint8 *buf = NULL;
+
+ GST_INFO("gst_pad_pull_range %d bytes (from %"G_GINT64_FORMAT") use max size", MP3_MAX_PULL_RANGE_BUF, base_offset);
+ res = gst_pad_pull_range (parse->sinkpad, base_offset, base_offset + MP3_MAX_PULL_RANGE_BUF, &buffer);
+ if (res != GST_FLOW_OK) {
+ GST_ERROR ("gst_pad_pull_range failed!");
+ break;
+ }
+
+ buf = GST_BUFFER_DATA(buffer);
+ if (buf == NULL) {
+ GST_WARNING("buffer is NULL in make mp3 seek table's STEP1");
+ gst_buffer_unref (buffer);
+ goto mp3_seek_null_exit;
+ }
+
+ while (offset <= MP3_MAX_PULL_RANGE_BUF) {
+ gint frame_size = 0;
+ guint32 header;
+
+ /* make sure the values in the frame header look sane */
+ header = GST_READ_UINT32_BE (buf);
+ frame_size = mp3_type_frame_length_calculation (mp3parse, header);
+
+ if ((frame_size > 0) && (frame_size < (MP3_MAX_PULL_RANGE_BUF - offset))) {
+ gst_base_parse_add_index_entry (parse, base_offset +offset, current_ts, TRUE, TRUE); /* force */
+ GST_DEBUG("Adding index ts=%"GST_TIME_FORMAT" offset %"G_GINT64_FORMAT, GST_TIME_ARGS(current_ts), base_offset + offset);
+ current_ts += mp3parse->frame_duration * GST_USECOND; /* each frame is (frame_duration) ms */
+ offset += frame_size;
+ buf += frame_size;
+ frame_count++;
+ } else if (frame_size >= (MP3_MAX_PULL_RANGE_BUF - offset)) {
+ GST_DEBUG("we need refill buffer");
+ break;
+ } else {
+ if (frame_size == 0) {
+ GST_WARNING("frame size is 0 , decoding end");
+ goto mp3_seek_null_exit;
+ }
+ GST_WARNING("we lost sync");
+ buf++;
+ offset++;
+ }
+ } /* while */
+ base_offset = base_offset + offset;
+ gst_buffer_unref (buffer);
+ } /* end MAX buffer cycle */
+
+ /*******************************/
+ /* STEP 2: Remain Buffer cycle */
+ /*******************************/
+ if (total_file_size - base_offset > 0) {
+ gint64 offset = 0;
+ GstBuffer *buffer = NULL;
+ guint8 *buf = NULL;
+
+ GST_INFO("gst_pad_pull_range %"G_GINT64_FORMAT" bytes (from %"G_GINT64_FORMAT") use remain_buf size",
+ total_file_size - base_offset, base_offset);
+ res = gst_pad_pull_range (parse->sinkpad, base_offset, total_file_size, &buffer);
+ if (res != GST_FLOW_OK) {
+ GST_ERROR ("gst_pad_pull_range failed!");
+ break;
+ }
+
+ buf = GST_BUFFER_DATA(buffer);
+ if (buf == NULL) {
+ GST_WARNING("buffer is NULL in make mp3 seek table's STEP2");
+ gst_buffer_unref (buffer);
+ goto mp3_seek_null_exit;
+ }
+
+ while (base_offset + offset < total_file_size) {
+ gint frame_size = 0;
+ guint32 header;
+
+ /* make sure the values in the frame header look sane */
+ header = GST_READ_UINT32_BE (buf);
+ frame_size = mp3_type_frame_length_calculation (mp3parse, header);
+
+ if ((frame_size > 0) && (frame_size <= (total_file_size - (base_offset + offset)))) {
+ gst_base_parse_add_index_entry (parse, base_offset +offset, current_ts, TRUE, TRUE); /* force */
+ GST_DEBUG("Adding index ts=%"GST_TIME_FORMAT" offset %"G_GINT64_FORMAT, GST_TIME_ARGS(current_ts), base_offset + offset);
+ current_ts += mp3parse->frame_duration * GST_USECOND; /* each frame is (frame_duration) ms */
+ offset += frame_size;
+ buf += frame_size;
+ frame_count++;
+ } else if (frame_size == 0) {
+ GST_DEBUG("frame size is 0 , decoding end");
+ break;
+ } else {
+ GST_WARNING("we lost sync");
+ buf++;
+ offset++;
+ }
+ } /* while */
+
+ gst_buffer_unref (buffer);
+ } /* end remain_buf buffer cycle */
+
+ GST_DEBUG("gst_mpeg_audio_parse_src_eventfunc GST_EVENT_SEEK leave");
+ }
+ break;
+
+ default:
+ break;
+ }
+
+mp3_seek_null_exit:
+ /* call baseparse src_event function to handle event */
+ handled = GST_BASE_PARSE_CLASS (parent_class)->src_event (parse, event);
+
+ return handled;
+}
+#endif
\ No newline at end of file
#define GST_IS_MPEG_AUDIO_PARSE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_MPEG_AUDIO_PARSE))
+#define GST_EXT_MP3PARSER_MODIFICATION
+#define GST_MP3PARSE_ALP_EXYNOS
+
typedef struct _GstMpegAudioParse GstMpegAudioParse;
typedef struct _GstMpegAudioParseClass GstMpegAudioParseClass;
/* samples per frame */
gint spf;
+ /* added for make seek table */
+ guint32 lsf;
+ guint32 frame_duration;
+ guint32 frame_per_sec;
+ gint64 encoded_file_size;
+ guint32 frame_byte;
+
gboolean sent_codec_tag;
guint last_posted_bitrate;
gint last_posted_crc, last_crc;
/* LAME info */
guint32 encoder_delay;
guint32 encoder_padding;
+
+#ifdef GST_MP3PARSE_ALP_EXYNOS
+ gboolean alp_mp3dec;
+ gboolean alp_mode_flag;
+ gboolean mp3alp_initialized;
+ /* ALP - for frame number */
+ guint32 mp3alp_frame_1st;
+ guint32 mp3alp_frame_count;
+ guint32 mp3alp_buffer_count;
+ gint64 mp3alp_frame_duration;
+ gint64 mp3alp_buffer_duration;
+ gdouble mp3alp_frame_duration_float;
+ guint32 mp3alp_frame_1st_bitrate;
+#endif
+
+#ifdef GST_EXT_MP3PARSER_MODIFICATION
+ gboolean http_seek_flag;
+#endif
};
/**
GST_RANK_PRIMARY + 1, GST_TYPE_AMR_PARSE);
ret &= gst_element_register (plugin, "ac3parse",
GST_RANK_PRIMARY + 1, GST_TYPE_AC3_PARSE);
+#ifdef GST_EXT_ENABLE_PARSER_MODIFICATION // should be disable in mobile because not supported codec
ret &= gst_element_register (plugin, "dcaparse",
GST_RANK_PRIMARY + 1, GST_TYPE_DCA_PARSE);
+#endif
ret &= gst_element_register (plugin, "flacparse",
GST_RANK_PRIMARY + 1, GST_TYPE_FLAC_PARSE);
ret &= gst_element_register (plugin, "mpegaudioparse",
#define ENTRY_SET_KEYFRAME(e) ((e)->flags = GST_AVI_KEYFRAME)
#define ENTRY_UNSET_KEYFRAME(e) ((e)->flags = 0)
+#ifdef AVIDEMUX_MODIFICATION
+enum
+{
+ PROP_0,
+ PROP_PLAYBACK_PROTECTION,
+};
+#endif
GST_DEBUG_CATEGORY_STATIC (avidemux_debug);
#define GST_CAT_DEFAULT avidemux_debug
static void gst_avidemux_forward_trickplay (GstAviDemux * avi, GstAviStream * stream, guint64 *timestamp);
static void gst_avidemux_backward_trickplay (GstAviDemux * avi, GstAviStream * stream, guint64 *timestamp);
static GstFlowReturn gst_avidemux_seek_to_previous_keyframe (GstAviDemux *avi);
+static void gst_avidemux_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_avidemux_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
#endif
static GstElementClass *parent_class = NULL;
parent_class = g_type_class_peek_parent (klass);
gobject_class->finalize = gst_avi_demux_finalize;
+
+#ifdef AVIDEMUX_MODIFICATION
+ gobject_class->set_property = gst_avidemux_set_property;
+ gobject_class->get_property = gst_avidemux_get_property;
+#endif
+
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_avi_demux_change_state);
gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_avi_demux_set_index);
gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_avi_demux_get_index);
+#ifdef AVIDEMUX_MODIFICATION
+ 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
+
#ifdef DIVX_DRM
gst_tag_register ("drm_divx", GST_TAG_FLAG_META,
G_TYPE_STRING,
#endif
}
+#ifdef AVIDEMUX_MODIFICATION
+static void
+gst_avidemux_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstAviDemux *avi = GST_AVI_DEMUX (object);
+ switch (prop_id) {
+ case PROP_PLAYBACK_PROTECTION:
+ {
+ avi->playback_protected = g_value_get_boolean(value);
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_avidemux_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstAviDemux *avi = GST_AVI_DEMUX (object);
+
+ switch (prop_id) {
+ case PROP_PLAYBACK_PROTECTION:
+ {
+ g_value_set_boolean(value, avi->playback_protected);
+ break;
+ }
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+#endif
+
static void
gst_avi_demux_init (GstAviDemux * avi)
{
gst_element_add_pad (GST_ELEMENT_CAST (avi), avi->sinkpad);
avi->adapter = gst_adapter_new ();
-
+#ifdef AVIDEMUX_MODIFICATION
+ avi->playback_protected = FALSE;
+#endif
gst_avi_demux_reset (avi);
}
GST_DEBUG_OBJECT (query, "total frames is %" G_GUINT32_FORMAT,
stream->idx_n);
- if (stream->idx_n >= 0)
- gst_query_set_duration (query, fmt, stream->idx_n);
- else if (gst_pad_query_convert (pad, GST_FORMAT_TIME,
- duration, &fmt, &dur))
- gst_query_set_duration (query, fmt, dur);
+ gst_query_set_duration (query, fmt, stream->idx_n);
break;
}
default:
for (i = 0; i < avi->num_streams; i++) {
avi->stream[i].last_flow = GST_FLOW_OK;
avi->stream[i].discont = TRUE;
+ avi->stream[i].sent_eos = FALSE;
}
/* fall through to default case so that the event gets passed downstream */
}
{
GST_ERROR_OBJECT (avi, "invalid index parameters (num = %d, bpe = %d)",
num, bpe);
- if (buf)
- gst_buffer_unref (buf);
+ gst_buffer_unref (buf);
return FALSE;
}
}
GST_DEBUG_OBJECT (avi, " version %d", ((gst_riff_strd*)GST_BUFFER_DATA(buf))->version);
GST_DEBUG_OBJECT (avi, " drm_size %d", ((gst_riff_strd*)GST_BUFFER_DATA(buf))->drm_size);
+ if(avi->playback_protected) {
+ gst_buffer_unref (buf);
+ GST_ERROR_OBJECT (avi, "Trusted OPL violation error");
+ GST_ELEMENT_ERROR (avi, STREAM, DECRYPT_NOKEY, ("Trusted opl violation error"), (NULL));
+ return FALSE;
+ }
+
return gst_avi_demux_init_divx_drm (avi, GST_BUFFER_DATA(buf)+sizeof(gst_riff_strd));
/* ERRORS */
stream->is_vbr = (stream->strh->samplesize == 0)
&& stream->strh->scale > 1
&& stream->strf.auds->blockalign != 1;
+#ifdef AVIDEMUX_MODIFICATION
+ /* This is processing for exception condition.
+ * samplesize is not 0, but is_vbr must become true. */
+ if(stream->strf.auds->format == GST_RIFF_WAVE_FORMAT_AAC) {
+ if (stream->strh->scale > 1 && stream->strf.auds->blockalign != 1)
+ stream->is_vbr = TRUE;
+ }
+#endif
GST_DEBUG_OBJECT (element, "marking audio as VBR:%d, res %d",
stream->is_vbr, res);
/* we need these or we have no way to come up with timestamps */
g_free (vprp);
vprp = NULL;
}
+#ifdef AVIDEMUX_MODIFICATION
+ gst_caps_set_simple (caps, "ts-linear", G_TYPE_BOOLEAN, TRUE, NULL);
+#endif
tag_name = GST_TAG_VIDEO_CODEC;
avi->num_v_streams++;
break;
stream->idx_n = 0;
stream->idx_max = 0;
+ stream->sent_eos = FALSE;
gst_pad_set_element_private (pad, stream);
avi->num_streams++;
GstAviStream * stream, guint64 time)
{
guint index = -1;
- guint64 total;
+ guint64 total = 0;
GST_LOG_OBJECT (avi, "search time:%" GST_TIME_FORMAT, GST_TIME_ARGS (time));
gst_riff_index_entry *index;
GstClockTime stamp;
GstAviStream *stream;
- GstAviIndexEntry entry;
+ GstAviIndexEntry entry = {0};
guint32 id;
if (!buf)
tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
size = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf) + 4);
if (tag == GST_RIFF_TAG_LIST) {
+
+#ifdef AVIDEMUX_MODIFICATION
+ /* check for multiple list tag */
+ do {
+ GstBuffer *temp_buf;
+ guint32 temp_tag;
+ guint32 temp_size;
+ res = gst_pad_pull_range (avi->sinkpad, offset + 20, 8, &temp_buf);
+ if (res != GST_FLOW_OK)
+ goto pull_failed;
+ else if (GST_BUFFER_SIZE (buf) < 8)
+ goto too_small;
+ /* check tag first before blindy trying to read 'size' bytes */
+ temp_tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (temp_buf));
+ temp_size = GST_READ_UINT32_LE (GST_BUFFER_DATA (temp_buf) + 4);
+ if (temp_tag == GST_RIFF_TAG_LIST) {
+ GST_WARNING_OBJECT (avi, "there are multiple list tags in the file, so skipping them");
+ gst_buffer_unref (buf);
+ buf = temp_buf;
+ tag = temp_tag;
+ size = temp_size;
+ offset += 20;
+ avi->offset += 20;
+ }
+ else
+ break;
+ } while (1);
+#endif
+
/* this is the movi tag */
GST_DEBUG_OBJECT (avi, "skip LIST chunk, size %" G_GUINT32_FORMAT,
(8 + GST_ROUND_UP_2 (size)));
{
gboolean result = FALSE;
gint i;
+ GstEventType etype = GST_EVENT_TYPE (event);
GST_DEBUG_OBJECT (avi, "sending %s event to %d streams",
GST_EVENT_TYPE_NAME (event), avi->num_streams);
GstAviStream *stream = &avi->stream[i];
if (stream->pad) {
+ if (etype == GST_EVENT_EOS) {
+ /* let's not send twice */
+ if (stream->sent_eos) {
+ result = TRUE;
+ continue;
+ }
+ GST_DEBUG_OBJECT(avi, "Sending EOS for stream %d", i);
+ stream->sent_eos = TRUE;
+ }
result = TRUE;
gst_pad_push_event (stream->pad, gst_event_ref (event));
}
stream->start_entry = index;
stream->step_entry = index;
stream->stop_entry = gst_avi_demux_index_last (avi, stream);
+ stream->sent_eos = FALSE;
}
if (stream->current_entry != index) {
GST_DEBUG_OBJECT (avi, "Move DISCONT from %u to %u",
GstFormat format;
GstSeekFlags flags;
GstSeekType cur_type = GST_SEEK_TYPE_NONE, stop_type;
- gint64 cur, stop;
+ gint64 cur = 0, stop;
gboolean flush;
gboolean update;
GstSegment seeksegment = { 0, };
/* we have the stream now */
stream = &avi->stream[stream_num];
+ if ((stream->current_entry + 1) > stream->stop_entry) {
+ if (stream->sent_eos == FALSE) {
+ if(avi->segment.applied_rate > 0.0)
+ {
+ GstEvent *event = NULL;
+ event = gst_event_new_new_segment_full (FALSE,
+ avi->segment.rate, avi->segment.applied_rate, avi->segment.format,
+ stream->hdr_duration, stream->hdr_duration, avi->segment.time);
+ gst_pad_push_event (stream->pad, event);
+ }
+
+ GST_DEBUG_OBJECT(avi, "Sending EOS for stream %d", stream_num);
+ gst_pad_push_event(stream->pad, gst_event_new_eos ());
+ stream->sent_eos = TRUE;
+ stream->last_flow = GST_FLOW_UNEXPECTED;
+ continue;
+ }
+ }
+
/* skip streams without pads */
if (!stream->pad) {
GST_DEBUG_OBJECT (avi, "skipping entry from stream %d without pad",
if (avi->segment.rate > 0.0) {
/* only check this for fowards playback for now */
+#ifdef AVIDEMUX_MODIFICATION
+ if (GST_CLOCK_TIME_IS_VALID (avi->segment.stop)
+ && (timestamp > avi->segment.stop)) {
+#else
if (keyframe && GST_CLOCK_TIME_IS_VALID (avi->segment.stop)
&& (timestamp > avi->segment.stop)) {
+#endif
goto eos_stop;
}
}
GstTagList *taglist;
gint index_id;
+ gboolean sent_eos;
} GstAviStream;
typedef enum {
gint index_id;
gboolean seekable;
+ //playback protection
+ gboolean playback_protected;
+
#ifdef DIVX_DRM
uint8_t* drmContext;
void *divx_handle;
/* Combine them */
pts |= pts_ext << 24;
+ if((pts * GST_MSECOND) < demux->segment.last_stop)
+ {
+ return GST_FLOW_OK;
+ }
+
GST_LOG_OBJECT (demux, "pts bytes %02X %02X %02X %02X (%d)", data[0], data[1],
data[2], data[3], pts);
demux->video_need_discont = FALSE;
}
- gst_segment_set_last_stop (&demux->segment, GST_FORMAT_TIME,
- GST_BUFFER_TIMESTAMP (outbuf));
-
/* Do we need a newsegment event ? */
if (G_UNLIKELY (demux->video_need_segment)) {
if (demux->close_seg_event)
"partial pull got %d when expecting %d from offset %" G_GUINT64_FORMAT,
GST_BUFFER_SIZE (*buffer), size, offset);
gst_buffer_unref (*buffer);
- ret = GST_FLOW_UNEXPECTED;
+ ret = GST_FLOW_NOT_SUPPORTED;
*buffer = NULL;
return ret;
}
static gint64
gst_flv_demux_get_metadata (GstFlvDemux * demux)
{
- gint64 ret = 0, offset;
+ gint64 ret = 0, offset = 0;
GstFormat fmt = GST_FORMAT_BYTES;
size_t tag_size, size;
GstBuffer *buffer = NULL;
GST_TIME_ARGS (segment->last_stop), GST_TIME_ARGS (time), bytes);
/* Key frame seeking */
- if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
+#ifndef FLVDEMUX_MODIFICATION
+ if (segment->flags & GST_SEEK_FLAG_KEY_UNIT)
+#endif
+ {
/* Adjust the segment so that the keyframe fits in */
if (time < segment->start) {
segment->start = segment->time = time;
}
- segment->last_stop = time;
}
} else {
GST_DEBUG_OBJECT (demux, "no index entry found for %" GST_TIME_FORMAT,
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FLV_DEMUX))
#define GST_IS_FLV_DEMUX_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FLV_DEMUX))
+
+#define FLVDEMUX_MODIFICATION
+
typedef struct _GstFlvDemux GstFlvDemux;
typedef struct _GstFlvDemuxClass GstFlvDemuxClass;
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@ \
-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 \
#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')
#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')
#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_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')
#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 */
#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)
#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
#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
{
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
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 */
*
* 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
{
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;
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;
GstByteReader stsc;
GstByteReader stts;
GstByteReader stss;
- GstByteReader stps;
GstByteReader ctts;
gboolean chunks_are_chunks;
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;
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
};
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,
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
"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",
"(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
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;
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;
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
}
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;
}
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;
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;
("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)
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;
}
}
}
+#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
{
gint64 desired_offset;
gint n;
+#ifdef QTDEMUX_MODIFICATION
+ guint64 min_moof_offset = -1;
+ gboolean bret = TRUE;
+#endif
desired_offset = segment->last_stop;
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;
GstFormat format;
GstSeekFlags flags;
GstSeekType cur_type, stop_type;
- gint64 cur, stop;
+ gint64 cur = 0, stop;
gboolean flush;
gboolean update;
GstSegment seeksegment;
}
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,
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)
"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");
}
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 */
demux->streams[i]->last_ret = GST_FLOW_OK;
demux->streams[i]->sent_eos = FALSE;
}
+#endif
break;
}
case GST_EVENT_EOS:
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;
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)
{
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);
}
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;
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;
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;
qtdemux->seek_offset = 0;
qtdemux->upstream_seekable = FALSE;
qtdemux->upstream_size = 0;
+#endif
break;
}
default:
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
}
}
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) {
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 {
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 =
/* 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;
return TRUE;
}
}
-
+#ifdef QTDEMUX_MODIFICATION
static gboolean
-qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
- guint64 moof_offset, QtDemuxStream * stream)
+qtdemux_parse_sample_encryption(GstQTDemux * qtdemux, GstByteReader *sample_encrypt, QtDemuxStream * stream)
{
- GNode *moof_node, *traf_node, *tfhd_node, *trun_node;
- GstByteReader trun_data, tfhd_data;
- guint32 ds_size = 0, ds_duration = 0, ds_flags = 0;
- gint64 base_offset, running_offset;
+ 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;
+ }
- /* NOTE @stream ignored */
+ /* Get sample count*/
+ if (!gst_byte_reader_get_uint32_be (sample_encrypt, &sample_count))
+ goto invalid_encryption;
- moof_node = g_node_new ((guint8 *) buffer);
- qtdemux_parse_node (qtdemux, moof_node, buffer, length);
- qtdemux_node_dump (qtdemux, moof_node);
+ GST_INFO_OBJECT (qtdemux, "Sample count = %d", sample_count);
- /* 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 */
- tfhd_node =
- qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_tfhd,
- &tfhd_data);
- if (!tfhd_node)
- goto missing_tfhd;
- if (!qtdemux_parse_tfhd (qtdemux, &tfhd_data, &stream, &ds_duration,
- &ds_size, &ds_flags, &base_offset))
- goto missing_tfhd;
- 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 */
- base_offset = -2;
- goto next;
+#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;
}
- if (G_UNLIKELY (base_offset < -1))
- goto lost_offset;
- /* Track Run node */
- trun_node =
- qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
- &trun_data);
- while (trun_node) {
- qtdemux_parse_trun (qtdemux, &trun_data, stream,
- ds_duration, ds_size, ds_flags, moof_offset, length, &base_offset,
- &running_offset);
- /* iterate all siblings */
- trun_node = qtdemux_tree_get_sibling_by_type_full (trun_node, FOURCC_trun,
- &trun_data);
+ 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++;
}
- /* if no new base_offset provided for next traf,
- * base is end of current traf */
- base_offset = running_offset;
- running_offset = -1;
- next:
- /* iterate all siblings */
- traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
- }
- g_node_destroy (moof_node);
- return TRUE;
-missing_tfhd:
+#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, *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);
+#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 */
+ base_offset = -2;
+ goto next;
+ }
+ if (G_UNLIKELY (base_offset < -1))
+ goto lost_offset;
+ /* Track Run node */
+ trun_node =
+ qtdemux_tree_get_child_by_type_full (traf_node, FOURCC_trun,
+ &trun_data);
+ while (trun_node) {
+ 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;
+ running_offset = -1;
+ next:
+ /* iterate all siblings */
+ traf_node = qtdemux_tree_get_sibling_by_type (traf_node, FOURCC_traf);
+ }
+ g_node_destroy (moof_node);
+ return TRUE;
+
+missing_tfhd:
{
GST_DEBUG_OBJECT (qtdemux, "missing tfhd box");
goto 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));
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 */
}
}
-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;
}
}
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");
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) {
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) {
"mfra atom expected at offset %" G_GUINT64_FORMAT, mfra_offset);
qtdemux->mfra_offset = mfra_offset;
}
+
+ return ret;
}
#endif
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))
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,
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;
}
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;
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;
}
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;
}
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 */
#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
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;
/* 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;
}
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;
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)),
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)) {
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:
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
#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
"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;
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
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 */
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");
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);
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;
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");
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;
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);
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) {
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:
{
offset = 0x24;
break;
case 1:
- offset = 0x34;
+ offset = 0x24;
break;
case 2:
offset = 0x48;
guint8 *buffer;
guint32 child_fourcc, child_len;
- for (child = g_node_next_sibling (node); child;
- child = g_node_next_sibling (child)) {
- buffer = (guint8 *) child->data;
+ for (child = g_node_next_sibling (node); child;
+ child = g_node_next_sibling (child)) {
+ buffer = (guint8 *) child->data;
+
+ child_fourcc = QT_FOURCC (buffer + 4);
+
+ if (child_fourcc == fourcc) {
+ if (parser) {
+ child_len = QT_UINT32 (buffer);
+ if (G_UNLIKELY (child_len < (4 + 4)))
+ return NULL;
+ /* FIXME: must verify if atom length < parent atom length */
+ gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
+ }
+ return child;
+ }
+ }
+ return NULL;
+}
+
+static GNode *
+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;
+ }
- child_fourcc = QT_FOURCC (buffer + 4);
- if (child_fourcc == fourcc) {
- if (parser) {
- child_len = QT_UINT32 (buffer);
- if (G_UNLIKELY (child_len < (4 + 4)))
- return NULL;
- /* FIXME: must verify if atom length < parent atom length */
- gst_byte_reader_init (parser, buffer + (4 + 4), child_len - (4 + 4));
- }
- return child;
+ 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;
}
- return NULL;
-}
-
-static GNode *
-qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc)
-{
- return qtdemux_tree_get_sibling_by_type_full (node, fourcc, NULL);
+done:
+ return TRUE;
}
-
+#else
+/* Deprecated */
static gboolean
gst_qtdemux_add_stream (GstQTDemux * qtdemux,
QtDemuxStream * stream, GstTagList * list)
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 =
done:
return TRUE;
}
-
+#endif
/* find next atom with @fourcc starting at @offset */
static GstFlowReturn
qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset,
}
/* 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) {
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 */
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) {
(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;
/* 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;
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,
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.
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;
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))
/* 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;
* 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 "
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) {
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;
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);
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:
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);
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);
}
}
}
} 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);
}
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;
stream->sampled = TRUE;
}
- /* collect sample information */
- if (!qtdemux_stbl_init (qtdemux, stream, stbl))
- goto samples_failed;
+ /* collect sample information */
+#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;
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;
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,
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)
{
return ret;
}
+#endif
/* check if major or compatible brand is 3GP */
static inline gboolean
qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major)
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";
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;
}
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
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) {
/* 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);
/* 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);
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;
}
}
}
#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);
"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:
{
}
}
+#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);
/* 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])) /
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
#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
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;
guint32 timescale;
guint64 duration;
-
gboolean fragmented;
/* offset of the mfra atom */
guint64 mfra_offset;
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
};
}
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;
}
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))
}
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;
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,
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,
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);
#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')
#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')
#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')
#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')
#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')
#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__ */
{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 */
{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},
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,
{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,},
{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},
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,},
};
static gint32 gst_matroska_demux_trickplay_nframes2show_bw_keyframes (GstMatroskaDemux* demux, GstMatroskaTrackContext * stream);
static GstFlowReturn gst_matroska_demux_backward_trickplay (GstMatroskaDemux* demux, GstMatroskaTrackContext * stream, GstBuffer* sub);
static GstFlowReturn gst_matroska_demux_forward_trickplay (GstMatroskaDemux* demux, GstMatroskaTrackContext * stream, GstBuffer *buffer, gboolean *skip);
+static gboolean gst_matroska_demux_create_index_table(GstMatroskaDemux* demux);
#endif
GType gst_matroska_demux_get_type (void);
demux->is_eos_blockgroup = FALSE;
demux->is_eos_simpleblock = FALSE;
demux->video_keyframe_pushed = FALSE;
+ demux->seek_head_cluster_info_absent = FALSE;
+ demux->seek_head_cue_info_absent = FALSE;
+ demux->index_table_created = TRUE;
+ demux->index_table_array_creation = FALSE;
+ demux->first_index_table_creation = TRUE;
+ demux->Subtitle_language_list = NULL;
#endif
/* property defaults */
}
demux->invalid_duration = FALSE;
+#ifdef MKV_DEMUX_MODIFICATION
+ if (demux->Subtitle_language_list) {
+ g_list_free (demux->Subtitle_language_list);
+ demux->Subtitle_language_list = NULL;
+ }
+#endif
}
static GstBuffer *
lang = gst_tag_get_language_code (context->language);
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
GST_TAG_LANGUAGE_CODE, (lang) ? lang : context->language, NULL);
+
+#ifdef MKV_DEMUX_MODIFICATION
+ if (context->type == GST_MATROSKA_TRACK_TYPE_SUBTITLE) {
+ GstMatroskaLanguageStruct* new = NULL;
+ new = g_new0 (GstMatroskaLanguageStruct, 1);
+ new->language_code = (lang ? lang : context->language);
+ new->language_key = (lang ? lang : context->language);
+ new->active = FALSE;
+ demux->Subtitle_language_list = g_list_append (demux->Subtitle_language_list, new);
+ }
+#endif
}
if (caps == NULL) {
GST_OBJECT_UNLOCK (demux);
return FALSE;
}
+#ifdef MKV_DEMUX_MODIFICATION
+ } else {
+ GST_INFO_OBJECT (demux, "Entry Details are");
+ GST_INFO_OBJECT (demux, "entry->block %u", (guint)entry->block);
+ GST_INFO_OBJECT (demux, "entry->pos %"G_GUINT64_FORMAT, entry->pos);
+ GST_INFO_OBJECT (demux, "entry->time %"GST_TIME_FORMAT, GST_TIME_ARGS(entry->time));
+ GST_INFO_OBJECT (demux, "entry->track %u", (guint)entry->track);
+#endif
}
#ifdef MKV_DEMUX_MODIFICATION
- if (entry == NULL)
+ if (entry == NULL) {
+ GST_OBJECT_UNLOCK (demux);
return FALSE;
+ }
if (seeksegment.rate < 0.0) {
GST_INFO("Current Index is %"GST_TIME_FORMAT, GST_TIME_ARGS(entry->time));
next_entry = gst_matroska_demux_get_next_index (demux, track, entry);
if (next_entry == NULL) {
GST_ERROR ("Entry Not found....");
+ GST_OBJECT_UNLOCK (demux);
return FALSE;
}
GST_INFO("Next Index is %"GST_TIME_FORMAT, GST_TIME_ARGS(next_entry->time));
seeksegment.start = cur;
seeksegment.time = cur;
seeksegment.last_stop = cur;
- seeksegment.stop = seeksegment.duration;
+ //seeksegment.stop = seeksegment.duration;
+ GST_DEBUG_OBJECT (demux, "New segment %" GST_SEGMENT_FORMAT, &seeksegment);
} else if (seeksegment.rate < 0.0) {/* Reverse trick play */
seeksegment.start = 0.0;
seeksegment.stop = next_entry->time;
goto done;
}
+ if(demux->first_index_table_creation == FALSE &&
+ demux->index_table_created == FALSE &&
+ !(flags == 0x80 || (!delta_unit && stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO)))
+ {
+ GST_INFO("Returning without doing nothing");
+ if (buf)
+ gst_buffer_unref (buf);
+ return GST_FLOW_OK;
+ }
for (n = 0; n < laces; n++) {
GstBuffer *sub;
#ifdef MKV_DEMUX_MODIFICATION
sub = gst_buffer_create_sub (buf,
GST_BUFFER_SIZE (buf) - size, lace_size[n]);
GST_DEBUG_OBJECT (demux, "created subbuffer %p", sub);
+#ifdef MKV_DEMUX_MODIFICATION
+ if(demux->index_table_created == FALSE) {
+ demux->common.segment.duration = 0;
+ }
+#endif
if (delta_unit)
GST_BUFFER_FLAG_SET (sub, GST_BUFFER_FLAG_DELTA_UNIT);
if (diff > 0 && diff > demux->max_gap_time
&& lace_time > demux->common.segment.start
&& (!GST_CLOCK_TIME_IS_VALID (demux->common.segment.stop)
- || lace_time < demux->common.segment.stop)) {
+ || lace_time < demux->common.segment.stop) && demux->index_table_created == TRUE) {
GST_DEBUG_OBJECT (demux,
"Gap of %" G_GINT64_FORMAT " ns detected in"
"stream %d (%" GST_TIME_FORMAT " -> %" GST_TIME_FORMAT "). "
demux->last_stop_end = last_stop_end;
GST_OBJECT_LOCK (demux);
+#ifdef MKV_DEMUX_MODIFICATION
+ if ((demux->common.segment.duration == -1 ||
+ demux->stream_start_time + demux->common.segment.duration <
+ last_stop_end) && demux->index_table_created == TRUE) {
+#else
if (demux->common.segment.duration == -1 ||
demux->stream_start_time + demux->common.segment.duration <
last_stop_end) {
+#endif
gst_segment_set_duration (&demux->common.segment, GST_FORMAT_TIME,
last_stop_end - demux->stream_start_time);
GST_OBJECT_UNLOCK (demux);
}
} else if (demux->common.segment.rate < 0.0) {
ret = gst_matroska_demux_backward_trickplay (demux, stream, sub);
- } else
+ } else
#endif
{
/* At this point, we have a sub-buffer pointing at data within a larger
gst_buffer_unref (sub);
sub = buffer;
}
-
+#ifdef MKV_DEMUX_MODIFICATION
+ if(demux->index_table_created == FALSE) {
+ // flag checking sufficient for simple block and delta unit for Block group
+ if(flags == 128 || (!delta_unit && stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO)) {
+ GST_INFO("Keyframe Found");
+
+ //Information required for index table creation
+ GstMatroskaIndex *idx = NULL;
+ idx = (GstMatroskaIndex*)malloc(sizeof(GstMatroskaIndex));
+ if(idx == NULL) {
+ GST_INFO("Unable to create memory");
+ ret = GST_FLOW_UNEXPECTED;
+ goto done;
+ }
+ idx->pos = (cluster_offset - demux->initial_offset);
+ idx->track = 1;
+ idx->time = GST_BUFFER_TIMESTAMP(sub);
+ idx->block = 1;
+
+ // Creating new/Over-Writing index table
+ if(!demux->index_table_array_creation) {
+ GST_INFO("Keyframe time stamp is %"GST_TIME_FORMAT"pos %"G_GUINT64_FORMAT, GST_TIME_ARGS(idx->time), idx->pos);
+ GST_INFO("Index table creation first time");
+ g_array_unref(demux->common.index);
+ demux->common.index = NULL;//g_array_unref(demux->common.index);
+ demux->common.index = g_array_new(FALSE, FALSE, sizeof(GstMatroskaIndex));
+ g_array_append_val(demux->common.index, *idx);
+ GST_INFO("size of the index is %d",demux->common.index->len);
+ demux->index_table_array_creation = TRUE;
+ demux->duration = GST_BUFFER_TIMESTAMP(sub) + GST_BUFFER_DURATION (sub);
+ } else {
+ GST_INFO("Adding Keyframe info to index table");
+ GST_INFO("Keyframe time stamp is %"GST_TIME_FORMAT"pos %"G_GUINT64_FORMAT, GST_TIME_ARGS(idx->time), idx->pos);
+ g_array_append_val(demux->common.index, *idx);
+ GST_INFO("size of the index is %d",demux->common.index->len);
+ demux->duration = GST_BUFFER_TIMESTAMP(sub) + GST_BUFFER_DURATION (sub);
+ GST_INFO("The duration of the video is %"GST_TIME_FORMAT, GST_TIME_ARGS(demux->duration));
+ }
+ free(idx);
+ idx = NULL;
+ } else {
+ //demux->common.segment.duration = (GST_BUFFER_TIMESTAMP(sub) + GST_BUFFER_DURATION (sub));
+ demux->duration = GST_BUFFER_TIMESTAMP(sub) + GST_BUFFER_DURATION (sub);
+ GST_INFO("duration is in capturing not setting%"GST_TIME_FORMAT, GST_TIME_ARGS(demux->duration));
+ GST_INFO("duration in segment duration still not set%"GST_TIME_FORMAT, GST_TIME_ARGS(demux->common.segment.duration));
+ }
+ } else {
+ ret = gst_pad_push (stream->pad, sub);
+ }
+#else
ret = gst_pad_push (stream->pad, sub);
+#endif
}
if (demux->common.segment.rate < 0) {
return ret;
+#ifndef MKV_DEMUX_MODIFICATION
/* EXITS */
eos:
{
ret = gst_matroska_demux_combine_flows (demux, stream, ret);
goto done;
}
+#endif
+
invalid_lacing:
{
GST_ELEMENT_WARNING (demux, STREAM, DEMUX, (NULL), ("Invalid lacing size"));
G_GUINT64_FORMAT "+%" G_GUINT64_FORMAT "+12 >= %"
G_GUINT64_FORMAT ")", seek_pos, demux->common.ebml_segment_start,
length);
+#ifdef MKV_DEMUX_MODIFICATION
+ demux->seek_head_cluster_info_absent = TRUE;
+ demux->seek_head_cue_info_absent = TRUE;
+ demux->index_table_created = FALSE;
+ GST_INFO("Need to create index table this might take time");
+#endif
break;
}
GST_DEBUG_OBJECT (demux,
"Found Segment start at offset %" G_GUINT64_FORMAT,
demux->common.offset);
+#ifdef MKV_DEMUX_MODIFICATION
+ demux->initial_offset = demux->common.offset;
+ GST_INFO("Initial offset storing is %u", demux->initial_offset);
+#endif
/* seeks are from the beginning of the segment,
* after the segment ID/length */
demux->common.ebml_segment_start = demux->common.offset;
if (!demux->tracks_parsed) {
GST_READ_CHECK (gst_matroska_demux_take (demux, read, &ebml));
ret = gst_matroska_demux_parse_tracks (demux, &ebml);
+#ifdef MKV_DEMUX_MODIFICATION
+ if (demux->Subtitle_language_list != NULL) {
+ GstMatroskaLanguageStruct* First_Language = g_list_nth_data (demux->Subtitle_language_list, 0);
+ First_Language->active = TRUE;
+ GstMessage *m;
+ m = gst_message_new_element (GST_OBJECT_CAST (demux),
+ gst_structure_new ("Int_Sub_Language_List", "lang_list",
+ G_TYPE_POINTER, demux->Subtitle_language_list, NULL));
+ gst_element_post_message (GST_ELEMENT_CAST (demux), m);
+ }
+#endif
} else {
GST_READ_CHECK (gst_matroska_demux_flush (demux, read));
}
int i =0;
int minusone = -1;
guint64 duration = 0;
- GstClockTime time_position;
+ GstClockTime time_position = 0;
GstMatroskaIndex *entry = NULL;
+ guint64 time=0;
demux->next_keyframe_ts = demux->prev_keyframe_ts;
GST_INFO("next_keyframe_ts is %"GST_TIME_FORMAT, GST_TIME_ARGS(demux->next_keyframe_ts));
if ((entry = gst_matroska_read_common_do_index_seek (&demux->common, stream, time_position, NULL, NULL)) == NULL) {
GST_DEBUG_OBJECT (demux, "No matching seek entry in index");
}
-
GST_DEBUG_OBJECT (demux, "pos = %"G_GUINT64_FORMAT", track = %d, block = %d, time = %"GST_TIME_FORMAT"\n",
entry->pos, entry->track, entry->block, GST_TIME_ARGS(entry->time));
-
+ time = entry->time;
offset = entry->pos + demux->common.ebml_segment_start;
if (offset >= gst_matroska_read_common_get_length(&demux->common)) {
GST_INFO_OBJECT (demux, " Seek failed");
}
demux->common.offset = offset;
}
- stream->pos = entry->time;
+ stream->pos = time;
stream->set_discont = TRUE;
stream->last_flow = GST_FLOW_OK;
if (stream->pos > 0.0) {
GST_INFO_OBJECT (demux, "Reached EOS.....");
}
}
- demux->common.segment.last_stop = entry->time;
+ demux->common.segment.last_stop = time;
GST_OBJECT_UNLOCK (demux);
}
- if (entry->time == 0.0 && demux->is_eos_blockgroup == TRUE && demux->is_eos_simpleblock == TRUE) {
+ if (time == 0.0 && demux->is_eos_blockgroup == TRUE && demux->is_eos_simpleblock == TRUE) {
gst_segment_init (&demux->common.segment, GST_FORMAT_TIME);
gst_segment_set_duration (&demux->common.segment, GST_FORMAT_TIME, demux->duration);
demux->found_videokeyframe = FALSE;
demux->found_audioframe = FALSE;
- demux->prev_keyframe_ts = entry->time;
+ demux->prev_keyframe_ts = time;
GST_INFO("prev_keyframe in blockgroup is %"GST_TIME_FORMAT,GST_TIME_ARGS(demux->prev_keyframe_ts));
return ret;
}
int i =0;
int minusone = -1;
guint64 duration = 0;
- GstClockTime time_position;
+ GstClockTime time_position = 0;
GstMatroskaIndex *entry = NULL;
demux->next_keyframe_ts = demux->prev_keyframe_ts;
offset = entry->pos + demux->common.ebml_segment_start;
if (offset >= gst_matroska_read_common_get_length(&demux->common)) {
GST_INFO_OBJECT (demux, " Seek failed");
+ GST_OBJECT_UNLOCK (demux);
goto seek_failed;
}
demux->common.offset = offset;
g_assert (demux->common.num_streams == demux->common.src->len);
for (i = 0; i < demux->common.src->len; i++) {
- GstMatroskaTrackContext *context = g_ptr_array_index (demux->common.src,
- i);
+
+#ifdef MKV_DEMUX_MODIFICATION
+//Index table Creation Logic
+ if((demux->seek_head_cluster_info_absent || demux->seek_head_cue_info_absent) && !demux->index_table_created) {
+ GST_INFO("Index table needs to be created");
+
+ guint64 index_offset = demux->common.offset;
+
+ GST_INFO("offset index is %"G_GUINT64_FORMAT, demux->common.offset);
+ demux->common.segment.duration = -1;
+ gint64 start = g_get_monotonic_time ();
+ gint64 end = 0;
+ demux->first_index_table_creation = FALSE;
+ gst_matroska_demux_create_index_table(demux);
+ demux->first_index_table_creation = TRUE;
+ end = g_get_monotonic_time ();
+ GST_INFO("the duration of the clip is %"GST_TIME_FORMAT, GST_TIME_ARGS(demux->duration));
+ GST_INFO("Index table build time is time: %llu\n",(end-start));
+ GST_INFO("size of the index is %d",demux->common.index->len);
+ //gst_matroska_demux_sync_streams (demux);
+
+ demux->common.offset = index_offset;
+ demux->index_table_created = TRUE;
+ demux->common.segment.last_stop = 0;
+ demux->common.segment.duration = demux->duration;
+ GST_INFO("index table creation completed");
+ }
+#endif
+ GstMatroskaTrackContext *context = g_ptr_array_index (demux->common.src, i);
GST_DEBUG_OBJECT (context->pad, "pos %" GST_TIME_FORMAT,
GST_TIME_ARGS (context->pos));
+ if(context->pos > demux->common.segment.stop) {
+ GST_DEBUG("Going to EOS since we reached the Stop position");
+ context->eos = TRUE;
+ }
if (context->eos == FALSE)
goto next;
}
GST_INFO_OBJECT (demux, "All streams are EOS");
ret = GST_FLOW_UNEXPECTED;
+#ifdef MKV_DEMUX_MODIFICATION
+ if(demux->is_eos_blockgroup || demux->is_eos_simpleblock) {
+ demux->seek_entry = 0;
+ }
+#endif
goto eos;
}
GstBuffer *priv = gst_buffer_new_and_alloc (size);
memcpy (GST_BUFFER_DATA (priv), data, size);
+#ifdef MKV_DEMUX_MODIFICATION
+ gsize size_codec = 0;
+ gint idx = 0;
+ guint8 *vol1 = NULL;
+ guint8 *vol_start = NULL;
+ guint8 *vo_start = NULL;
+
+ vol1 = GST_BUFFER_DATA (priv);
+ size_codec = size;
+ 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("found 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("found the vo start byte");
+ }
+ idx++;
+ }
+ if (vo_start == NULL || vol_start == NULL) {
+ GST_ERROR ("could not find vo & vol start codes");
+ return NULL;
+ }
+#endif
gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, priv, NULL);
gst_buffer_unref (priv);
}
+#ifdef MKV_DEMUX_MODIFICATION
+ else {
+ GST_ERROR("codec data is not present");
+ return NULL;
+ }
+#endif
if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_VIDEO_MPEG4_ASP))
*codec_name = g_strdup ("MPEG-4 advanced simple profile");
else
gboolean delta_unit;
stream = g_ptr_array_index (demux->common.src, stream_num);
-
+
if (cluster_time != GST_CLOCK_TIME_NONE) {
/* FIXME: What to do with negative timestamps? Give timestamp 0 or -1?
* Drop unless the lace contains timestamp 0? */
guint64 prev_cluster_time = demux->cluster_time;
guint64 prev_cluster_offset = demux->cluster_offset;
int i = 0;
-
+
while (stream->found_next_kframe == FALSE) {
next_keyframe_offset = demux->common.offset;
}
GST_LOG_OBJECT (demux, "trickplay : Offset %" G_GUINT64_FORMAT ", Element id 0x%x, "
"size %" G_GUINT64_FORMAT ", needed %d", demux->common.offset, id,
- length, needed);
+ length, needed);
if (ret != GST_FLOW_OK) {
GST_WARNING_OBJECT (demux, "Error in id_length_pull flow ret...reason : %s\n", gst_flow_get_name (ret));
context->frames_to_show_bw_keyframes = context->num_frames_bw_keyframes / demux->common.segment.rate;
}
}
-
+
/* keeping previous offset values for normal operation */
demux->common.offset = prev_offset;
demux->cluster_offset = prev_cluster_offset;
demux->cluster_time = prev_cluster_time;
-
+
return stream->num_frames_bw_keyframes;
}
if (((stream->type == GST_MATROSKA_TRACK_TYPE_AUDIO) || (!GST_BUFFER_FLAG_IS_SET (sub, GST_BUFFER_FLAG_DELTA_UNIT)))
&& (demux->prev_keyframe_ts <= (GST_BUFFER_TIMESTAMP (sub)) <= demux->next_keyframe_ts)) {
- if(stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO && !demux->video_keyframe_pushed) {
+ if(stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO && !demux->video_keyframe_pushed) {
demux->video_keyframe_pushed = TRUE;
- } else if(stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO && demux->video_keyframe_pushed){
- GST_INFO("unreffing the video frame (already sent one keyframe)");
- gst_buffer_unref (sub);
- return ret;
- }
-
- if(demux->prev_keyframe_ts == demux->next_keyframe_ts || demux->prev_keyframe_ts > demux->next_keyframe_ts) {
- GST_INFO("Unreffing the already pushed buffer");
+ } else if(stream->type == GST_MATROSKA_TRACK_TYPE_VIDEO && demux->video_keyframe_pushed){
+ GST_INFO("unreffing the video frame (already sent one keyframe)");
+ gst_buffer_unref (sub);
+ return ret;
+ }
+
+ if(demux->prev_keyframe_ts == demux->next_keyframe_ts || demux->prev_keyframe_ts > demux->next_keyframe_ts) {
+ GST_INFO("Unreffing the already pushed buffer");
gst_buffer_unref (sub);
- return ret;
- }
+ return ret;
+ }
GST_DEBUG_OBJECT (demux, "Pushing data of size %d for stream %d, time=%"GST_TIME_FORMAT
" and duration=%" GST_TIME_FORMAT,
*skip = TRUE;
if(stream->frames_to_show_bw_keyframes > 0) {
- GST_INFO("Time stamp modification %"GST_TIME_FORMAT,GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
+ GST_INFO("Time stamp modification %"GST_TIME_FORMAT,GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
- time_escalation = ((stream->num_frames_bw_keyframes/demux->common.segment.rate) - stream->frames_to_show_bw_keyframes) * stream->avg_duration_bw_keyframes * demux->common.segment.rate;
+ time_escalation = ((stream->num_frames_bw_keyframes/demux->common.segment.rate) - stream->frames_to_show_bw_keyframes) * stream->avg_duration_bw_keyframes * demux->common.segment.rate;
GST_BUFFER_TIMESTAMP(buffer) = stream->prev_kframe_timestamp + time_escalation;
GST_INFO("Time stamp modified %"GST_TIME_FORMAT,GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buffer)));
}
}
+#ifdef MKV_DEMUX_MODIFICATION
+static gboolean gst_matroska_demux_create_index_table(GstMatroskaDemux* demux)
+{
+ GstFlowReturn ret;
+ guint32 id_index;
+ guint64 length_index;
+ guint needed_index;
+
+ while(TRUE) {
+ ret = gst_matroska_read_common_peek_id_length_pull (&demux->common,
+ GST_ELEMENT_CAST (demux), &id_index, &length_index, &needed_index);
+
+ if (ret == GST_FLOW_UNEXPECTED || ret != GST_FLOW_OK) {
+ GST_INFO("problem in pulling data or eos reached");
+ demux->index_table_created = TRUE;
+ break;
+ }
+
+ GST_LOG_OBJECT (demux, "Offset %" G_GUINT64_FORMAT ", Element id 0x%x, "
+ "size %" G_GUINT64_FORMAT ", needed %d", demux->common.offset, id_index,
+ length_index, needed_index);
+
+ ret = gst_matroska_demux_parse_id (demux, id_index, length_index, needed_index);
+
+ if(ret == GST_FLOW_UNEXPECTED || ret != GST_FLOW_OK) {
+ GST_INFO("problem in pulling data or eos reached");
+ demux->index_table_created = TRUE;
+ break;
+ }
+ }
+
+ return TRUE;
+}
+#endif
+
gboolean
gst_matroska_demux_plugin_init (GstPlugin * plugin)
{
gboolean video;
gboolean found_videokeyframe;
gboolean found_audioframe;
+ gboolean seek_head_cluster_info_absent;
+ gboolean seek_head_cue_info_absent;
+ gboolean index_table_created;
+ gboolean index_table_array_creation;
+ gboolean first_index_table_creation;
+ guint32 initial_offset;
#endif
/* index stuff */
gboolean is_eos_simpleblock;
gint no_video_frame;
gboolean video_keyframe_pushed;
+ GList* Subtitle_language_list;
#endif
/* gap handling */
guint comp_settings_length;
} GstMatroskaTrackEncoding;
+typedef struct _GstMatroskaLanguageStruct {
+ gchar* language_code;
+ gchar* language_key;
+ gboolean active;
+} GstMatroskaLanguageStruct;
+
gboolean gst_matroska_track_init_video_context (GstMatroskaTrackContext ** p_context);
gboolean gst_matroska_track_init_audio_context (GstMatroskaTrackContext ** p_context);
gboolean gst_matroska_track_init_subtitle_context (GstMatroskaTrackContext ** p_context);
GstFlowReturn ret = GST_FLOW_OK;
guint i;
- if (common->index)
- g_array_free (common->index, TRUE);
+ if (common->index){
+ return ret;
+ }
common->index =
g_array_sized_new (FALSE, FALSE, sizeof (GstMatroskaIndex), 128);
header = g_strdup_printf ("--%s\r\nContent-Type: %s\r\n"
"Content-Length: %u\r\n\r\n",
mux->boundary, mime, GST_BUFFER_SIZE (best->buffer));
+#ifdef GST_EXT_ENHANCEMENT
+ if (header == NULL) {
+ GST_ERROR_OBJECT(mux, "failed to alloc header");
+ goto alloc_failed;
+ }
+#endif /* GST_EXT_ENHANCEMENT */
headerlen = strlen (header);
ret = gst_pad_alloc_buffer_and_set_caps (mux->srcpad, GST_BUFFER_OFFSET_NONE,
memcpy (GST_BUFFER_DATA (headerbuf), header, headerlen);
g_free (header);
+#ifdef GST_EXT_ENHANCEMENT
+ header = NULL;
+#endif /* GST_EXT_ENHANCEMENT */
/* the header has the same timestamp as the data buffer (which we will push
* below) and has a duration of 0 */
{
GST_WARNING_OBJECT (mux,
"failed allocating a %" G_GSIZE_FORMAT " bytes buffer", headerlen);
+#ifdef GST_EXT_ENHANCEMENT
+ if (header) {
+ g_free (header);
+ header = NULL;
+ }
+#else /* GST_EXT_ENHANCEMENT */
g_free (header);
+#endif /* GST_EXT_ENHANCEMENT */
goto beach;
}
}
gst_rtp_depay_plugin_init (GstPlugin * plugin)
{
return gst_element_register (plugin, "rtpdepay",
- GST_RANK_SECONDARY, GST_TYPE_RTP_DEPAY);
+ GST_RANK_MARGINAL, GST_TYPE_RTP_DEPAY);
}
#include "gstrtpmp2tpay.h"
+#define DEFAULT_RTP_FLUSH FALSE
+
+enum {
+ PROP_0,
+ PROP_RTP_FLUSH
+};
+
static GstStaticPadTemplate gst_rtp_mp2t_pay_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
"clock-rate = (int) 90000, " "encoding-name = (string) \"MP2T-ES\"")
);
+static void gst_rtp_mp2t_pay_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_rtp_mp2t_pay_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
static gboolean gst_rtp_mp2t_pay_setcaps (GstBaseRTPPayload * payload,
GstCaps * caps);
static GstFlowReturn gst_rtp_mp2t_pay_handle_buffer (GstBaseRTPPayload *
gstbasertppayload_class->set_caps = gst_rtp_mp2t_pay_setcaps;
gstbasertppayload_class->handle_buffer = gst_rtp_mp2t_pay_handle_buffer;
+
+ gobject_class->set_property = gst_rtp_mp2t_pay_set_property;
+ gobject_class->get_property = gst_rtp_mp2t_pay_get_property;
+ /* GstRTPMP2TPay: rtp-flush
+ * Try to use this option to flush rtp packet if adaptation field is present
+ * in mpeg2-ts packet. If this option is disabled then packet will be flushed
+ * based on MTU of RTP packet.*/
+
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RTP_FLUSH,
+ g_param_spec_boolean ("rtp-flush", "RTP-FLUSH",
+ "Flush RTP packet when adaptation field is present in MP2TS packet",
+ DEFAULT_RTP_FLUSH, G_PARAM_READWRITE));
}
static void
GST_BASE_RTP_PAYLOAD_PT (rtpmp2tpay) = GST_RTP_PAYLOAD_MP2T;
rtpmp2tpay->adapter = gst_adapter_new ();
+ rtpmp2tpay->rtp_flush = DEFAULT_RTP_FLUSH;
}
static void
G_OBJECT_CLASS (parent_class)->finalize (object);
}
+static void gst_rtp_mp2t_pay_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec) {
+ GstRTPMP2TPay *rtpmp2tpay;
+
+ rtpmp2tpay = GST_RTP_MP2T_PAY (object);
+
+ switch (prop_id) {
+ case PROP_RTP_FLUSH:
+ rtpmp2tpay->rtp_flush = g_value_get_boolean (value);
+ break;
+ default :
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void gst_rtp_mp2t_pay_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec) {
+ GstRTPMP2TPay *rtpmp2tpay;
+
+ rtpmp2tpay = GST_RTP_MP2T_PAY (object);
+
+ switch (prop_id) {
+ case PROP_RTP_FLUSH:
+ g_value_set_boolean (value, rtpmp2tpay->rtp_flush);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
static gboolean
gst_rtp_mp2t_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps)
{
guint size, avail, packet_len;
GstClockTime timestamp, duration;
GstFlowReturn ret;
-
+ guint8 *outbuf = NULL;
+ gboolean check_flush = FALSE;
rtpmp2tpay = GST_RTP_MP2T_PAY (basepayload);
size = GST_BUFFER_SIZE (buffer);
timestamp = GST_BUFFER_TIMESTAMP (buffer);
duration = GST_BUFFER_DURATION (buffer);
-
+ outbuf = GST_BUFFER_DATA(buffer);
ret = GST_FLOW_OK;
avail = gst_adapter_available (rtpmp2tpay->adapter);
/* if this buffer is going to overflow the packet, flush what we
* have. */
if (gst_basertppayload_is_filled (basepayload,
- packet_len, rtpmp2tpay->duration + duration)) {
+ packet_len, rtpmp2tpay->duration + duration) ) {
+ GST_DEBUG ("**frame has been flushed and size of frame is %d", packet_len);
+ check_flush = TRUE;
ret = gst_rtp_mp2t_pay_flush (rtpmp2tpay);
rtpmp2tpay->first_ts = timestamp;
rtpmp2tpay->duration = duration;
/* copy buffer to adapter */
gst_adapter_push (rtpmp2tpay->adapter, buffer);
+ /* If last MP2TSmux packet has adaptation field then flush the RTP packet. */
+ if((rtpmp2tpay->rtp_flush == TRUE) && (outbuf[3] & 0x20) && (check_flush == FALSE)) {
+ ret = gst_rtp_mp2t_pay_flush (rtpmp2tpay);
+ rtpmp2tpay->first_ts = timestamp;
+ rtpmp2tpay->duration = duration;
+ }
return ret;
}
GstAdapter *adapter;
GstClockTime first_ts;
GstClockTime duration;
+ gboolean rtp_flush;
};
struct _GstRTPMP2TPayClass
src->udp_buffer_size = DEFAULT_UDP_BUFFER_SIZE;
src->short_header = DEFAULT_SHORT_HEADER;
+#ifdef GST_EXT_RTSPSRC_MODIFICATION
+ src->wait_pause_response = FALSE;
+#endif
+
/* get a list of all extensions */
src->extensions = gst_rtsp_ext_list_get ();
gst_rtsp_connection_reset_timeout (conn);
+#ifdef GST_EXT_RTSPSRC_MODIFICATION
+ if(request->type_data.request.method == GST_RTSP_PAUSE)
+ src->wait_pause_response = TRUE;
+#endif
+
next:
res = gst_rtspsrc_connection_receive (src, conn, response, src->ptcp_timeout);
if (res < 0)
if (src->debug)
gst_rtsp_message_dump (response);
+#ifdef GST_EXT_RTSPSRC_MODIFICATION
+ if(request->type_data.request.method != GST_RTSP_PAUSE && src->wait_pause_response) {
+ GST_DEBUG_OBJECT(src, "ignoring PAUSE response message");
+ src->wait_pause_response = FALSE;
+ goto next;
+ }
+#endif
+
+
switch (response->type) {
case GST_RTSP_MESSAGE_REQUEST:
res = gst_rtspsrc_handle_request (src, conn, response);
GST_DEBUG_OBJECT (src, "got response message %d", thecode);
+#ifdef GST_EXT_RTSPSRC_MODIFICATION
+ if(request->type_data.request.method == GST_RTSP_PAUSE)
+ src->wait_pause_response = FALSE;
+#endif
+
/* if the caller wanted the result code, we store it. */
if (code)
*code = thecode;
switch (res) {
case GST_RTSP_EEOF:
GST_WARNING_OBJECT (src, "server closed connection, doing reconnect");
- if (try == 0) {
+#ifdef GST_EXT_RTSPSRC_MODIFICATION
+ if(request->type_data.request.method == GST_RTSP_PAUSE)
+ src->wait_pause_response = FALSE;
+#endif
+ if (try == 0) {
try++;
/* if reconnect succeeds, try again */
if ((res =
if (!(src->methods & GST_RTSP_PLAY))
goto not_supported;
+#ifdef GST_EXT_RTSPSRC_MODIFICATION
+ if (src->state == GST_RTSP_STATE_PLAYING) {
+ if(src->wait_pause_response == FALSE)
+ goto was_playing;
+ }
+#else
if (src->state == GST_RTSP_STATE_PLAYING)
goto was_playing;
+#endif
if (!src->conninfo.connection || !src->conninfo.connected)
goto done;
gchar *control;
guint next_port_num;
+#ifdef GST_EXT_RTSPSRC_MODIFICATION
+ gboolean wait_pause_response;
+#endif
+
+
/* supported methods */
gint methods;
}
len = sizeof (sndsize);
+#if 0 //WFD Wifi Device socket size issue, kernel 3.4.5 patch (sendto() performance -to use default buffer size
if (sink->buffer_size != 0) {
sndsize = sink->buffer_size;
sndsize, ret, g_strerror (errno), errno));
}
}
-
+#endif
/* read the value of the receive buffer. Note that on linux this returns 2x the
* value we set because the kernel allocates extra memory for metadata.
* The default on Linux is about 100K (which is about 50K without metadata) */
return TRUE;
}
- if (wav->bps > 0) {
+ if (wav->bps > 0 && !(wav->fact)) { /* bug fix */
*bytepos = uint64_ceiling_scale (ts, (guint64) wav->bps, GST_SECOND);
return TRUE;
} else if (wav->fact) {
if (wav->duration > 0)
return TRUE;
- if (wav->bps > 0) {
+ if (wav->bps > 0 && !(wav->fact)) { /* bug fix */
GST_INFO_OBJECT (wav, "Got datasize %" G_GUINT64_FORMAT, wav->datasize);
wav->duration =
uint64_ceiling_scale (wav->datasize, GST_SECOND, (guint64) wav->bps);
break;
}
case GST_RIFF_WAVE_FORMAT_PCM:
+#ifdef WAVPARSER_MODIFICATION
+ /* This spec-out case occurs in certain product.
+ * So, we added code such as lower part. */
+ if (wav->blockalign > wav->channels * (guint) ceil (wav->depth / 8.0)) {
+ guint32 tmp_bps = (wav->rate * wav->depth * wav->channels) / 8;
+ if (wav->av_bps == (tmp_bps * 8) ) {
+ GST_WARNING_OBJECT (wav, "invalid_blockalign : worng blockalign = %u, bps = %u", (guint) wav->blockalign, wav->av_bps);
+ GST_WARNING_OBJECT (wav, "must update caps for spec-out case ");
+ wav->blockalign = wav->channels * (guint) ceil (wav->depth / 8.0);
+ if (wav->channels > 0)
+ wav->width = (wav->blockalign * 8) / wav->channels;
+ else
+ goto no_channels;
+ wav->av_bps = wav->rate * wav->blockalign * wav->channels;
+ wav->bps = wav->av_bps;
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
+ "channels", G_TYPE_INT, wav->channels,
+ "width", G_TYPE_INT, wav->width,
+ "depth", G_TYPE_INT, (guint) wav->depth,
+ "signed", G_TYPE_BOOLEAN, wav->width != 8,
+ "rate", G_TYPE_INT, wav->rate,
+ NULL);
+ break;
+ } else {
+ goto invalid_blockalign;
+ }
+ }
+#else
if (wav->blockalign > wav->channels * (guint) ceil (wav->depth / 8.0))
goto invalid_blockalign;
+#endif
/* fall through */
default:
if (wav->av_bps > wav->blockalign * wav->rate)
if (wav->bytes_per_sample <= 0)
goto no_bytes_per_sample;
+#ifdef WAVPARSER_MODIFICATION
+ if (wav->depth > 32 || wav->width > 32) {
+ GST_WARNING_OBJECT (wav, " invalid_bitresolution : depths (%d) width (%d)", wav->depth, wav->width);
+ goto invalid_bitresolution;
+ }
+#endif
+
GST_DEBUG_OBJECT (wav, "blockalign = %u", (guint) wav->blockalign);
GST_DEBUG_OBJECT (wav, "width = %u", (guint) wav->width);
GST_DEBUG_OBJECT (wav, "depth = %u", (guint) wav->depth);
("Couldn't read in header %d (%s)", res, gst_flow_get_name (res)));
goto fail;
}
+#ifdef WAVPARSER_MODIFICATION
+invalid_bitresolution :
+ {
+ GST_ELEMENT_ERROR (wav, STREAM, DEMUX, (NULL),
+ ("Not support higher than 32 bit resolution"));
+ goto fail;
+ }
+#endif
}
/*
}
}
- if (wav->bps > 0) {
+ if (wav->bps > 0 && !(wav->fact)) { /* bug fix */
/* and timestamps if we have a bitrate, be careful for overflows */
timestamp = uint64_ceiling_scale (pos, GST_SECOND, (guint64) wav->bps);
next_timestamp =
guint64 bps = wav->bps;
/* operating in format TIME, so we can convert */
+#if 0 /* original */
if (!bps && wav->fact)
+#else /* bug fix */
+ if (wav->fact)
+#endif
bps =
gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact);
if (bps) {
GST_INFO_OBJECT (wavparse,
"src=%" G_GINT64_FORMAT ", offset=%" G_GINT64_FORMAT, src_value,
wavparse->offset);
- if (wavparse->bps > 0)
+ if (wavparse->bps > 0 && !(wavparse->fact)) /* bug fix */
*dest_value = uint64_ceiling_scale (src_value, GST_SECOND,
(guint64) wavparse->bps);
else if (wavparse->fact) {
case GST_FORMAT_TIME:
switch (*dest_format) {
case GST_FORMAT_BYTES:
- if (wavparse->bps > 0)
+ if (wavparse->bps > 0 && !(wavparse->fact)) /* bug fix */
*dest_value = gst_util_uint64_scale (src_value,
(guint64) wavparse->bps, GST_SECOND);
else {
#sbs-git:slp/pkgs/g/gst-plugins-good0.10 gst-plugins-good 0.10.31 6e8625ba6fe94fb9d09e6c3be220b54ffaa01273
Name: gst-plugins-good
Summary: GStreamer plugins from the "good" set
-Version: 0.10.31
-Release: 3
+Version: 0.10.34
+Release: 127
Group: Applications/Multimedia
-License: LGPLv2+
+License: LGPL-2.1+
Source0: %{name}-%{version}.tar.gz
#Patch0 : gst-plugins-good-divx-drm.patch
#Patch1 : gst-plugins-good-ebml-read.patch
BuildRequires: gettext
BuildRequires: which
BuildRequires: gst-plugins-base-devel
-BuildRequires: libjpeg-devel
+BuildRequires: libjpeg-turbo-devel
BuildRequires: pkgconfig(gstreamer-0.10)
BuildRequires: pkgconfig(glib-2.0)
-BuildRequires: pkgconfig(liboil-0.3)
BuildRequires: pkgconfig(libpng)
BuildRequires: pkgconfig(libsoup-2.4)
BuildRequires: pkgconfig(libpulse)
BuildRequires: pkgconfig(xfixes)
BuildRequires: pkgconfig(xdamage)
BuildRequires: pkgconfig(xext)
+BuildRequires: pkgconfig(vconf)
+BuildRequires: pkgconfig(iniparser)
+#BuildRequires: pkgconfig(drm-client)
+#BuildRequires: pkgconfig(drm-trusted)
+#BuildRequires: pkgconfig(libdrm)
%description
GStreamer is a streaming media framework, based on graphs of filters
about anything else media-related. Its plugin-based architecture means
that new data types or processing capabilities can be added simply by
installing new plug-ins.
-.
This package contains the GStreamer plugins from the "good" set, a set
of good-quality plug-ins under the LGPL license.
#%patch5 -p1
%patch6 -p1
-
%build
./autogen.sh
export CFLAGS+=" -Wall -g -fPIC\
- -DGST_EXT_SOUP_MODIFICATION"
+ -DGST_EXT_SOUP_MODIFICATION \
+ -DGST_EXT_RTSPSRC_MODIFICATION \
+ -DGST_EXT_AMRPARSER_MODIFICATION \
+ -DGST_EXT_AACPARSER_MODIFICATION \
+ -DGST_EXT_SS_TYPE \
+ -DGST_EXT_MPEGAUDIO_MODIFICATION \
+ -DGST_EXT_ENHANCEMENT"
%configure --prefix=%{_prefix}\
--disable-static\
--disable-deinterlace\
--disable-effectv\
--disable-equalizer\
- --disable-icydemux\
+ --enable-icydemux\
--disable-flx\
--disable-goom\
--disable-goom2k1\
%install
rm -rf %{buildroot}
+mkdir -p %{buildroot}/usr/share/license
+cp COPYING %{buildroot}/usr/share/license/%{name}
%make_install
%files
+%manifest gst-plugins-good.manifest
%defattr(-,root,root,-)
%{_libdir}/gstreamer-0.10
%{_libdir}/gstreamer-0.10/libgstavi.so
+%{_libdir}/gstreamer-0.10/libgstflv.so
+%{_libdir}/gstreamer-0.10/libgstmatroska.so
%{_libdir}/gstreamer-0.10/libgstrtsp.so
%{_libdir}/gstreamer-0.10/libgstisomp4.so
%{_libdir}/gstreamer-0.10/libgstvideocrop.so
%{_libdir}/gstreamer-0.10/libgstpulse.so
%{_libdir}/gstreamer-0.10/libgstmultifile.so
%{_libdir}/gstreamer-0.10/libgstpng.so
-%{_libdir}/gstreamer-0.10/libgstflv.so
%{_libdir}/gstreamer-0.10/libgstudp.so
%{_libdir}/gstreamer-0.10/libgstximagesrc.so
%{_libdir}/gstreamer-0.10/libgstalaw.so
%{_libdir}/gstreamer-0.10/libgstjpeg.so
%{_libdir}/gstreamer-0.10/libgstautodetect.so
%{_libdir}/gstreamer-0.10/libgstvideofilter.so
-%{_libdir}/gstreamer-0.10/libgstmatroska.so
%{_libdir}/gstreamer-0.10/libgstmulaw.so
%{_libdir}/gstreamer-0.10/libgstrtp.so
%{_libdir}/gstreamer-0.10/libgstwavparse.so
%{_libdir}/gstreamer-0.10/libgstshapewipe.so
%{_libdir}/gstreamer-0.10/libgstoss4audio.so
%{_libdir}/gstreamer-0.10/libgstsouphttpsrc.so
+/usr/share/license/%{name}
GST_LOG_OBJECT (pool->v4l2elem, " MMAP offset: %u",
ret->vbuffer.m.offset);
GST_LOG_OBJECT (pool->v4l2elem, " length: %u", ret->vbuffer.length);
- GST_LOG_OBJECT (pool->v4l2elem, " input: %u", ret->vbuffer.input);
data = (guint8 *) v4l2_mmap (0, ret->vbuffer.length,
PROT_READ | PROT_WRITE, MAP_SHARED, pool->video_fd,
break;
case V4L2_CID_HFLIP:
case V4L2_CID_VFLIP:
+#ifndef V4L2_CID_PAN_RESET
case V4L2_CID_HCENTER:
+#endif
+#ifndef V4L2_CID_TILT_RESET
case V4L2_CID_VCENTER:
+#endif
#ifdef V4L2_CID_PAN_RESET
case V4L2_CID_PAN_RESET:
#endif