From: jk7744.park Date: Tue, 8 Sep 2015 13:28:02 +0000 (+0900) Subject: tizen 2.3.1 release X-Git-Tag: submit/tizen_2.3.1/20150915.081250^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b3fb092a5efa39ca872a5a4168dd9f7bed486c21;p=framework%2Fmultimedia%2Fgst-plugins-good0.10.git tizen 2.3.1 release --- diff --git a/configure.ac b/configure.ac index 13a6745..54f8438 100644 --- a/configure.ac +++ b/configure.ac @@ -280,6 +280,23 @@ AG_GST_CHECK_FEATURE(GCONFTOOL, [GConf schemas], , [ 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]), [ @@ -385,6 +402,17 @@ AG_GST_CHECK_PLUGIN(wavenc) 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)]) diff --git a/debian/changelog b/debian/changelog deleted file mode 100755 index 066f9ad..0000000 --- a/debian/changelog +++ /dev/null @@ -1,462 +0,0 @@ -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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 > 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Thu, 25 Mar 2010 15:13:04 +0900 - -gst-plugins-good0.10 (0.10.17-16) unstable; urgency=low - - * Add uploader list - - -- Wonhyung Cho 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 Mon, 22 Mar 2010 14:31:02 +0900 - -gst-plugins-good0.10 (0.10.17-14) unstable; urgency=low - - * Remove taglib dependancy - - -- Seungbae Shin 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 Mon, 08 Mar 2010 16:58:36 +0530 - -gst-plugins-good0.10 (0.10.17-12) unstable; urgency=low - - * Repacking by kishore - - -- YoungHwan Ahn 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 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 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 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 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 Sat, 06 Feb 2010 01:26:15 +0900 - -gst-plugins-good0.10 (0.10.17-6) unstable; urgency=low - - * trick play implementation - - -- younghwan ahn 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 Wed, 02 Dec 2009 10:35:21 +0900 - -gst-plugins-good0.10 (0.10.17-4) unstable; urgency=low - - * force revision up - - -- sangho park 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 Mon, 30 Nov 2009 10:44:16 +0900 - -gst-plugins-good0.10 (0.10.17-2) unstable; urgency=low - - * resolve dependency break - - -- jongmin lee Tue, 17 Nov 2009 17:56:42 +0900 - -gst-plugins-good0.10 (0.10.17-1) unstable; urgency=low - - * Initial release. - - -- jongmin lee The, 17 Nov 2009 12:50:00 +0900 diff --git a/debian/compat b/debian/compat deleted file mode 100644 index 7ed6ff8..0000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -5 diff --git a/debian/control b/debian/control deleted file mode 100755 index 17b5600..0000000 --- a/debian/control +++ /dev/null @@ -1,45 +0,0 @@ -Source: gst-plugins-good0.10 -Section: libs -Priority: optional -Maintainer: Shin Seung Bae , JongHyuk Choi -Uploaders: younghwan ahn , Naveen Ch , Hyunseok Lee , Jeongmo Yang -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) diff --git a/debian/copyright b/debian/copyright deleted file mode 100644 index e3e9f94..0000000 --- a/debian/copyright +++ /dev/null @@ -1,31 +0,0 @@ -This package was debianized by David I. Lehn on -Mon, 15 Jan 2001 18:21:37 -0500. - -It was downloaded from http://gstreamer.net/ - -Upstream Authors: - - Erik Walthinsen - Wim Taymans - Richard Boulton - 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'. - diff --git a/debian/gstreamer0.10-plugins-good.install.in b/debian/gstreamer0.10-plugins-good.install.in deleted file mode 100644 index a32767e..0000000 --- a/debian/gstreamer0.10-plugins-good.install.in +++ /dev/null @@ -1 +0,0 @@ -@PREFIX@/lib/gstreamer-0.10/libgst*.so* diff --git a/debian/rules b/debian/rules deleted file mode 100755 index 0cd87f0..0000000 --- a/debian/rules +++ /dev/null @@ -1,215 +0,0 @@ -#!/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 diff --git a/debian/rules.old b/debian/rules.old deleted file mode 100755 index b2be11f..0000000 --- a/debian/rules.old +++ /dev/null @@ -1,214 +0,0 @@ -#!/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 diff --git a/ext/pulse/Makefile.am b/ext/pulse/Makefile.am index 2438f5e..0196b45 100644 --- a/ext/pulse/Makefile.am +++ b/ext/pulse/Makefile.am @@ -11,13 +11,18 @@ libgstpulse_la_SOURCES = \ 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 \ diff --git a/ext/pulse/pulsesink.c b/ext/pulse/pulsesink.c old mode 100644 new mode 100755 index 11e9c89..a9bb341 --- a/ext/pulse/pulsesink.c +++ b/ext/pulse/pulsesink.c @@ -59,7 +59,13 @@ #include /* only used for GST_PLUGINS_BASE_VERSION_* */ #include - +#ifdef __TIZEN__ +#include +#ifdef PCM_DUMP_ENABLE +#include +#endif +#include +#endif #include "pulsesink.h" #include "pulseutil.h" @@ -83,9 +89,83 @@ enum 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) \ @@ -588,6 +668,15 @@ gst_pulseringbuffer_close_device (GstRingBuffer * buf) 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; @@ -809,7 +898,7 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) #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]; @@ -818,6 +907,13 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) #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); @@ -848,8 +944,10 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) /* 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 @@ -858,7 +956,83 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) 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 @@ -894,10 +1068,27 @@ gst_pulseringbuffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec) /* 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); @@ -1176,7 +1367,6 @@ gst_pulseringbuffer_start (GstRingBuffer * buf) 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; @@ -1198,6 +1388,7 @@ gst_pulseringbuffer_pause (GstRingBuffer * buf) /* 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"); @@ -1860,6 +2051,99 @@ gst_pulsesink_payload (GstBaseAudioSink * sink, GstBuffer * buf) } } +#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) { @@ -1910,11 +2194,20 @@ 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 @@ -1954,6 +2247,54 @@ gst_pulsesink_class_init (GstPulseSinkClass * klass) 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 */ @@ -2183,9 +2524,41 @@ info_failed: } #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; @@ -2203,6 +2576,27 @@ gst_pulsesink_init (GstPulseSink * pulsesink, GstPulseSinkClass * klass) 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); @@ -2221,6 +2615,7 @@ gst_pulsesink_init (GstPulseSink * pulsesink, GstPulseSinkClass * klass) (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 @@ -2382,7 +2777,80 @@ unlock: 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; @@ -2390,7 +2858,7 @@ no_mainloop: no_buffer: { psink->mute = mute; - psink->mute_set = TRUE; + psink->mute_set = FALSE; GST_DEBUG_OBJECT (psink, "we have no ringbuffer"); goto unlock; @@ -2409,6 +2877,79 @@ mute_failed: } } +#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) @@ -2647,9 +3188,24 @@ gst_pulsesink_set_property (GObject * object, 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)) { @@ -2668,6 +3224,69 @@ gst_pulsesink_set_property (GObject * object, 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; @@ -2695,7 +3314,11 @@ gst_pulsesink_get_property (GObject * object, 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); @@ -2703,6 +3326,33 @@ gst_pulsesink_get_property (GObject * object, 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; @@ -2972,7 +3622,6 @@ gst_pulsesink_change_state (GstElement * element, GstStateChange transition) gst_message_new_clock_provide (GST_OBJECT_CAST (element), GST_BASE_AUDIO_SINK (pulsesink)->provided_clock, TRUE)); break; - default: break; } @@ -2981,6 +3630,16 @@ gst_pulsesink_change_state (GstElement * element, GstStateChange transition) 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 */ diff --git a/ext/pulse/pulsesink.h b/ext/pulse/pulsesink.h index 340b481..01859a2 100644 --- a/ext/pulse/pulsesink.h +++ b/ext/pulse/pulsesink.h @@ -51,6 +51,16 @@ G_BEGIN_DECLS #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; @@ -61,17 +71,37 @@ struct _GstPulseSink 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; diff --git a/ext/pulse/pulsesrc.c b/ext/pulse/pulsesrc.c index 12e5282..adeb31a 100644 --- a/ext/pulse/pulsesrc.c +++ b/ext/pulse/pulsesrc.c @@ -214,6 +214,13 @@ gst_pulsesrc_base_init (gpointer g_class) "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 ];" diff --git a/ext/soup/gstsouphttpsrc.c b/ext/soup/gstsouphttpsrc.c old mode 100644 new mode 100755 index 158eac2..9fa5727 --- a/ext/soup/gstsouphttpsrc.c +++ b/ext/soup/gstsouphttpsrc.c @@ -17,7 +17,7 @@ * * 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 @@ -87,6 +87,7 @@ #include #define SEEK_CHANGES + GST_DEBUG_CATEGORY_STATIC (souphttpsrc_debug); #define GST_CAT_DEFAULT souphttpsrc_debug @@ -114,15 +115,29 @@ enum 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); @@ -142,6 +157,10 @@ static gboolean gst_soup_http_src_is_seekable (GstBaseSrc * bsrc); 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, @@ -152,8 +171,13 @@ static char *gst_soup_http_src_unicodify (const char *str); 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); @@ -260,26 +284,50 @@ gst_soup_http_src_class_init (GstSoupHTTPSrcClass * klass) "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, @@ -318,6 +366,9 @@ gst_soup_http_src_class_init (GstSoupHTTPSrcClass * klass) 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); @@ -334,7 +385,13 @@ gst_soup_http_src_reset (GstSoupHTTPSrc * src) 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 @@ -377,6 +434,12 @@ gst_soup_http_src_init (GstSoupHTTPSrc * src, GstSoupHTTPSrcClass * g_class) "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); } @@ -397,6 +460,11 @@ gst_soup_http_src_finalize (GObject * gobject) 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); @@ -456,19 +524,80 @@ gst_soup_http_src_set_property (GObject * object, guint prop_id, { #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; @@ -493,7 +622,11 @@ gst_soup_http_src_set_property (GObject * object, guint prop_id, 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); @@ -505,11 +638,12 @@ gst_soup_http_src_set_property (GObject * object, guint prop_id, 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; @@ -571,6 +705,11 @@ gst_soup_http_src_get_property (GObject * object, guint prop_id, 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; @@ -596,16 +735,25 @@ gst_soup_http_src_get_property (GObject * object, guint prop_id, 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; @@ -636,13 +784,19 @@ gst_soup_http_src_cancel_message (GstSoupHTTPSrc * src) 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]; @@ -651,13 +805,66 @@ gst_soup_http_src_add_range_header (GstSoupHTTPSrc * src, guint64 offset) 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); @@ -698,6 +905,33 @@ _append_extra_header (GQuark field_id, const GValue * value, gpointer user_data) 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); @@ -790,6 +1024,31 @@ gst_soup_http_src_headers_foreach (const gchar * name, const gchar * val, 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 @@ -805,6 +1064,11 @@ gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src) 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); @@ -828,6 +1092,52 @@ gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * 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) { @@ -841,8 +1151,10 @@ gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src) #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); @@ -855,7 +1167,7 @@ gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src) 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); @@ -865,6 +1177,27 @@ gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src) } #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 (); @@ -883,10 +1216,26 @@ gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src) "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; @@ -964,6 +1313,12 @@ gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src) /* 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) { @@ -990,9 +1345,14 @@ gst_soup_http_src_got_body_cb (SoupMessage * msg, GstSoupHTTPSrc * src) } 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. */ @@ -1003,24 +1363,83 @@ gst_soup_http_src_finished_cb (SoupMessage * msg, GstSoupHTTPSrc * src) 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); @@ -1103,6 +1522,8 @@ gst_soup_http_src_got_chunk_cb (SoupMessage * msg, SoupBuffer * chunk, 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. */ @@ -1149,7 +1570,7 @@ gst_soup_http_src_response_cb (SoupSession * session, SoupMessage * msg, /* 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) { @@ -1175,15 +1596,25 @@ gst_soup_http_src_parse_status (SoupMessage * msg, GstSoupHTTPSrc * src) 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, @@ -1191,10 +1622,15 @@ gst_soup_http_src_parse_status (SoupMessage * msg, GstSoupHTTPSrc * src) 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, @@ -1209,6 +1645,17 @@ gst_soup_http_src_parse_status (SoupMessage * msg, GstSoupHTTPSrc * src) 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? */ @@ -1223,6 +1670,8 @@ gst_soup_http_src_parse_status (SoupMessage * msg, GstSoupHTTPSrc * src) 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, @@ -1236,31 +1685,54 @@ gst_soup_http_src_build_message (GstSoupHTTPSrc * src) 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; @@ -1277,18 +1749,48 @@ gst_soup_http_src_build_message (GstSoupHTTPSrc * src) (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); @@ -1317,7 +1819,11 @@ gst_soup_http_src_create (GstPushSrc * psrc, GstBuffer ** outbuf) 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", @@ -1356,7 +1862,10 @@ gst_soup_http_src_create (GstPushSrc * psrc, GstBuffer ** outbuf) 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; @@ -1395,9 +1904,38 @@ static gboolean 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")); @@ -1414,11 +1952,26 @@ gst_soup_http_src_start (GstBaseSrc * bsrc) 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 @@ -1427,7 +1980,11 @@ gst_soup_http_src_start (GstBaseSrc * bsrc) 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); } @@ -1438,8 +1995,38 @@ gst_soup_http_src_start (GstBaseSrc * bsrc) } #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", @@ -1541,6 +2128,11 @@ gst_soup_http_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment) #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; } @@ -1575,9 +2167,92 @@ gst_soup_http_src_query (GstBaseSrc * bsrc, GstQuery * query) 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; @@ -1589,6 +2264,43 @@ gst_soup_http_src_query (GstBaseSrc * bsrc, GstQuery * query) 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) { diff --git a/ext/soup/gstsouphttpsrc.h b/ext/soup/gstsouphttpsrc.h old mode 100644 new mode 100755 index 95bbc0c..14f1044 --- a/ext/soup/gstsouphttpsrc.h +++ b/ext/soup/gstsouphttpsrc.h @@ -9,7 +9,7 @@ * 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__ @@ -34,9 +34,13 @@ G_BEGIN_DECLS (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, @@ -68,6 +72,10 @@ struct _GstSoupHTTPSrc { 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; @@ -78,6 +86,11 @@ struct _GstSoupHTTPSrc { 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; @@ -89,10 +102,27 @@ struct _GstSoupHTTPSrc { 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 { diff --git a/gst-plugins-good.manifest b/gst-plugins-good.manifest new file mode 100755 index 0000000..a76fdba --- /dev/null +++ b/gst-plugins-good.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/gst-plugins-good.spec.in b/gst-plugins-good.spec.in index 2dfc0eb..8ed8950 100644 --- a/gst-plugins-good.spec.in +++ b/gst-plugins-good.spec.in @@ -27,7 +27,7 @@ BuildRequires: gcc-c++ @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 diff --git a/gst/audioparsers/gstaacparse.c b/gst/audioparsers/gstaacparse.c index d8c0995..cb984da 100644 --- a/gst/audioparsers/gstaacparse.c +++ b/gst/audioparsers/gstaacparse.c @@ -66,6 +66,10 @@ GST_DEBUG_CATEGORY_STATIC (aacparse_debug); #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) @@ -82,6 +86,15 @@ static gboolean gst_aac_parse_check_valid_frame (GstBaseParse * parse, 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"); @@ -139,6 +152,10 @@ gst_aac_parse_class_init (GstAacParseClass * klass) 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 } @@ -152,6 +169,9 @@ static void 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 } @@ -356,6 +376,7 @@ gst_aac_parse_check_adts_frame (GstAacParse * aacparse, 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); @@ -384,7 +405,206 @@ gst_aac_parse_parse_adts_header (GstAacParse * aacparse, const guint8 * data, 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. @@ -472,10 +692,11 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse, 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; @@ -487,7 +708,7 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse, 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) | @@ -541,12 +762,15 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse, 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; } @@ -585,8 +809,7 @@ gst_aac_parse_check_valid_frame (GstBaseParse * parse, 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) { @@ -617,6 +840,176 @@ gst_aac_parse_check_valid_frame (GstBaseParse * parse, } +#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. @@ -651,11 +1044,10 @@ gst_aac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) 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); @@ -676,6 +1068,44 @@ gst_aac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) 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; } @@ -748,3 +1178,423 @@ gst_aac_parse_sink_getcaps (GstBaseParse * parse) 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 diff --git a/gst/audioparsers/gstaacparse.h b/gst/audioparsers/gstaacparse.h index 1907c2e..21949a3 100644 --- a/gst/audioparsers/gstaacparse.h +++ b/gst/audioparsers/gstaacparse.h @@ -38,6 +38,19 @@ G_BEGIN_DECLS #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: @@ -77,6 +90,20 @@ struct _GstAacParse { 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; }; diff --git a/gst/audioparsers/gstamrparse.c b/gst/audioparsers/gstamrparse.c index 8f6ef94..6c5bb6d 100644 --- a/gst/audioparsers/gstamrparse.c +++ b/gst/audioparsers/gstamrparse.c @@ -69,7 +69,7 @@ static const gint block_size_wb[16] = /* 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); @@ -84,6 +84,11 @@ static gboolean gst_amr_parse_check_valid_frame (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"); @@ -129,6 +134,10 @@ gst_amr_parse_class_init (GstAmrParseClass * klass) 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 } @@ -213,7 +222,11 @@ gst_amr_parse_sink_setcaps (GstBaseParse * parse, GstCaps * caps) 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; @@ -236,17 +249,24 @@ gst_amr_parse_parse_header (GstAmrParse * amrparse, 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; @@ -288,6 +308,15 @@ gst_amr_parse_check_valid_frame (GstBaseParse * parse, 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. */ @@ -300,6 +329,31 @@ gst_amr_parse_check_valid_frame (GstBaseParse * parse, 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 @@ -370,6 +424,16 @@ gst_amr_parse_start (GstBaseParse * parse) 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; } @@ -429,3 +493,190 @@ gst_amr_parse_sink_getcaps (GstBaseParse * parse) 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 diff --git a/gst/audioparsers/gstamrparse.h b/gst/audioparsers/gstamrparse.h index 86a26e0..f9c6f12 100644 --- a/gst/audioparsers/gstamrparse.h +++ b/gst/audioparsers/gstamrparse.h @@ -63,6 +63,10 @@ struct _GstAmrParse { gboolean need_header; gint header; gboolean wide; +#ifdef GST_EXT_AMRPARSER_MODIFICATION + gboolean sync_check; + GstActivateMode pad_mode; +#endif }; /** diff --git a/gst/audioparsers/gstflacparse.c b/gst/audioparsers/gstflacparse.c old mode 100644 new mode 100755 index 9cfdb98..f85e964 --- a/gst/audioparsers/gstflacparse.c +++ b/gst/audioparsers/gstflacparse.c @@ -202,6 +202,8 @@ static gboolean gst_flac_parse_convert (GstBaseParse * parse, 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); @@ -249,6 +251,7 @@ gst_flac_parse_class_init (GstFlacParseClass * klass) 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 @@ -257,6 +260,113 @@ gst_flac_parse_init (GstFlacParse * flacparse, GstFlacParseClass * klass) 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) @@ -1254,6 +1364,7 @@ gst_flac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) } 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"); @@ -1264,20 +1375,16 @@ gst_flac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) 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 */ @@ -1286,13 +1393,26 @@ gst_flac_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) 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)) diff --git a/gst/audioparsers/gstmpegaudioparse.c b/gst/audioparsers/gstmpegaudioparse.c index 2381fc3..f25c711 100644 --- a/gst/audioparsers/gstmpegaudioparse.c +++ b/gst/audioparsers/gstmpegaudioparse.c @@ -70,6 +70,23 @@ GST_DEBUG_CATEGORY_STATIC (mpeg_audio_parse_debug); #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, @@ -87,12 +104,27 @@ static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", 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, @@ -168,6 +200,25 @@ gst_mpeg_audio_parse_class_init (GstMpegAudioParseClass * klass) 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 = @@ -180,6 +231,11 @@ gst_mpeg_audio_parse_class_init (GstMpegAudioParseClass * klass) 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" @@ -222,16 +278,80 @@ gst_mpeg_audio_parse_reset (GstMpegAudioParse * mp3parse) 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); @@ -245,8 +365,34 @@ gst_mpeg_audio_parse_start (GstBaseParse * parse) 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; } @@ -256,7 +402,6 @@ gst_mpeg_audio_parse_stop (GstBaseParse * parse) GstMpegAudioParse *mp3parse = GST_MPEG_AUDIO_PARSE (parse); GST_DEBUG_OBJECT (parse, "stopping"); - gst_mpeg_audio_parse_reset (mp3parse); return TRUE; @@ -353,6 +498,22 @@ mp3_type_frame_length_from_header (GstMpegAudioParse * mp3parse, guint32 header, 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; } @@ -486,6 +647,148 @@ gst_mpeg_audio_parse_head_check (GstMpegAudioParse * mp3parse, 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) @@ -498,6 +801,13 @@ gst_mpeg_audio_parse_check_valid_frame (GstBaseParse * parse, 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; @@ -563,6 +873,7 @@ gst_mpeg_audio_parse_check_valid_frame (GstBaseParse * parse, gst_base_parse_set_min_frame_size (parse, MIN_FRAME_SIZE); *framesize = bpf; + mp3parse->frame_byte = bpf; return TRUE; } @@ -621,6 +932,8 @@ gst_mpeg_audio_parse_handle_first_frame (GstMpegAudioParse * mp3parse, 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; @@ -975,12 +1288,27 @@ gst_mpeg_audio_parse_parse_frame (GstBaseParse * parse, 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", @@ -1304,3 +1632,410 @@ gst_mpeg_audio_parse_get_sink_caps (GstBaseParse * parse) 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 diff --git a/gst/audioparsers/gstmpegaudioparse.h b/gst/audioparsers/gstmpegaudioparse.h index 7580001..bd4008a 100644 --- a/gst/audioparsers/gstmpegaudioparse.h +++ b/gst/audioparsers/gstmpegaudioparse.h @@ -39,6 +39,9 @@ G_BEGIN_DECLS #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; @@ -60,6 +63,13 @@ struct _GstMpegAudioParse { /* 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; @@ -92,6 +102,24 @@ struct _GstMpegAudioParse { /* 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 }; /** diff --git a/gst/audioparsers/plugin.c b/gst/audioparsers/plugin.c index ae8332d..4f1745a 100644 --- a/gst/audioparsers/plugin.c +++ b/gst/audioparsers/plugin.c @@ -39,8 +39,10 @@ plugin_init (GstPlugin * plugin) 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", diff --git a/gst/avi/gstavidemux.c b/gst/avi/gstavidemux.c old mode 100644 new mode 100755 index 0b5a55e..e8bef94 --- a/gst/avi/gstavidemux.c +++ b/gst/avi/gstavidemux.c @@ -74,6 +74,13 @@ #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 @@ -157,6 +164,8 @@ gst_avi_demux_find_frame_type (GstAviStream *stream, GstBuffer *buf, int *frame_ 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; @@ -347,12 +356,25 @@ gst_avi_demux_class_init (GstAviDemuxClass * klass) 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, @@ -362,6 +384,43 @@ gst_avi_demux_class_init (GstAviDemuxClass * klass) #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) { @@ -379,7 +438,9 @@ 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); } @@ -748,11 +809,7 @@ gst_avi_demux_handle_src_query (GstPad * pad, GstQuery * query) 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: @@ -1085,6 +1142,7 @@ gst_avi_demux_handle_sink_event (GstPad * pad, GstEvent * event) 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 */ } @@ -1483,8 +1541,7 @@ invalid_params: { 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; } } @@ -2088,6 +2145,13 @@ gst_riff_parse_strd (GstAviDemux * avi, 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 */ @@ -2257,6 +2321,14 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) 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 */ @@ -2434,6 +2506,9 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) 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; @@ -2531,6 +2606,7 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf) stream->idx_n = 0; stream->idx_max = 0; + stream->sent_eos = FALSE; gst_pad_set_element_private (pad, stream); avi->num_streams++; @@ -2706,7 +2782,7 @@ gst_avi_demux_index_for_time (GstAviDemux * avi, 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)); @@ -2793,7 +2869,7 @@ gst_avi_demux_parse_index (GstAviDemux * avi, GstBuffer * buf) gst_riff_index_entry *index; GstClockTime stamp; GstAviStream *stream; - GstAviIndexEntry entry; + GstAviIndexEntry entry = {0}; guint32 id; if (!buf) @@ -2927,6 +3003,35 @@ gst_avi_demux_stream_index (GstAviDemux * avi) 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))); @@ -3408,6 +3513,7 @@ gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event) { 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); @@ -3416,6 +3522,15 @@ gst_avi_demux_push_event (GstAviDemux * avi, GstEvent * event) 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)); } @@ -4305,6 +4420,7 @@ gst_avi_demux_move_stream (GstAviDemux * avi, GstAviStream * stream, 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", @@ -4442,7 +4558,7 @@ gst_avi_demux_handle_seek (GstAviDemux * avi, GstPad * pad, GstEvent * event) 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, }; @@ -5093,6 +5209,25 @@ gst_avi_demux_loop_data (GstAviDemux * avi) /* 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", @@ -5131,8 +5266,13 @@ gst_avi_demux_loop_data (GstAviDemux * avi) 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; } } diff --git a/gst/avi/gstavidemux.h b/gst/avi/gstavidemux.h old mode 100644 new mode 100755 index 174d8a8..8e3afec --- a/gst/avi/gstavidemux.h +++ b/gst/avi/gstavidemux.h @@ -141,6 +141,7 @@ typedef struct { GstTagList *taglist; gint index_id; + gboolean sent_eos; } GstAviStream; typedef enum { @@ -210,6 +211,9 @@ typedef struct _GstAviDemux { gint index_id; gboolean seekable; + //playback protection + gboolean playback_protected; + #ifdef DIVX_DRM uint8_t* drmContext; void *divx_handle; diff --git a/gst/flv/gstflvdemux.c b/gst/flv/gstflvdemux.c old mode 100644 new mode 100755 index fbf7e25..9057ec2 --- a/gst/flv/gstflvdemux.c +++ b/gst/flv/gstflvdemux.c @@ -811,6 +811,11 @@ gst_flv_demux_parse_tag_audio (GstFlvDemux * demux, GstBuffer * buffer) /* 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); @@ -1369,9 +1374,6 @@ gst_flv_demux_parse_tag_video (GstFlvDemux * demux, GstBuffer * buffer) 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) @@ -2007,7 +2009,7 @@ gst_flv_demux_pull_range (GstFlvDemux * demux, GstPad * pad, guint64 offset, "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; } @@ -2248,7 +2250,7 @@ exit: 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; @@ -2456,12 +2458,14 @@ gst_flv_demux_find_offset (GstFlvDemux * demux, GstSegment * segment) 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, diff --git a/gst/flv/gstflvdemux.h b/gst/flv/gstflvdemux.h old mode 100644 new mode 100755 index 07559a5..2d064b7 --- a/gst/flv/gstflvdemux.h +++ b/gst/flv/gstflvdemux.h @@ -34,6 +34,9 @@ G_BEGIN_DECLS (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; diff --git a/gst/isomp4/Makefile.am b/gst/isomp4/Makefile.am index 010e09c..d7be13e 100644 --- a/gst/isomp4/Makefile.am +++ b/gst/isomp4/Makefile.am @@ -1,7 +1,7 @@ plugin_LTLIBRARIES = libgstisomp4.la -libgstisomp4_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) +libgstisomp4_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(DRM_CLIENT_CFLAGS) $(DRM_TRUSTED_CFLAGS) libgstisomp4_la_LIBADD = \ $(GST_PLUGINS_BASE_LIBS) \ -lgstriff-@GST_MAJORMINOR@ \ @@ -9,7 +9,7 @@ libgstisomp4_la_LIBADD = \ -lgstrtp-@GST_MAJORMINOR@ \ -lgsttag-@GST_MAJORMINOR@ \ -lgstpbutils-@GST_MAJORMINOR@ \ - $(GST_BASE_LIBS) $(GST_LIBS) $(ZLIB_LIBS) + $(GST_BASE_LIBS) $(GST_LIBS) $(ZLIB_LIBS) $(DRM_CLIENT_LIBS) $(DRM_TRUSTED_LIBS) libgstisomp4_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS} libgstisomp4_la_SOURCES = isomp4-plugin.c gstrtpxqtdepay.c \ qtdemux.c qtdemux_types.c qtdemux_dump.c qtdemux_lang.c \ diff --git a/gst/isomp4/fourcc.h b/gst/isomp4/fourcc.h old mode 100644 new mode 100755 index 295d17e..eb36cb5 --- a/gst/isomp4/fourcc.h +++ b/gst/isomp4/fourcc.h @@ -55,14 +55,10 @@ G_BEGIN_DECLS #define FOURCC_clip GST_MAKE_FOURCC('c','l','i','p') #define FOURCC_trak GST_MAKE_FOURCC('t','r','a','k') #define FOURCC_udta GST_MAKE_FOURCC('u','d','t','a') -#define FOURCC_ctab GST_MAKE_FOURCC('c','t','a','b') #define FOURCC_tkhd GST_MAKE_FOURCC('t','k','h','d') #define FOURCC_crgn GST_MAKE_FOURCC('c','r','g','n') -#define FOURCC_matt GST_MAKE_FOURCC('m','a','t','t') -#define FOURCC_kmat GST_MAKE_FOURCC('k','m','a','t') #define FOURCC_edts GST_MAKE_FOURCC('e','d','t','s') #define FOURCC_elst GST_MAKE_FOURCC('e','l','s','t') -#define FOURCC_load GST_MAKE_FOURCC('l','o','a','d') #define FOURCC_tref GST_MAKE_FOURCC('t','r','e','f') #define FOURCC_imap GST_MAKE_FOURCC('i','m','a','p') #define FOURCC___in GST_MAKE_FOURCC(' ',' ','i','n') @@ -78,7 +74,6 @@ G_BEGIN_DECLS #define FOURCC_smhd GST_MAKE_FOURCC('s','m','h','d') #define FOURCC_gmhd GST_MAKE_FOURCC('g','m','h','d') #define FOURCC_hmhd GST_MAKE_FOURCC('h','m','h','d') -#define FOURCC_gmin GST_MAKE_FOURCC('g','m','i','n') #define FOURCC_dinf GST_MAKE_FOURCC('d','i','n','f') #define FOURCC_dref GST_MAKE_FOURCC('d','r','e','f') #define FOURCC_stbl GST_MAKE_FOURCC('s','t','b','l') @@ -93,7 +88,6 @@ G_BEGIN_DECLS #define FOURCC_strm GST_MAKE_FOURCC('s','t','r','m') #define FOURCC_rtsp GST_MAKE_FOURCC('r','t','s','p') #define FOURCC_co64 GST_MAKE_FOURCC('c','o','6','4') -#define FOURCC_cmov GST_MAKE_FOURCC('c','m','o','v') #define FOURCC_dcom GST_MAKE_FOURCC('d','c','o','m') #define FOURCC_cmvd GST_MAKE_FOURCC('c','m','v','d') #define FOURCC_hint GST_MAKE_FOURCC('h','i','n','t') @@ -134,13 +128,7 @@ G_BEGIN_DECLS #define FOURCC_free GST_MAKE_FOURCC('f','r','e','e') #define FOURCC_data GST_MAKE_FOURCC('d','a','t','a') #define FOURCC_SVQ3 GST_MAKE_FOURCC('S','V','Q','3') -#define FOURCC_rmra GST_MAKE_FOURCC('r','m','r','a') -#define FOURCC_rmda GST_MAKE_FOURCC('r','m','d','a') -#define FOURCC_rdrf GST_MAKE_FOURCC('r','d','r','f') #define FOURCC__gen GST_MAKE_FOURCC(0xa9, 'g', 'e', 'n') -#define FOURCC_rmdr GST_MAKE_FOURCC('r','m','d','r') -#define FOURCC_rmvc GST_MAKE_FOURCC('r','m','v','c') -#define FOURCC_qtim GST_MAKE_FOURCC('q','t','i','m') #define FOURCC_drms GST_MAKE_FOURCC('d','r','m','s') #define FOURCC_avc1 GST_MAKE_FOURCC('a','v','c','1') #define FOURCC_h263 GST_MAKE_FOURCC('h','2','6','3') @@ -188,6 +176,11 @@ G_BEGIN_DECLS #define FOURCC_sosn GST_MAKE_FOURCC('s','o','s','n') #define FOURCC_XMP_ GST_MAKE_FOURCC('X','M','P','_') #define FOURCC_uuid GST_MAKE_FOURCC('u','u','i','d') +#define FOURCC_pssh GST_MAKE_FOURCC('p','s','s','h') +#define FOURCC_saiz GST_MAKE_FOURCC('s','a','i','z') +#define FOURCC_saio GST_MAKE_FOURCC('s','a','i','o') +#define FOURCC_senc GST_MAKE_FOURCC('s','e','n','c') +#define FOURCC_mfhd GST_MAKE_FOURCC('m','f','h','d') /* SVQ3 fourcc */ diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c old mode 100644 new mode 100755 index 29d7ef5..cc15f89 --- a/gst/isomp4/qtdemux.c +++ b/gst/isomp4/qtdemux.c @@ -76,12 +76,80 @@ #ifdef HAVE_ZLIB # include #endif +#ifdef QTDEMUX_MODIFICATION +#include +#define READ_UINT8(reader, val, nbits) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint8 (reader, &val, nbits)) { \ + GST_WARNING ("failed to read uint8, nbits: %d", nbits); \ + goto failed; \ + } \ +}G_STMT_END +#define MARKER_UNCHECKED(br) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint8_unchecked (br, 1)) { \ + GST_WARNING ("Wrong marker bit"); \ + goto failed; \ + } \ +}G_STMT_END +#define CHECK_REMAINING(br, needed) G_STMT_START { \ + if (gst_bit_reader_get_remaining (br) < needed) \ + goto failed; \ +} G_STMT_END +#define READ_UINT16(reader, val, nbits) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint16 (reader, &val, nbits)) { \ + GST_WARNING ("failed to read uint16, nbits: %d", nbits); \ + goto failed; \ + } \ +}G_STMT_END +#define SKIP(reader, nbits) G_STMT_START { \ + if (!gst_bit_reader_skip (reader, nbits)) { \ + GST_WARNING ("failed to skip nbits: %d", nbits); \ + goto failed; \ + } \ +} G_STMT_END +#define CHECK_ALLOWED(val, min, max) G_STMT_START { \ + if (val < min || val > max) { \ + GST_WARNING ("value not in allowed range. value: %d, range %d-%d", \ + val, min, max); \ + goto failed; \ + } \ +}G_STMT_END +static const guint8 default_intra_quant_mat[64] = { + 8, 17, 18, 19, 21, 23, 25, 27, + 17, 18, 19, 21, 23, 25, 27, 28, + 20, 21, 22, 23, 24, 26, 28, 30, + 21, 22, 23, 24, 26, 28, 30, 32, + 22, 23, 24, 26, 28, 30, 32, 35, + 23, 24, 26, 28, 30, 32, 35, 38, + 25, 26, 28, 30, 32, 35, 38, 41, + 27, 28, 30, 32, 35, 38, 41, 45 +}; +static const guint8 default_non_intra_quant_mat[64] = { + 16, 17, 18, 19, 20, 21, 22, 23, + 17, 18, 19, 20, 21, 22, 23, 24, + 18, 19, 20, 21, 22, 23, 24, 25, + 19, 20, 21, 22, 23, 24, 26, 27, + 20, 21, 22, 23, 25, 26, 27, 28, + 21, 22, 23, 24, 26, 27, 28, 30, + 22, 23, 24, 26, 27, 28, 30, 31, + 23, 24, 25, 27, 28, 30, 31, 33, +}; +static const guint8 mpeg4_zigzag_8x8[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; +#endif /* max. size considered 'sane' for non-mdat atoms */ #define QTDEMUX_MAX_ATOM_SIZE (25*1024*1024) /* if the sample index is larger than this, something is likely wrong */ -#define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (50*1024*1024) +#define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (60*1024*1024) /* For converting qt creation times to unix epoch times */ #define QTDEMUX_SECONDS_PER_DAY (60 * 60 * 24) @@ -92,15 +160,20 @@ #ifdef QTDEMUX_MODIFICATION /* default value of buffer-size property */ #define QTDEMUX_MAX_BUFFER_SIZE (100*1024*1024) - +#ifdef DRM_ENABLE +#define QTDEMUX_DRM_WORD_SIZE 50 +#endif +#define QTDEMUX_MAX_SAMPLE_DURATION 2*GST_SECOND #define QTDEMUX_DEFAULT_FWD_TRICKPLAY_MODE 0 /* 0: sending P-frames also, 1: only key frames */ /* properties considering different cases of file formats */ + enum { PROP_0, PROP_MAX_BUFFER_SIZE, PROP_TEMP_LOCATION, PROP_FWDTRICK_MODE, + PROP_PLAYBACK_PROTECTION, }; #endif @@ -112,6 +185,11 @@ typedef struct _QtDemuxSample QtDemuxSample; #ifdef QTDEMUX_MODIFICATION typedef struct _TrickPlayInfo TrickPlayInfo; +typedef struct _QtDemuxSubSampleEncryption QtDemuxSubSampleEncryption; +typedef struct _QtDemuxSubSampleEntryInfo QtDemuxSubSampleEntryInfo; +typedef struct _QtDemuxTfraTable QtDemuxTfraTable; +typedef struct _QtDemuxTfraEntryInfo QtDemuxTfraEntryInfo; +typedef struct _QtDemuxLanguageStruct QtDemuxLanguageStruct; struct _TrickPlayInfo { @@ -121,6 +199,37 @@ struct _TrickPlayInfo gint32 show_samples; /* samples to show between two consecutive key frames */ guint64 start_pos; /* trickplay start position */ }; + +struct _QtDemuxSubSampleEntryInfo +{ + guint16 LenofClearData; + guint32 LenofEncryptData; +}; + +struct _QtDemuxSubSampleEncryption +{ + guint16 n_entries; + QtDemuxSubSampleEntryInfo *sub_entry; +}; + +struct _QtDemuxTfraEntryInfo +{ + guint64 time; + guint64 moof_offset; +}; + +struct _QtDemuxTfraTable +{ + guint32 n_entries; + GArray *tfra_entries; +}; + +struct _QtDemuxLanguageStruct +{ + gchar* language_code; + gchar* language_key; + gboolean active; +}; #endif @@ -136,9 +245,13 @@ struct _QtDemuxSample guint32 size; gint32 pts_offset; /* Add this value to timestamp to get the pts */ guint64 offset; - guint64 timestamp; /* DTS In mov time */ + guint64 timestamp; /* DTS In mov time */ guint32 duration; /* In mov time */ - gboolean keyframe; /* TRUE when this packet is a keyframe */ + gboolean keyframe; /* TRUE when this packet is a keyframe */ +#ifdef QTDEMUX_MODIFICATION + guint8 *iv; /* initialization vector for decryption*/ + QtDemuxSubSampleEncryption *sub_encry; +#endif }; /* timestamp is the DTS */ @@ -213,6 +326,70 @@ struct _QtDemuxSample * * This is a good usecase for the GStreamer accumulated SEGMENT events. */ +#ifdef QTDEMUX_MODIFICATION +typedef char uuid_t[16]; + +static const uuid_t tfxd_uuid = + { + 0x6d, 0x1d, 0x9b, 0x05, + 0x42, 0xd5, 0x44, 0xe6, + 0x80, 0xe2, 0x14, 0x1d, + 0xaf, 0xf7, 0x57, 0xb2 + }; + +static const uuid_t tfrf_uuid = + { + 0xd4, 0x80, 0x7e, 0xf2, + 0xca, 0x39, 0x46, 0x95, + 0x8e, 0x54, 0x26, 0xcb, + 0x9e, 0x46, 0xa7, 0x9f + }; + +static const uuid_t encrypt_uuid = + { + 0xa2, 0x39, 0x4f, 0x52, + 0x5a, 0x9b, 0x4f, 0x14, + 0xa2, 0x44, 0x6c, 0x42, + 0x7c, 0x64, 0x8d, 0xf4 + }; + +static const uuid_t protection_uuid = + { + 0xd0, 0x8a, 0x4f, 0x18, + 0x10, 0xf3, 0x4a, 0x82, + 0xb6, 0xc8, 0x32, 0xd8, + 0xab, 0xa1, 0x83, 0xd3 + }; +static const uuid_t playready_system_id = + { + 0x9a, 0x04, 0xf0, 0x79, + 0x98, 0x40, 0x42, 0x86, + 0xab, 0x92, 0xe6, 0x5b, + 0xe0, 0x88, 0x5f, 0x95 + }; + +static const uuid_t dash_playready_system_id = + { + 0x79, 0xf0, 0x04, 0x9a, + 0x40, 0x98, 0x86, 0x42, + 0xab, 0x92, 0xe6, 0x5b, + 0xe0, 0x88, 0x5f, 0x95 + }; + +#define SE_OVERRIDE_TE_FLAGS 0x000001 +#define SE_USE_SUBSAMPLE_ENCRYPTION 0x000002 + +#ifdef DRM_ENABLE +typedef enum +{ + UUID_UNKNOWN = -1, + UUID_TFXD, + UUID_TFRF, + UUID_SAMPLE_ENCRYPT, + UUID_PROTECTION_HEADER, +}uuid_type_t; +#endif // DRM_ENABLE +#endif struct _QtDemuxSegment { @@ -225,7 +402,20 @@ struct _QtDemuxSegment guint64 media_stop; gdouble rate; }; - +#ifdef QTDEMUX_MODIFICATION +#ifdef DRM_ENABLE +struct _QtDemuxDrm +{ + drm_trusted_open_decrypt_info_s open_input_data ; + drm_trusted_open_decrypt_resp_data_s open_output_data ; + drm_trusted_set_consumption_state_info_s state_input_data; + drm_permission_type_e status_perm_type; + drm_license_status_e license_status ; + gchar *license_file_path; + drm_bool_type_e is_drm_file; +}; +#endif +#endif struct _QtDemuxStream { GstPad *pad; @@ -253,7 +443,7 @@ struct _QtDemuxStream QtDemuxSample *samples; gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */ guint32 min_duration; /* duration in timescale of first sample, used for figuring out - the framerate, in timescale units */ + the framerate, in timescale units */ /* if we use chunks or samples */ gboolean sampled; @@ -324,7 +514,6 @@ struct _QtDemuxStream GstByteReader stsc; GstByteReader stts; GstByteReader stss; - GstByteReader stps; GstByteReader ctts; gboolean chunks_are_chunks; @@ -356,10 +545,7 @@ struct _QtDemuxStream gboolean stss_present; guint32 n_sample_syncs; guint32 stss_index; - /* stps */ - gboolean stps_present; guint32 n_sample_partial_syncs; - guint32 stps_index; /* ctts */ gboolean ctts_present; guint32 n_composition_times; @@ -374,7 +560,14 @@ struct _QtDemuxStream guint32 def_sample_size; guint32 def_sample_flags; #ifdef QTDEMUX_MODIFICATION + guint32 orientation; TrickPlayInfo *trickplay_info; /* trickplay specific handle */ + guint32 prev_n_samples; + GQueue *frag_queue; /* used for PIFF fragments in chain mode */ + QtDemuxTfraTable *tfra_table; + /* dash specified */ + guint64 dash_seek_offset; + guint64 moof_seeked_time; #endif }; @@ -462,8 +655,16 @@ static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n); static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux); +static GstFlowReturn qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset, + guint64 * length, guint32 fourcc); #ifdef QTDEMUX_MODIFICATION +static void gst_qtdemux_stream_clear (GstQTDemux *qtdemux, QtDemuxStream * stream); +static void gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream); +static gboolean gst_qtdemux_sink_setcaps (GstPad * pad, GstCaps *caps); +static void gst_qtdemux_configure_stream (GstQTDemux * qtdemux, + QtDemuxStream * stream); +static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux); static void gst_qtdemux_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_qtdemux_get_property (GObject * object, guint prop_id, @@ -471,6 +672,14 @@ static void gst_qtdemux_get_property (GObject * object, guint prop_id, static gint32 gst_qtdemux_find_next_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str, guint32 index); static void gst_qtdemux_forward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, guint64 *timestamp); static void gst_qtdemux_backward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, guint64 *timestamp); + +#ifdef DRM_ENABLE +static gboolean qtdemux_get_playready_licence (GstQTDemux * qtdemux, guint8* data, guint size, char* query_file_path); +void test_drm_trusted_operation_cb(drm_trusted_user_operation_info_s *operation_info, void *output_data); +static gboolean qtdemux_parse_playready_system_id(GstQTDemux * qtdemux, GstByteReader *uuid_data); +static uuid_type_t qtdemux_get_uuid_type(GstQTDemux * qtdemux, GstByteReader *uuid_data, gint64 *uuid_offset); +static void qtdemux_post_drm_error (GstQTDemux * qtdemux, int drm_error); +#endif // DRM_ENABLE #endif static void @@ -523,7 +732,7 @@ gst_qtdemux_class_init (GstQTDemuxClass * klass) "Maximum buffer size for mdat atom buffering in case it comes before the moov atom", 1, G_MAXUINT, QTDEMUX_MAX_BUFFER_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - + g_object_class_install_property (gobject_class, PROP_TEMP_LOCATION, g_param_spec_string ("temp-location", "temp-location", "Location for the mdat atom buffering incase it comes before the moov atom", @@ -536,6 +745,11 @@ gst_qtdemux_class_init (GstQTDemuxClass * klass) "(0) Sending Non-Keyframes also in forward trickplay (1) Sending only keyframes in forward trickplay", QTDEMUX_DEFAULT_FWD_TRICKPLAY_MODE, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_PLAYBACK_PROTECTION, + g_param_spec_boolean ("playback-protection", "whether file is playback protected or not", + "TRUE->playback protected, FALSE->not playback protected", FALSE, + G_PARAM_READWRITE)); #endif @@ -554,9 +768,15 @@ gst_qtdemux_init (GstQTDemux * qtdemux, GstQTDemuxClass * klass) qtdemux_sink_activate_push); gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain); gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event); - gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad); qtdemux->state = QTDEMUX_STATE_INITIAL; +#ifdef QTDEMUX_MODIFICATION + gst_pad_set_setcaps_function (qtdemux->sinkpad, gst_qtdemux_sink_setcaps); + gst_pad_set_element_private (qtdemux->sinkpad, qtdemux); + gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad); + + qtdemux->protection_header_present = FALSE; +#endif qtdemux->pullbased = FALSE; qtdemux->posted_redirect = FALSE; qtdemux->neededbytes = 16; @@ -568,7 +788,12 @@ gst_qtdemux_init (GstQTDemux * qtdemux, GstQTDemuxClass * klass) qtdemux->mdatoffset = GST_CLOCK_TIME_NONE; qtdemux->mdatbuffer = NULL; gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME); +#ifdef MULTI_AUDIO + qtdemux->Language_list = NULL; +#endif #ifdef QTDEMUX_MODIFICATION + qtdemux->is_dash = FALSE; + qtdemux->dash_init_offset = GST_CLOCK_TIME_NONE; /* Initialization of properties with default values*/ qtdemux->filename = g_strdup("/tmp/qtdemux"); qtdemux->file = NULL; @@ -576,6 +801,31 @@ gst_qtdemux_init (GstQTDemux * qtdemux, GstQTDemuxClass * klass) qtdemux->filesize = 0; qtdemux->maxbuffersize = QTDEMUX_MAX_BUFFER_SIZE; qtdemux->fwdtrick_mode = QTDEMUX_DEFAULT_FWD_TRICKPLAY_MODE; +#ifdef DRM_ENABLE + qtdemux->encrypt_content = FALSE; + qtdemux->pr_handle = NULL; +#endif + qtdemux->piff_fragmented = FALSE; + qtdemux->max_pop_ts = 0; + qtdemux->playback_protected = FALSE; + qtdemux->need_parsing_moov = FALSE; + qtdemux->sample_count[0] = 0; + qtdemux->sample_count[1] = 0; + qtdemux->current_sample[0] = 0; + qtdemux->current_sample[1] = 0; + qtdemux->sequence_id = 0; + qtdemux->dash_content = FALSE; + qtdemux->sub_sample_count = NULL; + qtdemux->encrypted_data = NULL; + qtdemux->clear_data = NULL; + qtdemux->iv_data_audio = NULL; + qtdemux->iv_data_video = NULL; + qtdemux->no_of_audio_samples = 0; + qtdemux->no_of_video_samples = 0; + qtdemux->moof_offsets = NULL; + qtdemux->need_moof_parsing = FALSE; + qtdemux->mfra_offset = 0; + qtdemux->Subtitle_language_list = NULL; #endif } @@ -610,13 +860,16 @@ gst_qtdemux_set_property (GObject * object, guint prop_id, demux->maxbuffersize = g_value_get_uint (value); break; case PROP_TEMP_LOCATION: - if (demux->filename) + if (demux->filename) g_free (demux->filename); demux->filename = g_strdup (g_value_get_string (value)); break; case PROP_FWDTRICK_MODE: demux->fwdtrick_mode = g_value_get_boolean(value); break; + case PROP_PLAYBACK_PROTECTION: + demux->playback_protected = g_value_get_boolean(value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -624,7 +877,7 @@ gst_qtdemux_set_property (GObject * object, guint prop_id, } static void -gst_qtdemux_get_property (GObject * object, guint prop_id, +gst_qtdemux_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstQTDemux *demux; @@ -640,6 +893,9 @@ gst_qtdemux_get_property (GObject * object, guint prop_id, case PROP_FWDTRICK_MODE: g_value_set_boolean(value, demux->fwdtrick_mode); break; + case PROP_PLAYBACK_PROTECTION: + g_value_set_boolean(value, demux->playback_protected); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -661,7 +917,190 @@ gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux) ("no known streams found")); } } +#ifdef QTDEMUX_MODIFICATION +#ifdef DRM_ENABLE +static gboolean +qtdemux_playready_parse_uuid(GNode * uuid,GstQTDemux * qtdemux) +{ + QtDemuxDrm struct_drm = {0, }; + GstByteReader uuid_data; + guint32 protection_header_size = 0; + guint8 *protection_header_data = NULL; + gint64 uuid_offset = 0; + int ret = -1; + int i = 0; + guint8 *buffer = (guint8 *) uuid->data; + gint len_protection_header = 0; + gboolean is_video = FALSE; + uuid_type_t uuid_type; + struct_drm.license_status = DRM_LICENSE_STATUS_UNDEFINED; + struct_drm.license_file_path = NULL; + struct_drm.is_drm_file = DRM_UNKNOWN; + + gst_byte_reader_init (&uuid_data, buffer, QT_UINT32 (buffer)); + uuid_type = qtdemux_get_uuid_type (qtdemux, &uuid_data, &uuid_offset);/*this function call is neccesary for changing uudi data offset*/ + if(!qtdemux) { + goto fail; + } + if(qtdemux->n_streams) { + for(i = 0 ; i < qtdemux->n_streams;i++ ) { + if(qtdemux->streams[i]->subtype == FOURCC_vide) { + GST_INFO_OBJECT(qtdemux,"video stream is present in content"); + is_video = TRUE; + break; + } + } + } + if (qtdemux->playback_protected && is_video ) { + GST_ERROR_OBJECT (qtdemux, "Video file is playback protected, so exiting now"); + GST_ERROR_OBJECT (qtdemux, "Trusted OPL violation error"); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT_NOKEY, ("Trusted opl violation error"), (NULL)); + goto fail; + } + + if (!qtdemux_parse_playready_system_id (qtdemux, &uuid_data)) { + GST_ERROR_OBJECT (qtdemux, "not a playready system id.."); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), ("not a valid playready system ID")); + goto fail; + } + +/* Enters into if body when valid system ID is present.*/ + gst_byte_reader_skip (&uuid_data, 12); + + gst_byte_reader_get_uint16_le (&uuid_data, &protection_header_size); + + GST_DEBUG_OBJECT(qtdemux,"protection header size: %d ", protection_header_size); + + if (!gst_byte_reader_dup_data(&uuid_data, protection_header_size , &protection_header_data)) { + GST_ERROR_OBJECT (qtdemux, "failed to duplicate bytereader data..."); + GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory")); + goto fail; + } +/* for adding wchar null check */ + protection_header_size = protection_header_size + 2; + + protection_header_data = (guint8 *) realloc (protection_header_data,protection_header_size); + if (!protection_header_data) { + GST_ERROR_OBJECT (qtdemux, "failed to allocate memory..."); + GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory")); + goto fail; + } + +/* adding wchar null character */ + protection_header_data[protection_header_size-2] = '\0'; + protection_header_data[protection_header_size-1] = '\0'; + + GST_DEBUG_OBJECT(qtdemux,"protection header size: %d ", protection_header_size); + + struct_drm.license_file_path = (gchar*)calloc((QTDEMUX_DRM_WORD_SIZE + 32 + protection_header_size), sizeof(gchar)); + if(struct_drm.license_file_path == NULL) { + GST_ERROR_OBJECT (qtdemux, "failed to allocate memory..."); + GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory")); + goto fail; + } + + strcpy(struct_drm.license_file_path, "ismv"); + + struct_drm.license_file_path[4] = '?'; + for(i = 0; i < 16; i++) { + sprintf(struct_drm.license_file_path + 4 + 1 + (i*2), "%02x", qtdemux->uuid_protection[i]); + } + + for(i = 0; i < 8; i++) { + struct_drm.license_file_path[(QTDEMUX_DRM_WORD_SIZE - 13 ) + i] = '0'; + } + + for(i = 0; i < 16; i++) { + sprintf(struct_drm.license_file_path + (QTDEMUX_DRM_WORD_SIZE -5 ) + (i*2), "%02x", qtdemux->uuid_playready[i]); + } + + struct_drm.license_file_path[(QTDEMUX_DRM_WORD_SIZE -5 ) + 32] = '?'; + len_protection_header = sprintf(struct_drm.license_file_path + (QTDEMUX_DRM_WORD_SIZE -5 ) + 32 + 1 , "%d", protection_header_size); + GST_DEBUG_OBJECT(qtdemux, "protection header size length %d", len_protection_header); + + for(i = 0; i < protection_header_size; i++) { + struct_drm.license_file_path[(QTDEMUX_DRM_WORD_SIZE -5 ) + 32 + 1 + len_protection_header + i] = protection_header_data[i]; + } + + GST_DEBUG_OBJECT(qtdemux, "ismv header is %s", struct_drm.license_file_path); + + ret = drm_is_drm_file(struct_drm.license_file_path, &struct_drm.is_drm_file); + if(DRM_RETURN_SUCCESS != ret) { + GST_ERROR_OBJECT (qtdemux, "Failed to read is DRM_FILE information"); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("Failed to read is DRM_FILE information"), (NULL)); + goto fail; + } + + GST_DEBUG_OBJECT(qtdemux, "is drm is drm %d", struct_drm.is_drm_file); + + + struct_drm.status_perm_type = DRM_PERMISSION_TYPE_PLAY; + ret = drm_get_license_status (struct_drm.license_file_path, struct_drm.status_perm_type, &struct_drm.license_status); + + if (DRM_RETURN_SUCCESS != ret) { + GST_ERROR_OBJECT (qtdemux, "failed to get license status : 0x%x", ret); + qtdemux_post_drm_error (qtdemux, DRM_LICENSE_STATUS_UNDEFINED); + goto fail; + } + + if (DRM_LICENSE_STATUS_VALID != struct_drm.license_status) { + GST_ERROR ("DRM license status is not valid, license_status=%d", struct_drm.license_status); + qtdemux_post_drm_error (qtdemux, struct_drm.license_status); + goto fail; + } + + GST_DEBUG_OBJECT (qtdemux, "successfully got the license status.."); + + +#ifdef GET_DRM_LICENSE + license_ret = qtdemux_get_playready_licence(qtdemux, protection_header_data, protection_header_size); + if(!license_ret) { + GST_ERROR_OBJECT (qtdemux, "failed to get playready license"); + goto fail; + } +#endif + + struct_drm.open_input_data.file_type = DRM_TRUSTED_TYPE_PIFF; + struct_drm.open_input_data.permission = DRM_TRUSTED_PERMISSION_TYPE_PLAY; + struct_drm.open_input_data.operation_callback.callback = test_drm_trusted_operation_cb; + struct_drm.open_input_data.lic_header.header = (unsigned char *) protection_header_data; + struct_drm.open_input_data.lic_header.header_len = protection_header_size; + +/* Open Decrypt Session*/ + ret = drm_trusted_open_decrypt_session(&struct_drm.open_input_data, &struct_drm.open_output_data, &(qtdemux->pr_handle)); + if (DRM_TRUSTED_RETURN_SUCCESS != ret) { + GST_ERROR_OBJECT (qtdemux, "failed to open decrypt session"); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("failed to open decrypt session"), (NULL)); + goto fail; + } + +/* Before Read, Appropriate state MUST be SET */ + struct_drm.state_input_data.state = DRM_CONSUMPTION_STARTED; + ret = drm_trusted_set_decrypt_state(qtdemux->pr_handle, &struct_drm.state_input_data); + if (DRM_TRUSTED_RETURN_SUCCESS != ret) { + GST_ERROR_OBJECT (qtdemux, "failed to set decrypt state..."); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("failed to set decrypt state"), (NULL)); + goto fail; + } + qtdemux->encrypt_content = TRUE; + + free(struct_drm.license_file_path); + free (protection_header_data); + return TRUE; +fail: + if (protection_header_data) { + free (protection_header_data); + } + if(struct_drm.license_file_path) { + free(struct_drm.license_file_path); + struct_drm.license_file_path = NULL; + } + GST_ERROR_OBJECT(qtdemux,"fail to parse uuid atom in qtdemux_playready_parse_uuid func "); + return FALSE; +} +#endif +#endif static GstFlowReturn gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size, GstBuffer ** buf) @@ -836,13 +1275,35 @@ gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query) gst_query_parse_duration (query, &fmt, NULL); if (fmt == GST_FORMAT_TIME) { - gint64 duration = -1; + /* First try to query upstream */ + res = gst_pad_query_default (pad, query); + if (!res) { + gint64 duration = -1; + + gst_qtdemux_get_duration (qtdemux, &duration); + if (duration > 0) { + gst_query_set_duration (query, GST_FORMAT_TIME, duration); + res = TRUE; + } + } +#ifdef QTDEMUX_MODIFICATION + else { + gint64 duration = -1; - gst_qtdemux_get_duration (qtdemux, &duration); - if (duration > 0) { - gst_query_set_duration (query, GST_FORMAT_TIME, duration); - res = TRUE; + gst_query_parse_duration(query, &fmt, &duration); + + if (duration <= 0) { + gst_qtdemux_get_duration (qtdemux, &duration); + + GST_LOG_OBJECT (pad, "set duration %"G_GUINT64_FORMAT, duration); + + if (duration > 0) { + gst_query_set_duration (query, GST_FORMAT_TIME, duration); + res = TRUE; + } + } } +#endif } break; } @@ -1406,6 +1867,46 @@ no_format: } } +#ifdef QTDEMUX_MODIFICATION +static gboolean +gst_qtdemux_update_moof_offset (GstQTDemux * qtdemux, QtDemuxStream *stream, gint64 desired_offset, guint64 *moof_offset) +{ + guint32 idx = 0; + QtDemuxTfraTable *tfra_table = stream->tfra_table; + QtDemuxTfraEntryInfo *entry = NULL; + gboolean found_entry = FALSE; + guint64 time = 0; + + entry = &g_array_index (tfra_table->tfra_entries, QtDemuxTfraEntryInfo, idx); + *moof_offset = entry->moof_offset; + + for (idx = 0; idx < tfra_table->n_entries; idx++) { + entry = &g_array_index (tfra_table->tfra_entries, QtDemuxTfraEntryInfo, idx); + if (gst_util_uint64_scale (entry->time, GST_SECOND, stream->timescale) > desired_offset) { + GST_INFO_OBJECT (stream->pad, "found moof time greater than desired time..."); + found_entry = TRUE; + break; + } + *moof_offset = entry->moof_offset; + time = entry->time; + } + + if (!found_entry) { + GST_WARNING_OBJECT (qtdemux, "Did not find entry...tfra table is not complete"); + *moof_offset = -1; + stream->moof_seeked_time = 0; + return FALSE; + } + + stream->moof_seeked_time = time; + + GST_INFO_OBJECT (stream->pad, "desired moof_offset = %"G_GUINT64_FORMAT" & fragment start time = %"GST_TIME_FORMAT, + *moof_offset, gst_util_uint64_scale (time, GST_SECOND, stream->timescale)); + + return TRUE; +} +#endif + /* perform the seek. * * We set all segment_indexes in the streams to unknown and @@ -1427,6 +1928,10 @@ gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment) { gint64 desired_offset; gint n; +#ifdef QTDEMUX_MODIFICATION + guint64 min_moof_offset = -1; + gboolean bret = TRUE; +#endif desired_offset = segment->last_stop; @@ -1464,6 +1969,52 @@ gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment) segment->last_stop = desired_offset; segment->time = desired_offset; +#ifdef QTDEMUX_MODIFICATION + if (qtdemux->fragmented) { + QtDemuxTfraEntryInfo *entry = NULL; + gint count = 0; + + for (n = 0; n < qtdemux->n_streams; n++) { + QtDemuxStream *stream = qtdemux->streams[n]; + guint64 cur_moof_offset = 0; + + if (stream->tfra_table && bret) { + /* all streams' tfra should be complete.. otherwise ignore seeking using tfra/mfra */ + bret = gst_qtdemux_update_moof_offset (qtdemux, stream, desired_offset, &cur_moof_offset); + count++; + if (!bret) { + min_moof_offset = -1; + break; + } + min_moof_offset = min_moof_offset < cur_moof_offset ? min_moof_offset : cur_moof_offset; + GST_INFO_OBJECT (qtdemux, "min moof offset = %"G_GUINT64_FORMAT, min_moof_offset < cur_moof_offset ? min_moof_offset : cur_moof_offset); + } + } + + if (min_moof_offset != -1 && (count == qtdemux->n_streams)) { + GST_INFO_OBJECT (qtdemux, "updated moof_offset = %" G_GUINT64_FORMAT, min_moof_offset); + for (n = 0; n < qtdemux->moof_offsets->len; n++) { + entry = &g_array_index (qtdemux->moof_offsets, QtDemuxTfraEntryInfo, n); + if (entry->moof_offset == min_moof_offset) + break; + } + GST_INFO_OBJECT (qtdemux, "updated moof index = %d", n); + qtdemux->moof_offset = qtdemux->offset = min_moof_offset; + qtdemux->need_moof_parsing = TRUE; + + GST_INFO_OBJECT (qtdemux, "after seek : offset = %" G_GUINT64_FORMAT, min_moof_offset); + + /*reset sample table */ + for (n = 0; n < qtdemux->n_streams; n++) { + QtDemuxStream *stream = qtdemux->streams[n]; + g_free(stream->samples); + stream->n_samples = 0; + stream->stbl_index = -1; + } + } + } +#endif + /* we stop at the end */ if (segment->stop == -1) segment->stop = segment->duration; @@ -1479,7 +2030,7 @@ gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event) GstFormat format; GstSeekFlags flags; GstSeekType cur_type, stop_type; - gint64 cur, stop; + gint64 cur = 0, stop; gboolean flush; gboolean update; GstSegment seeksegment; @@ -1657,8 +2208,11 @@ gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event) } if (qtdemux->pullbased) { res = gst_qtdemux_do_seek (qtdemux, pad, event); - } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams && - !qtdemux->fragmented) { + } else if (gst_pad_push_event (qtdemux->sinkpad, gst_event_ref (event))) { + GST_DEBUG_OBJECT (qtdemux, "Upstream successfully seeked"); + res = TRUE; + } else if (qtdemux->state == QTDEMUX_STATE_MOVIE && qtdemux->n_streams + && !qtdemux->fragmented) { res = gst_qtdemux_do_push_seek (qtdemux, pad, event); } else { GST_DEBUG_OBJECT (qtdemux, @@ -1763,6 +2317,178 @@ gst_qtdemux_find_sample (GstQTDemux * qtdemux, gint64 byte_pos, gboolean fw, if (_index) *_index = index; } +#ifdef QTDEMUX_MODIFICATION +static void +gst_qtdemux_reset(GstQTDemux *qtdemux, gboolean hard) +{ + gint n; + gint ret; + + qtdemux->offset = 0; + gst_adapter_clear (qtdemux->adapter); + qtdemux->neededbytes = -1; + + if(hard || qtdemux->is_dash) + { + qtdemux->state = QTDEMUX_STATE_INITIAL; + qtdemux->neededbytes = 16; + qtdemux->todrop = 0; + qtdemux->pullbased = FALSE; + qtdemux->posted_redirect = FALSE; + qtdemux->first_mdat = -1; + qtdemux->header_size = 0; + qtdemux->mdatoffset = GST_CLOCK_TIME_NONE; + if (qtdemux->mdatbuffer) + gst_buffer_unref (qtdemux->mdatbuffer); + qtdemux->mdatbuffer = NULL; + if (qtdemux->comp_brands) + gst_buffer_unref (qtdemux->comp_brands); + qtdemux->comp_brands = NULL; + if (qtdemux->tag_list) + gst_tag_list_free (qtdemux->tag_list); + qtdemux->tag_list = NULL; + gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME); + qtdemux->requested_seek_time = GST_CLOCK_TIME_NONE; + qtdemux->seek_offset = 0; + qtdemux->upstream_seekable = FALSE; + qtdemux->upstream_size = 0; + qtdemux->dash_init_offset = GST_CLOCK_TIME_NONE; + } + + if(hard) + { + for (n = 0; n < qtdemux->n_streams; n++) { + gst_qtdemux_stream_free (qtdemux, qtdemux->streams[n]); + qtdemux->streams[n] = NULL; + } + + qtdemux->got_moov = FALSE; + qtdemux->major_brand = 0; + qtdemux->n_streams = 0; + qtdemux->n_video_streams = 0; + qtdemux->n_audio_streams = 0; + qtdemux->n_sub_streams = 0; + qtdemux->is_dash = FALSE; + qtdemux->video_max_width = 0; + qtdemux->video_max_height = 0; + + if (qtdemux->element_index) + gst_object_unref (qtdemux->element_index); + qtdemux->element_index = NULL; + // TODO: Check new modifications + if (qtdemux->file) { + fclose (qtdemux->file); + if (qtdemux->ofile) + fclose (qtdemux->ofile); + if(!remove (qtdemux->filename)) GST_DEBUG_OBJECT (qtdemux, "Error removing temp file"); + } + qtdemux->file = NULL; + qtdemux->ofile = NULL; + qtdemux->filesize = 0; + qtdemux->maxbuffersize = QTDEMUX_MAX_BUFFER_SIZE; +#ifdef DRM_ENABLE + if (qtdemux->pr_handle) { + if (!drm_trusted_close_decrypt_session(&qtdemux->pr_handle)) { + GST_ERROR_OBJECT (qtdemux, "failed to close decrypt session..."); + } + } + + ret = drm_process_request(DRM_REQUEST_TYPE_CLIENT_CLEAN_UP, NULL, NULL); + if (DRM_RETURN_SUCCESS == ret) { + GST_INFO_OBJECT(qtdemux, "Clean Up successful!!"); + } else { + GST_ERROR_OBJECT (qtdemux, "Clean Up Failed!!, ret = 0x%x", ret); + } + + ret = drm_trusted_handle_request(DRM_TRUSTED_REQ_TYPE_CLIENT_CLEAN_UP, NULL, NULL); + if (DRM_RETURN_SUCCESS == ret) { + GST_INFO_OBJECT(qtdemux, "Clean Up successful!!"); + } else { + GST_ERROR_OBJECT (qtdemux, "Clean Up Failed!!, ret = 0x%x", ret); + } + + qtdemux->encrypt_content = FALSE; +#endif +#ifdef MULTI_AUDIO + if(qtdemux->Language_list) + { + g_list_free(qtdemux->Language_list); + qtdemux->Language_list = NULL; + } +#endif + + if (qtdemux->Subtitle_language_list) { + g_list_free (qtdemux->Subtitle_language_list); + qtdemux->Subtitle_language_list = NULL; + } + + if(qtdemux->sub_sample_count) { + g_free(qtdemux->sub_sample_count); + qtdemux->sub_sample_count = NULL; + } + if(qtdemux->clear_data) { + g_free(qtdemux->clear_data); + qtdemux->clear_data = NULL; + } + if(qtdemux->encrypted_data) { + g_free(qtdemux->encrypted_data); + qtdemux->encrypted_data = NULL; + } + + if(qtdemux->iv_data_audio) { + for(int i = 0; i < qtdemux->no_of_audio_samples; i++) { + g_free(qtdemux->iv_data_audio[i]); + qtdemux->iv_data_audio[i] = NULL; + } + g_free(qtdemux->iv_data_audio); + qtdemux->iv_data_audio = NULL; + } + + if(qtdemux->iv_data_video) { + for(int i = 0; i < qtdemux->no_of_video_samples; i++) { + g_free(qtdemux->iv_data_video[i]); + qtdemux->iv_data_video[i] = NULL; + } + g_free(qtdemux->iv_data_video); + qtdemux->iv_data_video = NULL; + } + }else if(qtdemux->is_dash){ + for (n = 0; n < qtdemux->n_streams; n++) { + gst_qtdemux_stream_clear(qtdemux,qtdemux->streams[n]); + qtdemux->streams[n]->dash_seek_offset = GST_CLOCK_TIME_NONE; + } + }else{ + for (n = 0; n < qtdemux->n_streams; n++) { + qtdemux->streams[n]->last_ret = GST_FLOW_OK; + qtdemux->streams[n]->sent_eos = FALSE; + } + } +} + +static gboolean +gst_qtdemux_sink_setcaps (GstPad * pad, GstCaps *caps) +{ + const gchar *variant; + GstStructure *structure; + GstQTDemux *demux = GST_QTDEMUX_CAST (gst_pad_get_element_private(pad)); + + structure = gst_caps_get_structure (caps, 0); + variant = gst_structure_get_string (structure, "variant"); + if(variant && strcmp (variant,"dash-fragmented") == 0) { + demux->is_dash = TRUE; + demux->fragmented = TRUE; + } + + if (gst_structure_has_field(structure, "max-width") + && gst_structure_has_field(structure, "max-height")) + { + gst_structure_get_int(structure, "max-width", &demux->video_max_width); + gst_structure_get_int(structure, "max-height", &demux->video_max_height); + } + + return gst_pad_set_caps (pad, caps); +} +#endif static gboolean gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event) @@ -1793,6 +2519,31 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event) "received format %d newsegment %" GST_SEGMENT_FORMAT, format, &segment); +#ifdef QTDEMUX_MODIFICATION + /*Processing event from dashdemux after seeking.*/ + if(demux->is_dash && format == GST_FORMAT_TIME) + { + int i; + demux->dash_init_offset = start; + + /* allows seek for multiple streams */ + for (i = 0; i < demux->n_streams; i++) { + demux->streams[i]->dash_seek_offset = start; + } + + gst_segment_set_newsegment_full (&demux->segment, update, rate, arate, + GST_FORMAT_TIME, start, stop, start); + GST_DEBUG_OBJECT (demux, "Pushing newseg update %d, rate %g, " + "applied rate %g, format %d, start %" GST_TIME_FORMAT ", " + "stop %" GST_TIME_FORMAT, update, rate, arate, GST_FORMAT_TIME, + GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); + gst_qtdemux_push_event (demux, + gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME, + start, stop, start)); + goto exit; + } +#endif + /* chain will send initial newsegment after pads have been added */ if (demux->state != QTDEMUX_STATE_MOVIE || !demux->n_streams) { GST_DEBUG_OBJECT (demux, "still starting, eating event"); @@ -1867,6 +2618,9 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event) } case GST_EVENT_FLUSH_STOP: { +#ifdef QTDEMUX_MODIFICATION + gst_qtdemux_reset(demux,FALSE); +#else gint i; /* clean up, force EOS if no more info follows */ @@ -1878,6 +2632,7 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event) demux->streams[i]->last_ret = GST_FLOW_OK; demux->streams[i]->sent_eos = FALSE; } +#endif break; } case GST_EVENT_EOS: @@ -1886,6 +2641,23 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event) if (!demux->pullbased) { gint i; gboolean has_valid_stream = FALSE; + +#ifdef QTDEMUX_MODIFICATION + if (demux->piff_fragmented) { + GstBuffer *pop_buf = NULL; + GstFlowReturn fret = GST_FLOW_OK; + + for (i = 0; i < demux->n_streams; i++) { + if (demux->streams[i]->pad != NULL) { + while (!g_queue_is_empty (demux->streams[i]->frag_queue)) { + pop_buf = g_queue_pop_head (demux->streams[i]->frag_queue); + fret = gst_pad_push (demux->streams[i]->pad, pop_buf); + GST_DEBUG_OBJECT (demux->streams[i]->pad, "pushing from event_func : %s\n", gst_flow_get_name (fret)); + } + } + } + } +#endif for (i = 0; i < demux->n_streams; i++) { if (demux->streams[i]->pad != NULL) { has_valid_stream = TRUE; @@ -1942,7 +2714,367 @@ gst_qtdemux_get_index (GstElement * element) return result; } - +#ifdef QTDEMUX_MODIFICATION +static void +mpeg4_util_par_from_info (guint8 aspect_ratio_info, guint8 * par_width, + guint8 * par_height) +{ + switch (aspect_ratio_info) { + case 0x02: + *par_width = 12; + *par_height = 11; + break; + case 0x03: + *par_width = 10; + *par_height = 11; + break; + case 0x04: + *par_width = 16; + *par_height = 11; + break; + case 0x05: + *par_width = 40; + *par_height = 33; + break; + + case 0x01: + default: + *par_width = 1; + *par_height = 1; + } +} + +static gboolean +parse_quant (GstBitReader * br, guint8 quant_mat[64], + const guint8 default_quant_mat[64], guint8 * load_quant_mat) +{ + READ_UINT8 (br, *load_quant_mat, 1); + if (*load_quant_mat) { + guint i; + guint8 val; + + val = 1; + for (i = 0; i < 64; i++) { + + if (val != 0) + READ_UINT8 (br, val, 8); + + if (val == 0) { + if (i == 0) + goto invalid_quant_mat; + quant_mat[mpeg4_zigzag_8x8[i]] = quant_mat[mpeg4_zigzag_8x8[i - 1]]; + } else + quant_mat[mpeg4_zigzag_8x8[i]] = val; + } + } else + memcpy (quant_mat, default_quant_mat, 64); + + return TRUE; + +failed: + GST_WARNING ("failed parsing quant matrix"); + return FALSE; + +invalid_quant_mat: + GST_WARNING ("the first value should be non zero"); + goto failed; +} +static GstMpeg4ParseResult gst_mpeg4_parse_video_object_layer (GstMpeg4VideoObjectLayer * vol, + GstMpeg4VisualObject * vo, const guint8 * data, gsize size) +{ + guint8 video_object_layer_start_code; + /* Used for enums types */ + guint8 tmp; + GstBitReader br = GST_BIT_READER_INIT (data, size); + + g_return_val_if_fail (vol != NULL, GST_MPEG4_PARSER_ERROR); + + GST_DEBUG ("Parsing video object layer"); + + READ_UINT8 (&br, video_object_layer_start_code, 8); + if (!(video_object_layer_start_code >= 0x20 && + video_object_layer_start_code <= 0x2f)) + goto wrong_start_code; + + /* set default values */ + if (vo) { + vol->verid = vo->verid; + vol->priority = vo->priority; + } + + vol->low_delay = FALSE; + vol->chroma_format = 1; + vol->vbv_parameters = FALSE; + vol->quant_precision = 5; + vol->bits_per_pixel = 8; + vol->quarter_sample = FALSE; + vol->newpred_enable = FALSE; + vol->interlaced = 0; + vol->width = 0; + vol->height = 0; + + READ_UINT8 (&br, vol->random_accessible_vol, 1); + READ_UINT8 (&br, vol->video_object_type_indication, 8); + READ_UINT8 (&br, vol->is_object_layer_identifier, 1); + if (vol->is_object_layer_identifier) { + READ_UINT8 (&br, vol->verid, 4); + READ_UINT8 (&br, vol->priority, 3); + } + + READ_UINT8 (&br, tmp, 4); + vol->aspect_ratio_info = tmp; + if (vol->aspect_ratio_info != GST_MPEG4_EXTENDED_PAR) { + mpeg4_util_par_from_info (vol->aspect_ratio_info, &vol->par_width, + &vol->par_height); + + } else { + gint v; + + READ_UINT8 (&br, vol->par_width, 8); + v = vol->par_width; + CHECK_ALLOWED (v, 1, 255); + + READ_UINT8 (&br, vol->par_height, 8); + v = vol->par_height; + CHECK_ALLOWED (v, 1, 255); + } + READ_UINT8 (&br, vol->control_parameters, 1); + if (vol->control_parameters) { + guint8 chroma_format; + + READ_UINT8 (&br, chroma_format, 2); + vol->chroma_format = chroma_format; + READ_UINT8 (&br, vol->low_delay, 1); + READ_UINT8 (&br, vol->vbv_parameters, 1); + if (vol->vbv_parameters) { + CHECK_REMAINING (&br, 79); + + vol->first_half_bitrate = + gst_bit_reader_get_bits_uint16_unchecked (&br, 15); + MARKER_UNCHECKED (&br); + + vol->latter_half_bitrate = + gst_bit_reader_get_bits_uint16_unchecked (&br, 15); + MARKER_UNCHECKED (&br); + + vol->bit_rate = + (vol->first_half_bitrate << 15) | vol->latter_half_bitrate; + + vol->first_half_vbv_buffer_size = + gst_bit_reader_get_bits_uint16_unchecked (&br, 15); + GST_ERROR("reading the vol->first_half_vbv_buffer_size 15 bit = %x",vol->first_half_vbv_buffer_size); + MARKER_UNCHECKED (&br); + + vol->latter_half_vbv_buffer_size = + gst_bit_reader_get_bits_uint8_unchecked (&br, 3); + MARKER_UNCHECKED (&br); + + vol->vbv_buffer_size = (vol->first_half_vbv_buffer_size << 15) | + vol->latter_half_vbv_buffer_size; + + vol->first_half_vbv_occupancy = + gst_bit_reader_get_bits_uint16_unchecked (&br, 11); + MARKER_UNCHECKED (&br); + + vol->latter_half_vbv_occupancy = + gst_bit_reader_get_bits_uint16_unchecked (&br, 15); + MARKER_UNCHECKED (&br); + } + } + + READ_UINT8 (&br, tmp, 2); + vol->shape = tmp; + + if (vol->shape == GST_MPEG4_GRAYSCALE) { + /* TODO support grayscale shapes, for now we just pass */ + + /* Something the standard starts to define... */ + GST_ERROR ("Grayscale shaped not supported"); + goto failed; + } + + if (vol->shape == GST_MPEG4_GRAYSCALE && vol->verid != 0x01) + READ_UINT8 (&br, vol->shape_extension, 4); + CHECK_REMAINING (&br, 19); + + MARKER_UNCHECKED (&br); + vol->vop_time_increment_resolution = + gst_bit_reader_get_bits_uint16_unchecked (&br, 16); + if (vol->vop_time_increment_resolution < 1) { + GST_ERROR ("value not in allowed range. value: %d, range %d-%d", + vol->vop_time_increment_resolution, 1, G_MAXUINT16); + goto failed; + } + vol->vop_time_increment_bits = + g_bit_storage (vol->vop_time_increment_resolution); + MARKER_UNCHECKED (&br); + vol->fixed_vop_rate = gst_bit_reader_get_bits_uint8_unchecked (&br, 1); + if (vol->fixed_vop_rate) + READ_UINT16 (&br, vol->fixed_vop_time_increment, + vol->vop_time_increment_bits); + + if (vol->shape != GST_MPEG4_BINARY_ONLY) { + if (vol->shape == GST_MPEG4_RECTANGULAR) { + CHECK_REMAINING (&br, 29); + + MARKER_UNCHECKED (&br); + vol->width = gst_bit_reader_get_bits_uint16_unchecked (&br, 13); + MARKER_UNCHECKED (&br); + vol->height = gst_bit_reader_get_bits_uint16_unchecked (&br, 13); + MARKER_UNCHECKED (&br); + } + + READ_UINT8 (&br, vol->interlaced, 1); + READ_UINT8 (&br, vol->obmc_disable, 1); + + if (vol->verid == 0x1) { + READ_UINT8 (&br, tmp, 1); + vol->sprite_enable = tmp; + } else { + READ_UINT8 (&br, tmp, 2); + vol->sprite_enable = tmp; + } + if (vol->sprite_enable == GST_MPEG4_SPRITE_STATIC || + vol->sprite_enable == GST_MPEG4_SPRITE_GMG) { + + if (vol->sprite_enable == GST_MPEG4_SPRITE_GMG) + CHECK_REMAINING (&br, 9); + else { + CHECK_REMAINING (&br, 65); + + vol->sprite_width = gst_bit_reader_get_bits_uint16_unchecked (&br, 13); + MARKER_UNCHECKED (&br); + vol->sprite_height = gst_bit_reader_get_bits_uint16_unchecked (&br, 13); + MARKER_UNCHECKED (&br); + + vol->sprite_left_coordinate = + gst_bit_reader_get_bits_uint16_unchecked (&br, 13); + MARKER_UNCHECKED (&br); + + vol->sprite_top_coordinate = + gst_bit_reader_get_bits_uint16_unchecked (&br, 13); + MARKER_UNCHECKED (&br); + } + vol->no_of_sprite_warping_points = + gst_bit_reader_get_bits_uint8_unchecked (&br, 6); + vol->sprite_warping_accuracy = + gst_bit_reader_get_bits_uint8_unchecked (&br, 2); + vol->sprite_brightness_change = + gst_bit_reader_get_bits_uint8_unchecked (&br, 1); + if (vol->sprite_enable != GST_MPEG4_SPRITE_GMG) { + vol->low_latency_sprite_enable = + gst_bit_reader_get_bits_uint8_unchecked (&br, 1); + } + } + + if (vol->shape != GST_MPEG4_RECTANGULAR) { + READ_UINT8 (&br, vol->sadct_disable, 1); + } + READ_UINT8 (&br, vol->not_8_bit, 1); + if (vol->not_8_bit) { + READ_UINT8 (&br, vol->quant_precision, 4); + CHECK_ALLOWED (vol->quant_precision, 3, 9); + READ_UINT8 (&br, vol->bits_per_pixel, 4); + CHECK_ALLOWED (vol->bits_per_pixel, 4, 12); + } + + if (vol->shape == GST_MPEG4_GRAYSCALE) { + /* We don't actually support it */ + READ_UINT8 (&br, vol->no_gray_quant_update, 1); + READ_UINT8 (&br, vol->composition_method, 1); + READ_UINT8 (&br, vol->linear_composition, 1); + } + + READ_UINT8 (&br, vol->quant_type, 1); + if (vol->quant_type) { + if (!parse_quant (&br, vol->intra_quant_mat, default_intra_quant_mat, + &vol->load_intra_quant_mat)) + goto failed; + + if (!parse_quant (&br, vol->non_intra_quant_mat, + default_non_intra_quant_mat, &vol->load_non_intra_quant_mat)) + goto failed; + + if (vol->shape == GST_MPEG4_GRAYSCALE) { + /* Something the standard starts to define... */ + GST_ERROR ("Grayscale shaped not supported"); + goto failed; + } + + } else { + memset (&vol->intra_quant_mat, 0, 64); + memset (&vol->non_intra_quant_mat, 0, 64); + } + + if (vol->verid != 0x1) + READ_UINT8 (&br, vol->quarter_sample, 1); + + READ_UINT8 (&br, vol->complexity_estimation_disable, 1); + if (!vol->complexity_estimation_disable) + goto complexity_estimation_error; + + + READ_UINT8 (&br, vol->resync_marker_disable, 1); + READ_UINT8 (&br, vol->data_partitioned, 1); + GST_DEBUG("reading vol->data_partitioned 1 bit = %x ",vol->data_partitioned); + if (vol->data_partitioned) + { + READ_UINT8 (&br, vol->reversible_vlc, 1); + } + } + /* ... */ + + return GST_MPEG4_PARSER_OK; + +failed: + GST_ERROR ("failed parsing \"Video Object Layer\""); + return GST_MPEG4_PARSER_ERROR; + +wrong_start_code: + GST_ERROR ("got buffer with wrong start code"); + goto failed; + +complexity_estimation_error: + GST_ERROR ("don't support complexity estimation"); + goto failed; +} +static GstMpeg4ParseResult +gst_mpeg4_parse_visual_object (GstMpeg4VisualObject * vo, const guint8 * data, gsize size) +{ + guint8 vo_start_code, type; + GstBitReader br = GST_BIT_READER_INIT (data, size); + + g_return_val_if_fail (vo != NULL, GST_MPEG4_PARSER_ERROR); + + GST_DEBUG ("Parsing visual object"); + + READ_UINT8 (&br, vo_start_code, 8); + if (vo_start_code != 0xb5) + goto wrong_start_code; + + /* set default values */ + vo->verid = 0x1; + vo->priority = 1; + READ_UINT8 (&br, vo->is_identifier, 1); + if (vo->is_identifier) { + READ_UINT8 (&br, vo->verid, 4); + READ_UINT8 (&br, vo->priority, 3); + } + + READ_UINT8 (&br, type, 4); + vo->type = type; + return GST_MPEG4_PARSER_OK; + +wrong_start_code: + GST_ERROR ("got buffer with wrong start code"); + return GST_MPEG4_PARSER_ERROR; + +failed: + GST_ERROR ("failed parsing \"Visual Object\""); + return GST_MPEG4_PARSER_ERROR; +} +#endif + static void gst_qtdemux_stbl_free (QtDemuxStream * stream) { @@ -1956,34 +3088,115 @@ gst_qtdemux_stbl_free (QtDemuxStream * stream) stream->stts.data = NULL; g_free ((gpointer) stream->stss.data); stream->stss.data = NULL; - g_free ((gpointer) stream->stps.data); - stream->stps.data = NULL; g_free ((gpointer) stream->ctts.data); stream->ctts.data = NULL; } +#ifdef QTDEMUX_MODIFICATION static void -gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream) +gst_qtdemux_stream_clear (GstQTDemux *qtdemux, QtDemuxStream *stream) { while (stream->buffers) { gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data)); stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers); } - if (stream->pad) - gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad); + +#ifdef DRM_ENABLE + if (qtdemux->encrypt_content && qtdemux->dash_content == FALSE) { + int i =0; + + for (i = 0 ; i < stream->n_samples; i++) { + if (stream->samples[i].sub_encry) { + if (stream->samples[i].sub_encry->sub_entry) { + g_free (stream->samples[i].sub_encry->sub_entry); + stream->samples[i].sub_encry->sub_entry = NULL; + } + free (stream->samples[i].sub_encry); + stream->samples[i].sub_encry = NULL; + } + free(stream->samples[i].iv); + stream->samples[i].iv = NULL; + } + } +#endif + + if (qtdemux->piff_fragmented && stream->frag_queue) { + while (!g_queue_is_empty (stream->frag_queue)) { + GstBuffer *buf = g_queue_pop_head (stream->frag_queue); + gst_buffer_unref (buf); + } + } + g_free (stream->samples); - if (stream->caps) - gst_caps_unref (stream->caps); + stream->samples = NULL; g_free (stream->segments); + stream->segments = NULL; + if (stream->pending_tags) gst_tag_list_free (stream->pending_tags); + stream->pending_tags = NULL; g_free (stream->redirect_uri); + stream->redirect_uri = NULL; /* free stbl sub-atoms */ gst_qtdemux_stbl_free (stream); -#ifdef QTDEMUX_MODIFICATION + if (stream->trickplay_info) g_free (stream->trickplay_info); + stream->trickplay_info = NULL; + + stream->last_ret = GST_FLOW_OK; + stream->sent_eos = FALSE; + stream->segment_index = -1; + stream->time_position = 0; + stream->sample_index = -1; + stream->stbl_index = -1; + stream->n_samples = 0; +} +#endif + +static void +gst_qtdemux_stream_free (GstQTDemux * qtdemux, QtDemuxStream * stream) +{ +#ifdef QTDEMUX_MODIFICATION + gst_qtdemux_stream_clear(qtdemux, stream); + + if (qtdemux->piff_fragmented && stream->frag_queue) { + g_queue_free (stream->frag_queue); + stream->frag_queue = NULL; + } +#else + while (stream->buffers) { + gst_buffer_unref (GST_BUFFER_CAST (stream->buffers->data)); + stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers); + } + + g_free (stream->samples); + stream->samples = NULL; + g_free (stream->segments); + stream->segments = NULL; + + if (stream->pending_tags) + gst_tag_list_free (stream->pending_tags); + stream->pending_tags = NULL; + g_free (stream->redirect_uri); + stream->redirect_uri = NULL; + /* free stbl sub-atoms */ + gst_qtdemux_stbl_free (stream); + + stream->last_ret = GST_FLOW_OK; + stream->sent_eos = FALSE; + stream->segment_index = -1; + stream->time_position = 0; + stream->sample_index = -1; + stream->stbl_index = -1; + stream->n_samples = 0; #endif + + if (stream->caps) + gst_caps_unref (stream->caps); + if (stream->pad) + gst_element_remove_pad (GST_ELEMENT_CAST (qtdemux), stream->pad); + g_free (stream); } @@ -2004,6 +3217,9 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY:{ +#ifdef QTDEMUX_MODIFICATION + gst_qtdemux_reset(qtdemux,TRUE); +#else gint n; qtdemux->state = QTDEMUX_STATE_INITIAL; @@ -2016,6 +3232,13 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition) qtdemux->header_size = 0; qtdemux->got_moov = FALSE; qtdemux->mdatoffset = GST_CLOCK_TIME_NONE; +#ifdef MULTI_AUDIO + if(qtdemux->Language_list) + { + g_list_free(qtdemux->Language_list); + qtdemux->Language_list = NULL; + } +#endif if (qtdemux->mdatbuffer) gst_buffer_unref (qtdemux->mdatbuffer); qtdemux->mdatbuffer = NULL; @@ -2029,19 +3252,7 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition) gst_object_unref (qtdemux->element_index); qtdemux->element_index = NULL; gst_adapter_clear (qtdemux->adapter); -#ifdef QTDEMUX_MODIFICATION - // TODO: Check new modifications - if (qtdemux->file) { - fclose (qtdemux->file); - if (qtdemux->ofile) - fclose (qtdemux->ofile); - remove (qtdemux->filename); - } - qtdemux->file = NULL; - qtdemux->ofile = NULL; - qtdemux->filesize = 0; - qtdemux->maxbuffersize = QTDEMUX_MAX_BUFFER_SIZE; -#endif + for (n = 0; n < qtdemux->n_streams; n++) { gst_qtdemux_stream_free (qtdemux, qtdemux->streams[n]); qtdemux->streams[n] = NULL; @@ -2056,6 +3267,7 @@ gst_qtdemux_change_state (GstElement * element, GstStateChange transition) qtdemux->seek_offset = 0; qtdemux->upstream_seekable = FALSE; qtdemux->upstream_size = 0; +#endif break; } default: @@ -2092,8 +3304,26 @@ qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length) qtdemux->major_brand = QT_FOURCC (buffer + 8); GST_DEBUG_OBJECT (qtdemux, "major brand: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (qtdemux->major_brand)); + +#ifdef QTDEMUX_MODIFICATION + /* checking for piff major brand */ + if (qtdemux->major_brand == GST_MAKE_FOURCC ('i', 's', 'm', 'l') || + qtdemux->major_brand == GST_MAKE_FOURCC ('p', 'i', 'f', 'f')) { + GST_INFO_OBJECT (qtdemux, "clip has PIFF as major brand"); + qtdemux->piff_fragmented = TRUE; + } +#endif + buf = qtdemux->comp_brands = gst_buffer_new_and_alloc (length - 16); memcpy (GST_BUFFER_DATA (buf), buffer + 16, GST_BUFFER_SIZE (buf)); + +#ifdef QTDEMUX_MODIFICATION + /* checking for piff major brand */ + if (g_strrstr_len (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), "piff")) { + GST_INFO_OBJECT (qtdemux, "clip has PIFF as compatible brand"); + qtdemux->piff_fragmented = TRUE; + } +#endif } } @@ -2297,6 +3527,8 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, stream->track_id, d_sample_duration, d_sample_size, d_sample_flags, *base_offset); + qtdemux->sequence_id = stream->track_id; + GST_DEBUG("sequence id is %d", qtdemux->sequence_id); /* presence of stss or not can't really tell us much, * and flags and so on tend to be marginally reliable in these files */ if (stream->subtype == FOURCC_soun) { @@ -2321,6 +3553,7 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, if (*base_offset == -1) { GST_LOG_OBJECT (qtdemux, "base_offset at moof"); *base_offset = moof_offset; + ismv=TRUE;/*according to piff specification base_offset should be omitted*/ } *running_offset = *base_offset + data_offset; } else { @@ -2401,9 +3634,21 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, goto out_of_memory; if (G_UNLIKELY (stream->n_samples == 0)) { +#ifdef QTDEMUX_MODIFICATION + if(qtdemux->is_dash && GST_CLOCK_TIME_IS_VALID(qtdemux->dash_init_offset)) + timestamp = gst_util_uint64_scale(qtdemux->dash_init_offset, stream->timescale, GST_SECOND); + else if (qtdemux->fragmented) { + timestamp = stream->moof_seeked_time; + } else { + /* the timestamp of the first sample is also provided by the tfra entry + * but we shouldnt rely on it as it is at the end of files */ + timestamp = 0; + } +#else /* the timestamp of the first sample is also provided by the tfra entry * but we shouldn't rely on it as it is at the end of files */ timestamp = 0; +#endif } else { /* subsequent fragments extend stream */ timestamp = @@ -2453,11 +3698,20 @@ qtdemux_parse_trun (GstQTDemux * qtdemux, GstByteReader * trun, /* ismv seems to use 0x40 for keyframe, 0xc0 for non-keyframe, * now idea how it relates to bitfield other than massive LE/BE confusion */ sample->keyframe = ismv ? ((sflags & 0xff) == 0x40) : !(sflags & 0x10000); + + /* For a video track, the first sample in a fragment MUST be an IDR frame(Key frame) according to + * PIFF specs (PIFF 1.1, page no. 17) */ + if (ismv && stream->subtype == FOURCC_vide && i == 0) { + sample->keyframe = TRUE; + } + *running_offset += size; timestamp += dur; sample++; } - +#ifdef QTDEMUX_MODIFICATION + stream->prev_n_samples = stream->n_samples; +#endif stream->n_samples += samples_count; return TRUE; @@ -2570,67 +3824,387 @@ unknown_stream: 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; @@ -2649,21 +4223,25 @@ fail: } } +#ifdef QTDEMUX_MODIFICATION + /* might be used if some day we actually use mfra & co * for random access to fragments, * but that will require quite some modifications and much less relying * on a sample array */ -#if 0 static gboolean -qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node, - QtDemuxStream * stream) +qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node) { guint64 time = 0, moof_offset = 0; guint32 ver_flags, track_id, len, num_entries, i; guint value_size, traf_size, trun_size, sample_size; - GstBuffer *buf = NULL; - GstFlowReturn ret; GstByteReader tfra; + guint n = 0; + QtDemuxStream *stream = NULL; + gboolean found_track = FALSE; + QtDemuxTfraTable *tfra_table = NULL; + + GST_DEBUG_OBJECT (qtdemux, "Starting tfra parsing..."); gst_byte_reader_init (&tfra, (guint8 *) tfra_node->data + (4 + 4), QT_UINT32 ((guint8 *) tfra_node->data) - (4 + 4)); @@ -2676,44 +4254,67 @@ qtdemux_parse_tfra (GstQTDemux * qtdemux, GNode * tfra_node, gst_byte_reader_get_uint32_be (&tfra, &num_entries))) return FALSE; - GST_LOG_OBJECT (qtdemux, "id %d == stream id %d ?", - track_id, stream->track_id); - if (track_id != stream->track_id) { + for (n = 0; n < qtdemux->n_streams; n++) { + stream = qtdemux->streams[n]; + + if (stream->track_id == track_id) { + GST_INFO_OBJECT (qtdemux, "found stream with track_id = %d", track_id); + found_track = TRUE; + break; + } + } + + if (!stream || !found_track) { + GST_WARNING_OBJECT (qtdemux, "not able to parse tfra..."); return FALSE; } + GST_LOG_OBJECT (qtdemux, "id %d == stream id %d ?", + track_id, stream->track_id); + + GST_INFO_OBJECT (stream->pad, "number of tfra entries = %u", num_entries); + + if (num_entries == 0) + goto no_samples; + + tfra_table = g_new0 (QtDemuxTfraTable, 1); + tfra_table->n_entries = num_entries; + tfra_table->tfra_entries = g_array_sized_new (FALSE, FALSE, sizeof (QtDemuxTfraEntryInfo), num_entries); + value_size = ((ver_flags >> 24) == 1) ? sizeof (guint64) : sizeof (guint32); sample_size = (len & 3) + 1; trun_size = ((len & 12) >> 2) + 1; traf_size = ((len & 48) >> 4) + 1; - if (num_entries == 0) - goto no_samples; + GST_DEBUG_OBJECT (stream->pad, "value_size = %u, sample_size = %u, trun_size = %u and traf_size = %u", + value_size, sample_size, trun_size, traf_size); if (!qt_atom_parser_has_chunks (&tfra, num_entries, value_size + value_size + traf_size + trun_size + sample_size)) goto corrupt_file; for (i = 0; i < num_entries; i++) { + QtDemuxTfraEntryInfo entry = {0, }; + qt_atom_parser_get_offset (&tfra, value_size, &time); qt_atom_parser_get_offset (&tfra, value_size, &moof_offset); qt_atom_parser_get_uint_with_size_unchecked (&tfra, traf_size); qt_atom_parser_get_uint_with_size_unchecked (&tfra, trun_size); qt_atom_parser_get_uint_with_size_unchecked (&tfra, sample_size); - GST_LOG_OBJECT (qtdemux, - "fragment time: %" GST_TIME_FORMAT " moof_offset: %u", - GST_TIME_ARGS (gst_util_uint64_scale (time, GST_SECOND, - stream->timescale)), moof_offset); + entry.time = time; + entry.moof_offset = moof_offset; + g_array_append_val (tfra_table->tfra_entries, entry); - ret = gst_qtdemux_pull_atom (qtdemux, moof_offset, 0, &buf); - if (ret != GST_FLOW_OK) - goto corrupt_file; - qtdemux_parse_moof (qtdemux, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), - moof_offset, stream); - gst_buffer_unref (buf); + GST_LOG_OBJECT (stream->pad, + "fragment time apend: %" GST_TIME_FORMAT " moof_offset: %"G_GUINT64_FORMAT, + GST_TIME_ARGS (gst_util_uint64_scale (time, GST_SECOND, stream->timescale)), entry.moof_offset); } + stream->tfra_table = tfra_table; + + GST_DEBUG_OBJECT (qtdemux, "successfully built tfra table..."); + return TRUE; /* ERRORS */ @@ -2730,41 +4331,91 @@ no_samples: } } -static gboolean -qtdemux_parse_mfra (GstQTDemux * qtdemux, QtDemuxStream * stream) + +static gint +gst_qtdemux_moof_offset_compare (QtDemuxTfraEntryInfo * i1, QtDemuxTfraEntryInfo * i2) { - GstFlowReturn ret; + return (i1->moof_offset - i2->moof_offset); +} + +static GstFlowReturn +qtdemux_parse_mfra (GstQTDemux * qtdemux) +{ + GstFlowReturn ret = GST_FLOW_OK; GNode *mfra_node, *tfra_node; - GstBuffer *buffer; + gint n = 0; + GstBuffer *buffer = NULL; + gboolean bret = FALSE; - if (!qtdemux->mfra_offset) - return FALSE; + GST_DEBUG_OBJECT (qtdemux, "Starting mfra atom parsing..."); ret = gst_qtdemux_pull_atom (qtdemux, qtdemux->mfra_offset, 0, &buffer); if (ret != GST_FLOW_OK) goto corrupt_file; mfra_node = g_node_new ((guint8 *) GST_BUFFER_DATA (buffer)); - qtdemux_parse_node (qtdemux, mfra_node, GST_BUFFER_DATA (buffer), - GST_BUFFER_SIZE (buffer)); + qtdemux_parse_node (qtdemux, mfra_node, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer)); tfra_node = qtdemux_tree_get_child_by_type (mfra_node, FOURCC_tfra); while (tfra_node) { - qtdemux_parse_tfra (qtdemux, tfra_node, stream); + bret = qtdemux_parse_tfra (qtdemux, tfra_node); + if (!bret) { + GST_WARNING_OBJECT (qtdemux, "issue in parsing tfra atom.."); + break; + } /* iterate all siblings */ tfra_node = qtdemux_tree_get_sibling_by_type (tfra_node, FOURCC_tfra); } + + if (!bret) { + GST_WARNING_OBJECT (qtdemux, "ignore parsing of mfra atom because of errors"); + goto exit; + } + + qtdemux->moof_offsets = g_array_new (FALSE, FALSE, sizeof (QtDemuxTfraEntryInfo)); + + /* prepare consolidated moof_offset_table */ + for (n = 0; n < qtdemux->n_streams; n++) { + QtDemuxStream *stream = qtdemux->streams[n]; + QtDemuxTfraEntryInfo *entry = NULL; + + if (stream->tfra_table) { + guint32 idx = 0; + guint32 prev_len = qtdemux->moof_offsets->len; + + GST_INFO_OBJECT (stream->pad, "prev len = %d and entries = %d", prev_len, stream->tfra_table->n_entries); + + for (idx = 0; idx < stream->tfra_table->n_entries; idx++) { + QtDemuxTfraEntryInfo new_entry = {0, }; + entry = &g_array_index (stream->tfra_table->tfra_entries, QtDemuxTfraEntryInfo, idx); + new_entry.time = entry->time; + new_entry.moof_offset = entry->moof_offset; + g_array_append_val (qtdemux->moof_offsets, new_entry); + GST_DEBUG_OBJECT (stream->pad, "appended %"G_GUINT64_FORMAT" to moof_offsets", entry->moof_offset); + } + } + } + + /* sort the moof_offsets array */ + g_array_sort (qtdemux->moof_offsets, (GCompareFunc)gst_qtdemux_moof_offset_compare); + + for (n = 0; n < qtdemux->moof_offsets->len; n++) { + QtDemuxTfraEntryInfo *entry = &g_array_index (qtdemux->moof_offsets, QtDemuxTfraEntryInfo, n); + GST_LOG_OBJECT (qtdemux, "idx = %d and moof_offset = %"G_GUINT64_FORMAT, n, entry->moof_offset); + } + +exit: g_node_destroy (mfra_node); gst_buffer_unref (buffer); - return TRUE; + return GST_FLOW_OK; corrupt_file: { GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE, (_("This file is corrupt and cannot be played.")), (NULL)); - return FALSE; + return ret; } } @@ -2778,6 +4429,8 @@ qtdemux_parse_mfro (GstQTDemux * qtdemux, guint64 * mfra_offset, gint64 len; GstFormat fmt = GST_FORMAT_BYTES; + GST_DEBUG_OBJECT (qtdemux, "Starting mfro atom parsing..."); + if (!gst_pad_query_peer_duration (qtdemux->sinkpad, &fmt, &len)) { GST_DEBUG_OBJECT (qtdemux, "upstream size not available; " "can not locate mfro"); @@ -2789,8 +4442,10 @@ qtdemux_parse_mfro (GstQTDemux * qtdemux, guint64 * mfra_offset, goto exit; fourcc = QT_FOURCC (GST_BUFFER_DATA (mfro) + 4); - if (fourcc != FOURCC_mfro) + if (fourcc != FOURCC_mfro) { + GST_WARNING_OBJECT (qtdemux, "mfro atom is not available, so mfra as well..."); goto exit; + } GST_INFO_OBJECT (qtdemux, "Found mfro atom: fragmented mp4 container"); if (GST_BUFFER_SIZE (mfro) >= 16) { @@ -2811,16 +4466,13 @@ exit: return ret; } -static void +static GstFlowReturn qtdemux_parse_fragmented (GstQTDemux * qtdemux) { GstFlowReturn ret; guint32 mfra_size = 0; guint64 mfra_offset = 0; - /* default */ - qtdemux->fragmented = FALSE; - /* We check here if it is a fragmented mp4 container */ ret = qtdemux_parse_mfro (qtdemux, &mfra_offset, &mfra_size); if (ret == GST_FLOW_OK && mfra_size != 0 && mfra_offset != 0) { @@ -2829,6 +4481,8 @@ qtdemux_parse_fragmented (GstQTDemux * qtdemux) "mfra atom expected at offset %" G_GUINT64_FORMAT, mfra_offset); qtdemux->mfra_offset = mfra_offset; } + + return ret; } #endif @@ -2840,6 +4494,9 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) GstBuffer *buf = NULL; GstFlowReturn ret = GST_FLOW_OK; guint64 cur_offset = qtdemux->offset; +#ifdef QTDEMUX_MODIFICATION + gboolean bret = FALSE; +#endif ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf); if (G_UNLIKELY (ret != GST_FLOW_OK)) @@ -2862,15 +4519,33 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) switch (fourcc) { case FOURCC_moof: /* record for later parsing when needed */ +#ifdef QTDEMUX_MODIFICATION if (!qtdemux->moof_offset) { qtdemux->moof_offset = qtdemux->offset; + GST_INFO_OBJECT (qtdemux, "received first moof atom..."); + + if (!qtdemux->moof_offsets) { + GST_WARNING_OBJECT (qtdemux, "Did not receive mfra yet.. look at the end of file"); + + /* ignoring return value, as it is not mandatory for our seeking/playback */ + ret = qtdemux_parse_fragmented (qtdemux); + + if (qtdemux->mfra_offset) { + ret = qtdemux_parse_mfra (qtdemux); + if (ret != GST_FLOW_OK) + goto beach; + } + } + } +#else + if (!qtdemux->moof_offset) { + qtdemux->moof_offset = qtdemux->offset; } +#endif /* fall-through */ case FOURCC_mdat: case FOURCC_free: - case FOURCC_wide: case FOURCC_PICT: - case FOURCC_pnot: { GST_LOG_OBJECT (qtdemux, "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT, @@ -2881,7 +4556,9 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) case FOURCC_moov: { GstBuffer *moov; - +#ifdef QTDEMUX_MODIFICATION + gboolean tree_ret; +#endif if (qtdemux->got_moov) { GST_DEBUG_OBJECT (qtdemux, "Skipping moov atom as we have one already"); qtdemux->offset += length; @@ -2931,10 +4608,24 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) } qtdemux->offset += length; - qtdemux_parse_moov (qtdemux, GST_BUFFER_DATA (moov), length); +#ifdef QTDEMUX_MODIFICATION + bret = qtdemux_parse_moov (qtdemux, GST_BUFFER_DATA (moov), length); + if(!bret) { + GST_DEBUG_OBJECT(qtdemux, "Failed to parse moov atom"); + return GST_FLOW_ERROR; + } +#else qtdemux_node_dump (qtdemux, qtdemux->moov_node); - +#endif +#ifdef QTDEMUX_MODIFICATION + tree_ret = qtdemux_parse_tree (qtdemux); + if(!tree_ret) { + GST_ERROR_OBJECT(qtdemux,"fail to parse uuid atom in tree"); + return GST_FLOW_ERROR; + } +#else qtdemux_parse_tree (qtdemux); +#endif g_node_destroy (qtdemux->moov_node); gst_buffer_unref (moov); qtdemux->moov_node = NULL; @@ -2953,6 +4644,12 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) qtdemux->offset += length; qtdemux_parse_ftyp (qtdemux, GST_BUFFER_DATA (ftyp), GST_BUFFER_SIZE (ftyp)); +#ifndef TIZEN_KEEP_QT_ATOMS + if (qtdemux->major_brand == FOURCC_qt__) { + GST_ERROR_OBJECT (qtdemux, "Unsupported major brand..."); + return GST_FLOW_ERROR; + } +#endif gst_buffer_unref (ftyp); break; } @@ -2970,6 +4667,18 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) gst_buffer_unref (uuid); break; } +#ifdef QTDEMUX_MODIFICATION + case FOURCC_mfra: + { + GST_DEBUG_OBJECT (qtdemux, "Got mfra atom..."); + qtdemux->mfra_offset = cur_offset; + qtdemux->offset += length; + ret = qtdemux_parse_mfra (qtdemux); + if (ret != GST_FLOW_OK) + goto beach; + break; + } +#endif default: { GstBuffer *unknown; @@ -2990,8 +4699,11 @@ gst_qtdemux_loop_state_header (GstQTDemux * qtdemux) } beach: - if (ret == GST_FLOW_UNEXPECTED && qtdemux->got_moov) { + if ((qtdemux->moof_offset != 0 || ret == GST_FLOW_UNEXPECTED) && qtdemux->got_moov) { /* digested all data, show what we have */ +#ifdef QTDEMUX_MODIFICATION + qtdemux_prepare_streams (qtdemux); +#endif ret = qtdemux_expose_streams (qtdemux); /* Only post, event on pads is done after newsegment */ @@ -3022,7 +4734,7 @@ gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux) #ifdef QTDEMUX_MODIFICATION QtDemuxSample *sample; gdouble minusone = -1; - guint64 time_position; + guint64 time_position = 0; #endif /* Now we choose an arbitrary stream, get the previous keyframe timestamp @@ -3032,7 +4744,12 @@ gst_qtdemux_seek_to_previous_keyframe (GstQTDemux * qtdemux) QtDemuxStream *str = qtdemux->streams[n]; #ifdef QTDEMUX_MODIFICATION - sample = &str->samples[str->sample_index]; + + sample = &str->samples[str->from_sample]; + + GST_DEBUG_OBJECT (qtdemux, "from sample timestamp = %"GST_TIME_FORMAT, GST_TIME_ARGS(sample->timestamp)); + + time_position = sample->timestamp; if((time_position - (minusone *qtdemux->segment.rate)*sample->duration)>0) time_position -= (minusone *qtdemux->segment.rate)*sample->duration; @@ -3751,8 +5468,11 @@ gst_qtdemux_process_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream, /* no further processing needed */ stream->need_process = FALSE; } - +#ifdef QTDEMUX_MODIFICATION + if (G_UNLIKELY (stream->subtype != FOURCC_text) && G_UNLIKELY (stream->subtype != FOURCC_sbtl)) { +#else if (G_UNLIKELY (stream->subtype != FOURCC_text)) { +#endif return buf; } @@ -3824,6 +5544,21 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux, goto exit; } +#ifdef QTDEMUX_MODIFICATION + if (qtdemux->is_dash && GST_CLOCK_TIME_IS_VALID(stream->dash_seek_offset) && qtdemux->n_video_streams == 0) { + /* Dropping audio buffers for syncronization audio and video streams when + *playing MPEG DASH content and audio stream have personal demuxer. */ + if ( (timestamp < stream->dash_seek_offset || !keyframe) ) { + GST_LOG_OBJECT (qtdemux, "Ignoring dash buffer with ts=%"GST_TIME_FORMAT + " before newsegment start time.", GST_TIME_ARGS(timestamp)); + gst_buffer_unref (buf); + goto exit; + } else { + stream->dash_seek_offset = GST_CLOCK_TIME_NONE; + } + } +#endif + /* send out pending buffers */ while (stream->buffers) { GstBuffer *buffer = (GstBuffer *) stream->buffers->data; @@ -3891,6 +5626,231 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux, gst_buffer_set_caps (buf, stream->caps); +#ifdef QTDEMUX_MODIFICATION + +#ifdef DRM_ENABLE + if (qtdemux->encrypt_content) { + unsigned int i = 0; + QtDemuxSample *sample = NULL; + guint n_entries = 0; + guint running_offset = 0; + + unsigned char *indata = NULL; + unsigned int insize = 0; + guint indata_offset = 0; + drm_trusted_payload_info_s read_input_data = {0, }; + drm_trusted_read_decrypt_resp_data_s read_output_data = {0, }; + drm_trusted_result_e drm_ret = DRM_TRUSTED_RETURN_SUCCESS; + + sample = &stream->samples[stream->sample_index]; + + if (sample->sub_encry && qtdemux->dash_content == FALSE) { + n_entries = sample->sub_encry->n_entries; + GST_DEBUG_OBJECT (qtdemux, " number of sub-sample entries = %d", n_entries); + } + + if (n_entries > 0) { + /* create indata */ + indata = (unsigned char *) malloc (GST_BUFFER_SIZE(buf)); + if (!indata) { + GST_ERROR_OBJECT (qtdemux, "failed to create indata..."); + return GST_FLOW_ERROR; + } + + /* copy encrypted data only */ + do { + gint clear_len = 0; + + gint encrypted_len = 0; + + clear_len = sample->sub_encry->sub_entry[i].LenofClearData; + encrypted_len = sample->sub_encry->sub_entry[i].LenofEncryptData; + running_offset += clear_len; + + GST_LOG_OBJECT (qtdemux, "entry = %d and copying %d bytes of encrypted data", i, encrypted_len); + memcpy (indata + insize, GST_BUFFER_DATA (buf)+ running_offset, encrypted_len); + insize += encrypted_len; + running_offset += encrypted_len; + i++; + } while (i < n_entries); + } else if(qtdemux->clear_data && stream->subtype == FOURCC_vide) { + gint clear_len = 0; + gint encrypted_len = 0; + indata = (unsigned char *) malloc (GST_BUFFER_SIZE(buf)); + if (!indata) { + GST_ERROR_OBJECT (qtdemux, "failed to create indata..."); + return GST_FLOW_ERROR; + } + clear_len = qtdemux->clear_data[qtdemux->current_sample[0]]; + encrypted_len = qtdemux->encrypted_data[qtdemux->current_sample[0]]; + running_offset += clear_len; + + GST_LOG_OBJECT (qtdemux, "entry = %d and copying %d bytes of encrypted data", i, encrypted_len); + memcpy (indata + insize, GST_BUFFER_DATA (buf)+ running_offset, encrypted_len); + insize += encrypted_len; + running_offset += encrypted_len; + } else { + indata = GST_BUFFER_DATA (buf); + insize = GST_BUFFER_SIZE (buf); + } + + read_input_data.media_offset = 0; + read_input_data.payload_data = indata; + read_input_data.payload_data_len = insize; + read_input_data.payload_data_output = read_input_data.payload_data; /* inplace decryption */ + + if(qtdemux->sample_count[0] || qtdemux->sample_count[1]) { + if(stream->subtype == FOURCC_vide) { + read_input_data.payload_iv_len = qtdemux->iv_size_video; + read_input_data.payload_iv = (unsigned char *) malloc (qtdemux->iv_size_video); + if (NULL == read_input_data.payload_iv) { + GST_ERROR_OBJECT (qtdemux, "Failed to allocate memory...\n"); + GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory")); + ret = GST_FLOW_ERROR; + goto exit; + } + for(i = 0 ; i < read_input_data.payload_iv_len; i++) { + read_input_data.payload_iv[i] = (unsigned char*)qtdemux->iv_data_video[qtdemux->current_sample[0]][i]; + } + qtdemux->current_sample[0]++; + } else if(stream->subtype == FOURCC_soun) { + read_input_data.payload_iv_len = qtdemux->iv_size_audio; + read_input_data.payload_iv = (unsigned char *) malloc (qtdemux->iv_size_audio); + if (NULL == read_input_data.payload_iv) { + GST_ERROR_OBJECT (qtdemux, "Failed to allocate memory...\n"); + GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory")); + ret = GST_FLOW_ERROR; + goto exit; + } + for(i = 0 ; i < read_input_data.payload_iv_len; i++) { + read_input_data.payload_iv[i] = (unsigned char*)qtdemux->iv_data_audio[qtdemux->current_sample[1]][i]; + } + qtdemux->current_sample[1]++; + } + } else { + read_input_data.payload_iv_len = 8; + read_input_data.payload_iv = (unsigned char *) malloc (8); + if (NULL == read_input_data.payload_iv) { + GST_ERROR_OBJECT (qtdemux, "Failed to allocate memory...\n"); + GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory")); + ret = GST_FLOW_ERROR; + goto exit; + } + memcpy (read_input_data.payload_iv, sample->iv, 8); + } + + drm_ret = drm_trusted_read_decrypt_session(qtdemux->pr_handle , &read_input_data, &read_output_data); + if (DRM_TRUSTED_RETURN_SUCCESS != drm_ret) { + GST_ERROR_OBJECT (qtdemux, "failed to decrypt buffer..."); + if (DRM_TRUSTED_RETURN_OPL_ERROR == drm_ret) { + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("opl violation"), (NULL)); + } else { + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("decryption failed"), (NULL)); + } + + free (read_input_data.payload_iv); + ret = GST_FLOW_ERROR; + goto exit; + } + + GST_LOG_OBJECT (qtdemux, "decryption is successful"); + + if (read_output_data.read_size != read_input_data.payload_data_len) { + GST_INFO_OBJECT (qtdemux, "Decrypter did not consume data fully..."); + } + + if(read_input_data.payload_iv) + free (read_input_data.payload_iv); + read_input_data.payload_iv = NULL; + + + i = 0; + running_offset = 0; + + if (n_entries > 0) { + do { + gint clear_len = 0; + gint encrypted_len = 0; + + clear_len = sample->sub_encry->sub_entry[i].LenofClearData; + encrypted_len = sample->sub_encry->sub_entry[i].LenofEncryptData; + running_offset += clear_len; + + GST_LOG_OBJECT (qtdemux, "entry = %d and copying back %d bytes of decrypted data", i, encrypted_len); + memcpy (GST_BUFFER_DATA (buf)+ running_offset, indata+indata_offset, encrypted_len); + running_offset += encrypted_len; + indata_offset += encrypted_len; + i++; + } while (i < n_entries); + free (indata); + } else if(qtdemux->clear_data > 0 && stream->subtype == FOURCC_vide) { + gint clear_len = 0; + gint encrypted_len = 0; + + clear_len = qtdemux->clear_data[qtdemux->current_sample[0] - 1]; + encrypted_len = qtdemux->encrypted_data[qtdemux->current_sample[0] - 1]; + running_offset += clear_len; + + GST_LOG_OBJECT (qtdemux, "entry = %d and copying back %d bytes of decrypted data", i, encrypted_len); + memcpy (GST_BUFFER_DATA (buf)+ running_offset, indata+indata_offset, encrypted_len); + running_offset += encrypted_len; + indata_offset += encrypted_len; + free (indata); + } + } +#endif // DRM_ENABLE + if (qtdemux->piff_fragmented && !qtdemux->pullbased) { + gboolean all_queues_filled = TRUE; + gint i = 0; + QtDemuxStream *cstream = NULL; + + /* push current buffer */ + g_queue_push_tail (stream->frag_queue, buf); + GST_LOG_OBJECT (stream->pad, "pushing buffer into frag_queue with ts = %"GST_TIME_FORMAT, GST_TIME_ARGS(GST_BUFFER_TIMESTAMP(buf))); + + for (i = 0; i < qtdemux->n_streams; i++) { + cstream = qtdemux->streams[i]; + if (g_queue_is_empty (cstream->frag_queue)) { + all_queues_filled = FALSE; + } + } + + if (all_queues_filled) { + /* all stream queues have data */ + for (i = 0; i < qtdemux->n_streams; i++) { + GstBuffer *pop_buf = NULL; + cstream = qtdemux->streams[i]; + +pop_again: + pop_buf = g_queue_pop_head (cstream->frag_queue); + + qtdemux->max_pop_ts = GST_BUFFER_TIMESTAMP(pop_buf) > qtdemux->max_pop_ts ? + GST_BUFFER_TIMESTAMP(pop_buf) : qtdemux->max_pop_ts; + + GST_DEBUG_OBJECT (cstream->pad, + "Pushing buffer with time %" GST_TIME_FORMAT ", duration %" + GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (pop_buf)), + GST_TIME_ARGS (GST_BUFFER_DURATION (pop_buf))); + + ret = gst_pad_push (cstream->pad, pop_buf); + if (ret != GST_FLOW_OK) { + GST_WARNING_OBJECT (cstream->pad, "pad_push returned = %s", gst_flow_get_name (ret)); + return ret; + } + + pop_buf = g_queue_peek_head (cstream->frag_queue); + + if (pop_buf && (GST_BUFFER_TIMESTAMP(pop_buf) < qtdemux->max_pop_ts)) { + GST_LOG_OBJECT (cstream->pad, "pop again next ts = %"GST_TIME_FORMAT" and max_pop_ts = %"GST_TIME_FORMAT, + GST_TIME_ARGS(GST_BUFFER_TIMESTAMP (pop_buf)), GST_TIME_ARGS(qtdemux->max_pop_ts)); + goto pop_again; + } + } + } + + return GST_FLOW_OK; + } +#endif GST_LOG_OBJECT (qtdemux, "Pushing buffer with time %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), @@ -3928,12 +5888,16 @@ gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux) stream = qtdemux->streams[i]; position = stream->time_position; - /* position of -1 is EOS */ if (position != -1 && position < min_time) { min_time = position; index = i; } + if (stream->sample_index == stream->n_samples && stream->sent_eos == FALSE) { + GST_DEBUG_OBJECT (qtdemux, "Sending EOS for Stream %d", i); + gst_pad_push_event(stream->pad, gst_event_new_eos ()); + stream->sent_eos = TRUE; + } } /* all are EOS */ if (G_UNLIKELY (index == -1)) { @@ -4010,15 +5974,23 @@ eos_stream: static void gst_qtdemux_loop (GstPad * pad) { - GstQTDemux *qtdemux; - guint64 cur_offset; - GstFlowReturn ret; + GstQTDemux *qtdemux = NULL; + guint64 cur_offset = 0; + GstFlowReturn ret = GST_FLOW_OK; qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad)); cur_offset = qtdemux->offset; GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d", cur_offset, qtdemux->state); +#ifdef QTDEMUX_MODIFICATION + if (qtdemux->need_moof_parsing) { + ret = qtdemux_prepare_streams (qtdemux); + if (ret != GST_FLOW_OK) + goto pause; + qtdemux->need_moof_parsing = FALSE; + } +#endif switch (qtdemux->state) { case QTDEMUX_STATE_INITIAL: @@ -4250,9 +6222,11 @@ done: static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) { - GstQTDemux *demux; + GstQTDemux *demux = NULL; GstFlowReturn ret = GST_FLOW_OK; - +#ifdef QTDEMUX_MODIFICATION + gboolean bret = FALSE; +#endif demux = GST_QTDEMUX (gst_pad_get_parent (sinkpad)); #ifdef QTDEMUX_MODIFICATION @@ -4284,7 +6258,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) #ifdef QTDEMUX_MODIFICATION /* Added consideration of demuxer state and file size*/ - while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes + while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes || (demux->file && demux->state == QTDEMUX_STATE_MOVIE && demux->filesize >= demux->neededbytes)) && ret == GST_FLOW_OK) { #else @@ -4296,6 +6270,12 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) "state:%d , demux->neededbytes:%d, demux->offset:%" G_GUINT64_FORMAT, demux->state, demux->neededbytes, demux->offset); +#ifdef QTDEMUX_MODIFICATION + if(demux->is_dash && GST_CLOCK_TIME_IS_VALID(GST_BUFFER_TIMESTAMP(inbuf))) { + demux->dash_init_offset = GST_BUFFER_TIMESTAMP(inbuf); + } +#endif + switch (demux->state) { case QTDEMUX_STATE_INITIAL:{ const guint8 *data; @@ -4386,7 +6366,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) demux->state = QTDEMUX_STATE_BUFFER_MDAT; demux->neededbytes = size; #ifdef QTDEMUX_MODIFICATION - if ((demux->filename && demux->file == NULL) || !demux->mdatbuffer) + if ((demux->filename && demux->file == NULL) && !demux->mdatbuffer) #else if (!demux->mdatbuffer) #endif @@ -4428,6 +6408,29 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) if (fourcc == FOURCC_moov) { GST_DEBUG_OBJECT (demux, "Parsing [moov]"); +#ifdef QTDEMUX_MODIFICATION + if (demux->got_moov && demux->fragmented) { + demux->need_parsing_moov = TRUE;//need to parse 2nd moov + GST_DEBUG_OBJECT (demux, + "Got a second moov, clean up data from old one"); + if (demux->moov_node) + g_node_destroy (demux->moov_node); + demux->moov_node = NULL; + demux->moov_node_compressed = NULL; + } + + /* prepare newsegment to send when streaming actually starts */ + if (!demux->pending_newsegment && !demux->got_moov) { + demux->pending_newsegment = + gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, + 0, GST_CLOCK_TIME_NONE, 0); + } + bret = qtdemux_parse_moov (demux, data, demux->neededbytes); + if(!bret) { + GST_DEBUG_OBJECT(demux, "returning from chain"); + return GST_FLOW_ERROR; + } +#else demux->got_moov = TRUE; /* prepare newsegment to send when streaming actually starts */ @@ -4436,12 +6439,29 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, GST_CLOCK_TIME_NONE, 0); } - qtdemux_parse_moov (demux, data, demux->neededbytes); +#endif qtdemux_node_dump (demux, demux->moov_node); qtdemux_parse_tree (demux); - qtdemux_expose_streams (demux); +#ifdef QTDEMUX_MODIFICATION + qtdemux_prepare_streams (demux); + if (!demux->got_moov) + qtdemux_expose_streams (demux); + else { + gint n; + + for (n = 0; n < demux->n_streams; n++) { + QtDemuxStream *stream = demux->streams[n]; + gst_qtdemux_configure_stream (demux, stream); + } + } + + demux->got_moov = TRUE; + demux->need_parsing_moov = FALSE;//reset 2nd moov +#else + qtdemux_expose_streams (demux); +#endif g_node_destroy (demux->moov_node); demux->moov_node = NULL; GST_DEBUG_OBJECT (demux, "Finished parsing the header"); @@ -4559,7 +6579,7 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) GST_FOURCC_ARGS (QT_FOURCC (GST_BUFFER_DATA (buf) + 4))); #ifdef QTDEMUX_MODIFICATION - if (demux->filename == NULL) { + if (demux->file == NULL) { #endif if (demux->mdatbuffer) demux->mdatbuffer = gst_buffer_join (demux->mdatbuffer, buf); @@ -4645,8 +6665,8 @@ gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf) gst_adapter_flush (demux->adapter, demux->todrop); #ifdef QTDEMUX_MODIFICATION else { - fseek (demux->ofile, (long) demux->todrop, SEEK_CUR); - demux->filesize -= demux->todrop; + if(!fseek (demux->ofile, (long) demux->todrop, SEEK_CUR)) + demux->filesize -= demux->todrop; } #endif demux->neededbytes -= demux->todrop; @@ -4887,74 +6907,46 @@ qtdemux_inflate (void *z_buffer, guint z_length, guint length) static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer, guint length) { - GNode *cmov; - +#ifdef QTDEMUX_MODIFICATION + gboolean bret = FALSE; +#endif qtdemux->moov_node = g_node_new ((guint8 *) buffer); /* counts as header data */ qtdemux->header_size += length; GST_DEBUG_OBJECT (qtdemux, "parsing 'moov' atom"); - qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length); - - cmov = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_cmov); - if (cmov) { - guint32 method; - GNode *dcom; - GNode *cmvd; - - dcom = qtdemux_tree_get_child_by_type (cmov, FOURCC_dcom); - cmvd = qtdemux_tree_get_child_by_type (cmov, FOURCC_cmvd); - if (dcom == NULL || cmvd == NULL) - goto invalid_compression; - - method = QT_FOURCC ((guint8 *) dcom->data + 8); - switch (method) { -#ifdef HAVE_ZLIB - case GST_MAKE_FOURCC ('z', 'l', 'i', 'b'):{ - guint uncompressed_length; - guint compressed_length; - guint8 *buf; - - uncompressed_length = QT_UINT32 ((guint8 *) cmvd->data + 8); - compressed_length = QT_UINT32 ((guint8 *) cmvd->data + 4) - 12; - GST_LOG ("length = %u", uncompressed_length); - - buf = - (guint8 *) qtdemux_inflate ((guint8 *) cmvd->data + 12, - compressed_length, uncompressed_length); - - qtdemux->moov_node_compressed = qtdemux->moov_node; - qtdemux->moov_node = g_node_new (buf); - qtdemux_parse_node (qtdemux, qtdemux->moov_node, buf, - uncompressed_length); - break; - } -#endif /* HAVE_ZLIB */ - default: - GST_WARNING_OBJECT (qtdemux, "unknown or unhandled header compression " - "type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (method)); - break; - } +#ifdef QTDEMUX_MODIFICATION + bret = qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length); + if(!bret) { + GST_ERROR_OBJECT(qtdemux, "Failed to parse node object"); + return bret; } +#else + qtdemux_parse_node (qtdemux, qtdemux->moov_node, buffer, length); +#endif return TRUE; - - /* ERRORS */ -invalid_compression: - { - GST_ERROR_OBJECT (qtdemux, "invalid compressed header"); - return FALSE; - } } static gboolean +#ifdef QTDEMUX_MODIFICATION +qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, guint8 * buf, + const guint8 * end) +#else qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf, const guint8 * end) +#endif { while (G_UNLIKELY (buf < end)) { GNode *child; guint32 len; +#ifdef QTDEMUX_MODIFICATION + guint32 fourcc; + guint8 i,j; + gboolean found_sinf = FALSE; + gboolean bret = FALSE; +#endif if (G_UNLIKELY (buf + 4 > end)) { GST_LOG_OBJECT (qtdemux, "buffer overrun"); @@ -4975,11 +6967,43 @@ qtdemux_parse_container (GstQTDemux * qtdemux, GNode * node, const guint8 * buf, break; } +#ifdef QTDEMUX_MODIFICATION +/* Replacing the fourcc value of 'enca' or 'encv' node with the fourcc value of original format stored in 'sinf' node. */ + fourcc = QT_FOURCC (buf + 4); + + if((fourcc & 0xFFFFFF) == GST_MAKE_FOURCC ('e', 'n', 'c', 0)) { + for(i = 0; i < len ; i++) { + fourcc = QT_FOURCC(buf + i); + if( fourcc == GST_MAKE_FOURCC ('s','i','n','f')) { + found_sinf = TRUE; + break; + } + } + + if (!found_sinf) { + GST_ERROR_OBJECT (qtdemux, "failed to find sinf for encx format"); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("failed to find sinf atom"), (NULL)); + return FALSE; + } + + fourcc = QT_FOURCC(buf + i +12); + GST_LOG_OBJECT(qtdemux,"Original format present in encX node is %"GST_FOURCC_FORMAT , GST_FOURCC_ARGS(fourcc)); + for(j=0; j<4;j++) + buf[4+j] = buf[12 + i + j]; + } +#endif child = g_node_new ((guint8 *) buf); g_node_append (node, child); GST_LOG_OBJECT (qtdemux, "adding new node of len %d", len); +#ifdef QTDEMUX_MODIFICATION + bret = qtdemux_parse_node (qtdemux, child, buf, len); + if(!bret) { + GST_ERROR_OBJECT(qtdemux, "failed to parse node object"); + return bret; + } +#else qtdemux_parse_node (qtdemux, child, buf, len); - +#endif buf += len; } return TRUE; @@ -5054,6 +7078,9 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer, guint32 node_length = 0; const QtNodeType *type; const guint8 *end; +#ifdef QTDEMUX_MODIFICATION + gboolean bret = FALSE; +#endif GST_LOG_OBJECT (qtdemux, "qtdemux_parse buffer %p length %u", buffer, length); @@ -5079,9 +7106,35 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer, goto broken_atom_size; if (type->flags & QT_FLAG_CONTAINER) { +#ifdef QTDEMUX_MODIFICATION + bret = qtdemux_parse_container (qtdemux, node, buffer + 8, end); + if(!bret) { + GST_ERROR_OBJECT(qtdemux, "failed to parse container object"); + return bret; + } +#else qtdemux_parse_container (qtdemux, node, buffer + 8, end); +#endif } else { switch (fourcc) { + case FOURCC_saiz: + { + GST_DEBUG("found saiz atom"); + if(qtdemux->sequence_id == 2) { + qtdemux->iv_size_audio = QT_UINT32 (buffer + 9); + GST_INFO("iv size is %d", qtdemux->iv_size_audio); + } else if(qtdemux->sequence_id == 1) { + qtdemux->iv_size_video = QT_UINT32 (buffer + 9); + qtdemux->iv_size_video = 8; + GST_INFO("iv size is %d", qtdemux->iv_size_video); + } + break; + } + case FOURCC_saio: + { + GST_DEBUG("found saio atom"); + break; + } case FOURCC_stsd: { if (node_length < 20) { @@ -5093,6 +7146,220 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer, qtdemux_parse_container (qtdemux, node, buffer + 16, end); break; } +#ifdef DRM_ENABLE +#ifdef QTDEMUX_MODIFICATION + case FOURCC_uuid: + { + GstByteReader uuid_data; + guint8 *protection_header_data = NULL; + gint64 uuid_offset = 0; + uuid_type_t uuid_type; + + gst_byte_reader_init (&uuid_data, buffer, QT_UINT32 (buffer)); + uuid_type = qtdemux_get_uuid_type (qtdemux, &uuid_data, &uuid_offset); + if (UUID_PROTECTION_HEADER != uuid_type) { + GST_WARNING_OBJECT (qtdemux, "Ignoring Wrong UUID..."); + return TRUE; + } else { + qtdemux->protection_header_present = TRUE; + GST_DEBUG_OBJECT (qtdemux, "protection_header is present in uuid..."); + } + break; + } +#endif + case FOURCC_pssh: + { + GstByteReader uuid_data; + guint32 protection_header_size = 0; + gchar *protection_header_data = NULL; + int ret = -1; + drm_trusted_open_decrypt_info_s open_input_data; + drm_trusted_open_decrypt_resp_data_s open_output_data; + drm_trusted_set_consumption_state_info_s state_input_data; + guint8 *buffer = (guint8 *) node->data; + drm_permission_type_e status_perm_type; + drm_license_status_e license_status = DRM_LICENSE_STATUS_UNDEFINED; + GstQuery *query = NULL; + gchar *file_path = NULL; + gchar *query_file_path = NULL; + gchar *modified_path = NULL; + gchar *drm_compatible_path = NULL; + drm_bool_type_e is_drm_file = DRM_UNKNOWN; + gint len_protection_header = 0; + int a = 0; + + if (qtdemux->dash_content) { + GST_WARNING_OBJECT (qtdemux, "Skipping second pssh"); + break; + } + + qtdemux->dash_content = TRUE; + + gst_byte_reader_init (&uuid_data, buffer, QT_UINT32 (buffer)); + + /* Skipping fourcc & length*/ + if (!gst_byte_reader_skip (&uuid_data, 8)) + goto not_enough_data; + + if (!qtdemux_parse_playready_system_id (qtdemux, &uuid_data)) { + GST_ERROR_OBJECT (qtdemux, "not a playready system id.."); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, (NULL), ("not a valid playready system ID")); + return FALSE; + } + + query = gst_query_new_uri (); + if (!gst_pad_peer_query (qtdemux->sinkpad, query)) { + GST_ERROR_OBJECT (qtdemux, "failed to query URI from upstream"); + GST_ELEMENT_ERROR (qtdemux, CORE, FAILED, (NULL), ("failed to get uri from upstream")); + gst_query_unref (query); + return FALSE; + } + gst_query_parse_uri (query, &query_file_path); + + gst_query_unref (query); + query = NULL; + + if (g_str_has_prefix (query_file_path, "file://")) { + modified_path = query_file_path + 7; + } else { + modified_path = query_file_path; + } + GST_INFO_OBJECT (qtdemux, "file path : %s", query_file_path); + GST_INFO_OBJECT (qtdemux, "modified path : %s", modified_path); + drm_compatible_path = g_uri_unescape_string(modified_path, NULL); + if(drm_compatible_path == NULL) { + GST_WARNING_OBJECT(qtdemux, "unsuccessful in converting the string continuing with the modified path"); + drm_compatible_path = modified_path; + } else { + GST_INFO_OBJECT (qtdemux, "DRM Compatible path : %s", drm_compatible_path); + if(query_file_path) { + g_free (query_file_path); + query_file_path = NULL; + } + } + + //drm_compatible_path = "http://img.samsungvideohub.com/cms-private/store/057e52tw7h/PRD/C00000682330/Movie_HEVC_SD/I00001645829/I00001645829.mpd?AWSAccessKeyId=AKIAJW7RUCSLREJCX3IA&Expires=1374745300&Signature=FnPehyWVpoQoSKs9dxSSm7U551E%3D[]<>0418hwir93[]<>nk61lxvw9v[]<>356449050005148[]<>10060079181422907[]<>V[]<>STG"; + //drm_compatible_path = "http://img.samsungvideohub.com/cms-private/store/057e52tw7h/PRD/C00000107359/Movie_HEVC_SD/I00001645159/I00001645159.mpd?AWSAccessKeyId=AKIAJW7RUCSLREJCX3IA&Expires=1375434936&Signature=rzKiT1HUIVsTEa5QHaSDhEqR%2Frc%3D[]<>0418hwir93[]<>nk61lxvw9v[]<>356449050005148[]<>10060081508510332[]<>V[]<>STG"; + + + /* Enters into if body when valid system ID is present.*/ + gst_byte_reader_skip (&uuid_data, 12); + + gst_byte_reader_get_uint16_le (&uuid_data, &protection_header_size); + + GST_DEBUG_OBJECT(qtdemux,"protection header size: %d ", protection_header_size); + + if (!gst_byte_reader_dup_data(&uuid_data, protection_header_size , &protection_header_data)) { + GST_ERROR_OBJECT (qtdemux, "failed to duplicate bytereader data..."); + GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory")); + return FALSE; + } + /* for adding wchar null check */ + protection_header_size = protection_header_size + 2; + + protection_header_data = (guint8 *) realloc (protection_header_data,protection_header_size); + if (!protection_header_data) { + GST_ERROR_OBJECT (qtdemux, "failed to allocate memory..."); + GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory")); + return FALSE; + } + + /* adding wchar null character */ + protection_header_data[protection_header_size-2] = '\0'; + protection_header_data[protection_header_size-1] = '\0'; + + GST_DEBUG_OBJECT(qtdemux,"protection header size: %d ", protection_header_size); + + file_path = (gchar*)calloc((4 + 1 + 32 + 4 + 1 + protection_header_size), sizeof(gchar)); + if(file_path == NULL) { + GST_ERROR_OBJECT (qtdemux, "failed to allocate memory..."); + GST_ELEMENT_ERROR (qtdemux, RESOURCE, NO_SPACE_LEFT, (NULL), ("failed to allocate memory")); + return FALSE; + } + + strcpy(file_path, "dash"); + + file_path[4] = '?'; + for(a = 0; a < 16; a++) { + sprintf(file_path + 4 + 1 + (a*2), "%02x", qtdemux->uuid_playready[a]); + } + + file_path[4 + 1 + 32] = '?'; + len_protection_header = sprintf(file_path + 4 + 1 + 32 + 1, "%d", protection_header_size); + for(a = 0; a < protection_header_size; a++) { + file_path[4 + 1 + 32 + len_protection_header + 1 + a] = protection_header_data[a]; + } + + GST_DEBUG_OBJECT(qtdemux, "pssh header is %s", file_path); + + GST_DEBUG_OBJECT(qtdemux, "file path info is %s", query_file_path); + + ret = drm_is_drm_file(file_path, &is_drm_file); + if(ret != 0) { + GST_ERROR_OBJECT (qtdemux, "Failed to read is DRM_FILE information"); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("Failed to read is DRM_FILE information"), (NULL)); + free (protection_header_data); + free (file_path); + return FALSE; + } + + GST_DEBUG_OBJECT(qtdemux, "is drm is drm %d", is_drm_file); + + status_perm_type = DRM_PERMISSION_TYPE_PLAY; + ret = drm_get_license_status (file_path, status_perm_type, &license_status); + if (DRM_RETURN_SUCCESS != ret) { + GST_ERROR_OBJECT (qtdemux, "failed to get license status : 0x%x", ret); + qtdemux_post_drm_error (qtdemux, DRM_LICENSE_STATUS_UNDEFINED); + free (protection_header_data); + free (file_path); + return FALSE; + } + + GST_DEBUG_OBJECT(qtdemux, "license status is %d", license_status); + if(license_status != DRM_LICENSE_STATUS_VALID) { + if(!qtdemux_get_playready_licence(qtdemux, protection_header_data, protection_header_size, drm_compatible_path)) { + GST_ERROR_OBJECT (qtdemux, "failed to get playready license"); + free (protection_header_data); + free (file_path); + return FALSE; + } + } + + open_input_data.file_type = DRM_TRUSTED_TYPE_PIFF; + open_input_data.permission = DRM_TRUSTED_PERMISSION_TYPE_PLAY; + open_input_data.operation_callback.callback = test_drm_trusted_operation_cb; + open_input_data.lic_header.header = (unsigned char *) protection_header_data; + open_input_data.lic_header.header_len = protection_header_size; + + /* Open Decrypt Session*/ + ret = drm_trusted_open_decrypt_session(&open_input_data, &open_output_data, &(qtdemux->pr_handle)); + if (DRM_TRUSTED_RETURN_SUCCESS != ret) { + GST_ERROR_OBJECT (qtdemux, "failed to open decrypt session"); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("failed to open decrypt session"), (NULL)); + free (protection_header_data); + free (file_path); + return FALSE; + } + + /* Before Read, Appropriate state MUST be SET */ + state_input_data.state = DRM_CONSUMPTION_STARTED; + ret = drm_trusted_set_decrypt_state(qtdemux->pr_handle, &state_input_data); + if (DRM_TRUSTED_RETURN_SUCCESS != ret) { + GST_ERROR_OBJECT (qtdemux, "failed to set decrypt state..."); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT, ("failed to set decrypt state"), (NULL)); + free (protection_header_data); + free (file_path); + return FALSE; + } + + qtdemux->encrypt_content = TRUE; + + free (protection_header_data); + free (file_path); + /* iterate all siblings */ + break; + } +#endif case FOURCC_mp4a: case FOURCC_alac: { @@ -5132,7 +7399,7 @@ qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node, const guint8 * buffer, offset = 0x24; break; case 1: - offset = 0x34; + offset = 0x24; break; case 2: offset = 0x48; @@ -5308,32 +7575,244 @@ qtdemux_tree_get_sibling_by_type_full (GNode * node, guint32 fourcc, 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) @@ -5475,7 +7954,11 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux, qtdemux->n_audio_streams++; } else if (stream->subtype == FOURCC_strm) { GST_DEBUG_OBJECT (qtdemux, "stream type, not creating pad"); +#ifdef QTDEMUX_MODIFICATION + } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl) { +#else } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text) { +#endif gchar *name = g_strdup_printf ("subtitle_%02d", qtdemux->n_sub_streams); stream->pad = @@ -5519,7 +8002,7 @@ gst_qtdemux_add_stream (GstQTDemux * qtdemux, done: return TRUE; } - +#endif /* find next atom with @fourcc starting at @offset */ static GstFlowReturn qtdemux_find_atom (GstQTDemux * qtdemux, guint64 * offset, @@ -5676,7 +8159,6 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl) } /* sync sample atom */ - stream->stps_present = FALSE; if ((stream->stss_present = ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stss, &stream->stss) ? TRUE : FALSE) == TRUE) { @@ -5693,29 +8175,6 @@ qtdemux_stbl_init (GstQTDemux * qtdemux, QtDemuxStream * stream, GNode * stbl) if (!qt_atom_parser_has_chunks (&stream->stss, stream->n_sample_syncs, 4)) goto corrupt_file; } - - /* partial sync sample atom */ - if ((stream->stps_present = - ! !qtdemux_tree_get_child_by_type_full (stbl, FOURCC_stps, - &stream->stps) ? TRUE : FALSE) == TRUE) { - /* copy atom data into a new buffer for later use */ - stream->stps.data = g_memdup (stream->stps.data, stream->stps.size); - - /* skip version + flags */ - if (!gst_byte_reader_skip (&stream->stps, 1 + 3) || - !gst_byte_reader_get_uint32_be (&stream->stps, - &stream->n_sample_partial_syncs)) - goto corrupt_file; - - /* if there are no entries, the stss table contains the real - * sync samples */ - if (stream->n_sample_partial_syncs) { - /* make sure there's enough data */ - if (!qt_atom_parser_has_chunks (&stream->stps, - stream->n_sample_partial_syncs, 4)) - goto corrupt_file; - } - } } /* sample size */ @@ -5878,9 +8337,13 @@ qtdemux_parse_samples (GstQTDemux * qtdemux, QtDemuxStream * stream, guint32 n) goto out_of_samples; GST_OBJECT_LOCK (qtdemux); +#ifdef QTDEMUX_MODIFICATION + if (n <= stream->stbl_index && !qtdemux->need_parsing_moov) + goto already_parsed; +#else if (n <= stream->stbl_index) goto already_parsed; - +#endif GST_DEBUG_OBJECT (qtdemux, "parsing up to sample %u", n); if (!stream->stsz.data) { @@ -6089,7 +8552,21 @@ done2: (guint) (cur - samples), j, GST_TIME_ARGS (gst_util_uint64_scale (stts_time, GST_SECOND, stream->timescale))); - +#ifdef QTDEMUX_MODIFICATION + if(G_UNLIKELY (stream->subtype != FOURCC_text) && G_UNLIKELY (stream->subtype != FOURCC_sbtl) && + G_UNLIKELY (stream->subtype != FOURCC_subp)) { /* can not make bogus code check for sub-titles */ + if(gst_util_uint64_scale (stts_duration, GST_SECOND,stream->timescale) > QTDEMUX_MAX_SAMPLE_DURATION && stream->stts_samples ==1) { + GST_WARNING_OBJECT(qtdemux,"got the bogus duration and hence finding the last correct duration to replace with"); + for(k = (guint)(cur - samples-1);k >= 0;k--) { + if(gst_util_uint64_scale (samples[k].duration, GST_SECOND,stream->timescale) < QTDEMUX_MAX_SAMPLE_DURATION) { + stts_duration = samples[k].duration; + GST_INFO_OBJECT(qtdemux,"find the last valid duration and replacing bogus duration with %d",samples[k].duration); + break; + } + } + } + } +#endif cur->timestamp = stts_time; cur->duration = stts_duration; @@ -6155,37 +8632,6 @@ done3: /* save state */ stream->stss_index = i; } - - /* stps marks partial sync frames like open GOP I-Frames */ - if (stream->stps_present == TRUE) { - guint32 n_sample_partial_syncs; - - n_sample_partial_syncs = stream->n_sample_partial_syncs; - - /* if there are no entries, the stss table contains the real - * sync samples */ - if (n_sample_partial_syncs) { - for (i = stream->stps_index; i < n_sample_partial_syncs; i++) { - /* note that the first sample is index 1, not 0 */ - guint32 index; - - index = gst_byte_reader_get_uint32_be_unchecked (&stream->stps); - - if (G_LIKELY (index > 0 && index <= n_samples)) { - index -= 1; - samples[index].keyframe = TRUE; - GST_DEBUG_OBJECT (qtdemux, "samples at %u is keyframe", index); - /* and exit if we have enough samples */ - if (G_UNLIKELY (index >= n)) { - i++; - break; - } - } - } - /* save state */ - stream->stps_index = i; - } - } } else { /* no stss, all samples are keyframes */ stream->all_keyframe = TRUE; @@ -6359,6 +8805,21 @@ qtdemux_parse_segments (GstQTDemux * qtdemux, QtDemuxStream * stream, GST_DEBUG_OBJECT (qtdemux, "found %d non-empty segments", count); stream->n_segments = count; } + +#ifdef QTDEMUX_MODIFICATION + if (stream->n_segments) { + GstClockTime movie_duration = + gst_util_uint64_scale (stream->duration, GST_SECOND, stream->timescale); + /* Any difference between the movie's duration and the track's duration + * is expressed as an implicit empty edit */ + if (stime != movie_duration) { + GST_WARNING_OBJECT (qtdemux, "Movie duration and track duration from segments " + "is not matching, so assuming empty edits"); + stream->n_segments = 0; + } + } +#endif + done: /* push based does not handle segments, so act accordingly here, @@ -6644,6 +9105,67 @@ bad_data: return 0; } +#ifdef QTDEMUX_MODIFICATION +static gboolean +gst_codec_utils_hevc_caps_set_level_and_profile (GstCaps * caps, + const guint8 * sps, guint len) +{ + const gchar *level, *profile; + + g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); + g_return_val_if_fail (GST_CAPS_IS_SIMPLE (caps), FALSE); + g_return_val_if_fail (GST_SIMPLE_CAPS_HAS_NAME (caps, "video/hevc"), FALSE); + g_return_val_if_fail (sps != NULL, FALSE); + + level = gst_codec_utils_h264_get_level (sps, len); + + if (level != NULL) + gst_caps_set_simple (caps, "level", G_TYPE_STRING, level, NULL); + + profile = gst_codec_utils_h264_get_profile (sps, len); + + if (profile != NULL) + gst_caps_set_simple (caps, "profile", G_TYPE_STRING, profile, NULL); + + GST_LOG ("profile : %s", (profile) ? profile : "---"); + GST_LOG ("level : %s", (level) ? level : "---"); + + return (level != NULL && profile != NULL); +} +#endif + +#ifdef QTDEMUX_MODIFICATION +static QtDemuxStream * +_create_stream(GstQTDemux *qtdemux) +{ + QtDemuxStream *stream; + stream = g_new0 (QtDemuxStream, 1); + /* new streams always need a discont */ + stream->discont = TRUE; + /* we enable clipping for raw audio/video streams */ + stream->need_clip = FALSE; + stream->need_process = FALSE; + stream->segment_index = -1; + stream->time_position = 0; + stream->sample_index = -1; + stream->last_ret = GST_FLOW_OK; + /* dash specified */ + stream->dash_seek_offset = GST_CLOCK_TIME_NONE; + + stream->trickplay_info = g_new0 (TrickPlayInfo, 1); + stream->trickplay_info->prev_kidx = 0; + stream->trickplay_info->next_kidx = 0; + stream->trickplay_info->kidxs_dur_diff = 0; + stream->orientation = -1; + stream->prev_n_samples=0; + stream->moof_seeked_time = 0; + + if (qtdemux->piff_fragmented) + stream->frag_queue = g_queue_new (); + + return stream; +} +#endif /* parse the traks. * With each track we associate a new QtDemuxStream that contains all the info * about the trak. @@ -6653,29 +9175,41 @@ static gboolean qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) { GstByteReader tkhd; - int offset; - GNode *mdia; - GNode *mdhd; - GNode *hdlr; - GNode *minf; - GNode *stbl; - GNode *stsd; - GNode *mp4a; - GNode *mp4v; - GNode *wave; - GNode *esds; - GNode *pasp; - QtDemuxStream *stream; + int offset = 0; + GNode *mdia = NULL; + GNode *mdhd = NULL; + GNode *hdlr = NULL; + GNode *minf = NULL; + GNode *stbl = NULL; + GNode *stsd = NULL; + GNode *mp4a = NULL; + GNode *mp4v = NULL; + GNode *wave = NULL; + GNode *esds = NULL; + GNode *pasp = NULL; + QtDemuxStream *stream = NULL; GstTagList *list = NULL; gchar *codec = NULL; - const guint8 *stsd_data; - guint16 lang_code; /* quicktime lang code or packed iso code */ - guint32 version; + const guint8 *stsd_data = NULL; + guint16 lang_code = 0; /* quicktime lang code or packed iso code */ + guint32 version = 0; guint32 tkhd_flags = 0; guint8 tkhd_version = 0; - guint32 fourcc; - guint value_size, len; - + guint32 fourcc = 0; + guint value_size = 0, len = 0; +#ifdef QTDEMUX_MODIFICATION + guint32 track_id; + guint32 a = 0; + guint32 b = 0; + guint32 c = 0; + guint32 d = 0; + + guint16 layer = 0; + guint16 alternate_group = 0; + guint16 volume = 0; + GstClockTime duration = 0; + int height, width; +#else stream = g_new0 (QtDemuxStream, 1); /* new streams always need a discont */ stream->discont = TRUE; @@ -6686,12 +9220,8 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) stream->time_position = 0; stream->sample_index = -1; stream->last_ret = GST_FLOW_OK; -#ifdef QTDEMUX_MODIFICATION - stream->trickplay_info = g_new0 (TrickPlayInfo, 1); - stream->trickplay_info->prev_kidx = 0; - stream->trickplay_info->next_kidx = 0; - stream->trickplay_info->kidxs_dur_diff = 0; #endif + if (!qtdemux_tree_get_child_by_type_full (trak, FOURCC_tkhd, &tkhd) || !gst_byte_reader_get_uint8 (&tkhd, &tkhd_version) || !gst_byte_reader_get_uint24_be (&tkhd, &tkhd_flags)) @@ -6700,12 +9230,117 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) /* pick between 64 or 32 bits */ value_size = tkhd_version == 1 ? 8 : 4; if (!gst_byte_reader_skip (&tkhd, value_size * 2) || - !gst_byte_reader_get_uint32_be (&tkhd, &stream->track_id)) + !gst_byte_reader_get_uint32_be (&tkhd, &track_id)) goto corrupt_file; +#ifdef QTDEMUX_MODIFICATION + if (!qtdemux->got_moov) { + if (!qtdemux->got_moov && qtdemux_find_stream (qtdemux, track_id)) + goto existing_stream; + stream = _create_stream (qtdemux); + stream->track_id = track_id; + } else { + stream = qtdemux_find_stream (qtdemux, track_id); + if (!stream) { + GST_WARNING_OBJECT (qtdemux, "Stream not found, going to ignore it"); + goto skip_track; + } + } +#endif + GST_LOG_OBJECT (qtdemux, "track[tkhd] version/flags/id: 0x%02x/%06x/%u", tkhd_version, tkhd_flags, stream->track_id); +#ifdef QTDEMUX_MODIFICATION + if (!gst_byte_reader_skip (&tkhd, 4)) //skipping for reserved bits + goto corrupt_file; + + //reading duration from the tkhd atom, 64 bits if version is 1 and 32 bits if version is 0 + if(tkhd_version == 1) { + if(gst_byte_reader_get_uint64_be (&tkhd, &duration)) { + GST_LOG_OBJECT(qtdemux, "the duration from tkhd field is %"GST_TIME_FORMAT, GST_TIME_ARGS(duration)); + } else { + GST_LOG_OBJECT(qtdemux ,"no duration information present"); + goto corrupt_file; + } + } else { + if(gst_byte_reader_get_uint32_be (&tkhd, &duration)) { + GST_LOG_OBJECT(qtdemux, "the duration from tkhd field is %"GST_TIME_FORMAT, GST_TIME_ARGS(duration)); + } else { + GST_LOG_OBJECT(qtdemux, "no duration information present"); + goto corrupt_file; + } + } + + if(!gst_byte_reader_skip(&tkhd, 8)) { //skipping for reserved + GST_LOG_OBJECT(qtdemux, "tkhd doesn't have more information"); + goto corrupt_file; + } + +//reading layer, alternate_group and volume information from tkhd atom + if(!gst_byte_reader_get_uint16_be(&tkhd, &layer) || + !gst_byte_reader_get_uint16_be(&tkhd, &alternate_group) || + !gst_byte_reader_get_uint16_be(&tkhd, &volume)) { + GST_LOG_OBJECT(qtdemux, "tkhd doesn't have layer, group or volume information"); + goto corrupt_file; + } else { + GST_LOG_OBJECT(qtdemux, "the layer info is %d", layer); + GST_LOG_OBJECT(qtdemux, "the alternate group info is %d", alternate_group); + GST_LOG_OBJECT(qtdemux, "the volume information is %d", volume); + } + if(!gst_byte_reader_skip(&tkhd, 2)) { //skipping for reserved + GST_LOG_OBJECT(qtdemux, "no information present in tkhd"); + goto corrupt_file; + } + + //reading the orientation matrix information + if(!gst_byte_reader_get_uint32_be(&tkhd, &a) || + !gst_byte_reader_get_uint32_be(&tkhd, &b)) { + GST_LOG_OBJECT(qtdemux, "No matrix information present"); + goto corrupt_file; + } else { + GST_LOG_OBJECT(qtdemux, "matrix a and b info is %08x %08x", a, b); + } + + if(!gst_byte_reader_skip(&tkhd, 4)) { //skipping some unrelevant information + GST_LOG_OBJECT(qtdemux, "tkhd doesn't have more information"); + goto corrupt_file; + } + + if(!gst_byte_reader_get_uint32_be(&tkhd, &c) || + !gst_byte_reader_get_uint32_be(&tkhd, &d)) { + goto corrupt_file; + } else { + GST_LOG_OBJECT(qtdemux, "c and d info is %08x %08x", c, d); + } + + //Checking the orientation information with parsed matrix information + if(a == 0x00010000 && b == 0x00000000 && c == 0x00000000 && d == 0x00010000) + stream->orientation = 0; + if(a == 0x00000000 && b == 0x00010000 && c == 0xFFFF0000 && d == 0x00000000) + stream->orientation = 90; + if(a == 0xFFFF0000 && b == 0x00000000 && c == 0x00000000 && d == 0xFFFF0000) + stream->orientation = 180; + if(a == 0x00000000 && b == 0xFFFF0000 && c == 0x00010000 && d == 0x00000000) + stream->orientation = 270; + + GST_INFO("the orientation is %d", stream->orientation); + int cur_pos = gst_byte_reader_get_pos(&tkhd); + if(!gst_byte_reader_skip(&tkhd, 16)) { //skipping some unrelevant information + GST_LOG_OBJECT(qtdemux, "tkhd doesn't have more information"); + goto corrupt_file; + } + + if(!gst_byte_reader_get_uint32_be(&tkhd, &height) || + !gst_byte_reader_get_uint32_be(&tkhd, &width)) { + goto corrupt_file; + } else { + height = height >> 16; + width = width >> 16; + GST_LOG_OBJECT(qtdemux, "height and width info is %d %d", height, width); + } + gst_byte_reader_set_pos(&tkhd, cur_pos); +#endif if (!(mdia = qtdemux_tree_get_child_by_type (trak, FOURCC_mdia))) goto corrupt_file; @@ -6765,7 +9400,7 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) * some of those trailers, nowadays, have prologue images that are * themselves vide tracks as well. I haven't really found a way to * identify those yet, except for just looking at their duration. */ - if (tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) { + if ((stream->subtype == FOURCC_vide) && tdur1 != 0 && (tdur2 * 10 / tdur1) < 2) { GST_WARNING_OBJECT (qtdemux, "Track shorter than 20%% (%" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT " vs. %" G_GUINT64_FORMAT "/%" G_GUINT32_FORMAT ") of the stream " @@ -6817,9 +9452,12 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) stream->fourcc = fourcc = QT_FOURCC (stsd_data + 16 + 4); GST_LOG_OBJECT (qtdemux, "stsd type: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (stream->fourcc)); - +#ifdef QTDEMUX_MODIFICATION + if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi) ) +#else if ((fourcc == FOURCC_drms) || (fourcc == FOURCC_drmi) || ((fourcc & 0xFFFFFF00) == GST_MAKE_FOURCC ('e', 'n', 'c', 0))) +#endif goto error_encrypted; if (stream->subtype == FOURCC_vide) { @@ -6828,9 +9466,15 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) stream->sampled = TRUE; /* version 1 uses some 64-bit ints */ +#ifdef QTDEMUX_MODIFICATION + if (!gst_byte_reader_get_uint32_be (&tkhd, &w) + || !gst_byte_reader_get_uint32_be (&tkhd, &h)) +#else if (!gst_byte_reader_skip (&tkhd, 56 + value_size) || !gst_byte_reader_get_uint32_be (&tkhd, &w) || !gst_byte_reader_get_uint32_be (&tkhd, &h)) +#endif + goto corrupt_file; stream->display_width = w >> 16; @@ -6842,6 +9486,14 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) stream->width = QT_UINT16 (stsd_data + offset + 32); stream->height = QT_UINT16 (stsd_data + offset + 34); + if(stream->height == 0) { + stream->height = height; + GST_WARNING_OBJECT(qtdemux, "replacing height by tkhd since height obtained from stsd is zero"); + } + if(stream->width == 0) { + stream->width = width; + GST_WARNING_OBJECT(qtdemux, "replacing width by tkhd since width obtained from stsd is zero"); + } stream->fps_n = 0; /* this is filled in later */ stream->fps_d = 0; /* this is filled in later */ stream->bits_per_sample = QT_UINT16 (stsd_data + offset + 82); @@ -6985,6 +9637,59 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) break; } +#ifdef QTDEMUX_MODIFICATION + case GST_MAKE_FOURCC ('h', 'v', 'c', '1'): + { + gint len = QT_UINT32 (stsd_data) - 0x66; + const guint8 *hevc_data = stsd_data + 0x66; + + /* find hevc */ + while (len >= 0x8) { + gint size; + + if (QT_UINT32 (hevc_data) <= len) + size = QT_UINT32 (hevc_data) - 0x8; + else + size = len - 0x8; + + if (size < 1) + /* No real data, so break out */ + break; + + switch (QT_FOURCC (hevc_data + 0x4)) { + case GST_MAKE_FOURCC ('h', 'v', 'c', 'C'): + { + /* parse, if found */ + GstBuffer *buf; + + GST_DEBUG_OBJECT (qtdemux, "found hvcC codec_data in stsd"); + + /* First 4 bytes are the length of the atom, the next 4 bytes + * are the fourcc, the next 1 byte is the version, and the + * subsequent bytes are sequence parameter set like data. */ + //gst_codec_utils_hevc_caps_set_level_and_profile (stream->caps, + // hevc_data + 8 + 1, size - 1); + + buf = gst_buffer_new_and_alloc (size); + memcpy (GST_BUFFER_DATA (buf), hevc_data + 0x8, size); + gst_caps_set_simple (stream->caps, + "codec_data", GST_TYPE_BUFFER, buf, NULL); + gst_buffer_unref (buf); + + break; + } + + default: + break; + } + + len -= size + 8; + hevc_data += size + 8; + } + + break; + } +#endif case FOURCC_mp4v: case FOURCC_MP4V: case FOURCC_fmp4: @@ -7006,7 +9711,13 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) guint8 *data; GstBuffer *buf; gint len; - +#ifdef QTDEMUX_MODIFICATION + gsize size_codec=0; + gint idx=0; + guint8 *vol1=NULL; + guint8 *vol_start=NULL; + guint8 *vo_start=NULL; +#endif GST_DEBUG_OBJECT (qtdemux, "found glbl data in stsd"); data = glbl->data; len = QT_UINT32 (data); @@ -7014,8 +9725,36 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) len -= 0x8; buf = gst_buffer_new_and_alloc (len); memcpy (GST_BUFFER_DATA (buf), data + 8, len); - gst_caps_set_simple (stream->caps, - "codec_data", GST_TYPE_BUFFER, buf, NULL); +#ifdef QTDEMUX_MODIFICATION + vol1 = GST_BUFFER_DATA (buf); + size_codec=len; + while (idx < size_codec) { + if (vol1[idx] == 0x00 && vol1[idx+1] == 0x00 && vol1[idx+2] == 0x01 && (vol1[idx+3] >= 0x20 && vol1[idx+3] <= 0x2f)) { + vol_start = vol1+idx+3; + GST_DEBUG_OBJECT(qtdemux,"find the vol start byte"); + break; + } + if (vol1[idx] == 0x00 && vol1[idx+1] == 0x00 && vol1[idx+2] == 0x01 && vol1[idx+3]==0xb5) { + vo_start = vol1+idx+3; + GST_DEBUG_OBJECT(qtdemux,"find the vo start byte"); + } + idx++; + } + GstMpeg4VideoObjectLayer * vol; + GstMpeg4VisualObject * vo; + vol=g_malloc(sizeof(GstMpeg4VideoObjectLayer)); + vo=g_malloc(sizeof(GstMpeg4VisualObject)); + vol->data_partitioned=0; + if (vo_start!=NULL && vol_start!=NULL) { + if (gst_mpeg4_parse_visual_object(vo,vo_start,size_codec)==GST_MPEG4_PARSER_OK) { + gst_mpeg4_parse_video_object_layer (vol,vo,vol_start,size_codec); + } + } + gst_caps_set_simple (stream->caps,"data_partitioned", G_TYPE_BOOLEAN, vol->data_partitioned, NULL); + g_free(vol); + g_free(vo); +#endif + gst_caps_set_simple (stream->caps,"codec_data", GST_TYPE_BUFFER, buf, NULL); gst_buffer_unref (buf); } } @@ -7602,6 +10341,9 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) } } else GST_DEBUG ("Didn't find waveheadernode for this codec"); + } else { + GST_ERROR_OBJECT(qtdemux, "failed to parse node object"); + return FALSE; } g_node_destroy (wavenode); } @@ -7717,8 +10459,11 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) goto unknown_stream; } stream->sampled = TRUE; +#ifdef QTDEMUX_MODIFICATION + } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl) { +#else } else if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text) { - +#endif stream->sampled = TRUE; offset = 16; @@ -7801,9 +10546,59 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) 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; @@ -7844,26 +10639,58 @@ qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) lang_code = gst_tag_get_language_code (stream->lang_id); gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_LANGUAGE_CODE, (lang_code) ? lang_code : stream->lang_id, NULL); +#ifdef MULTI_AUDIO + if (stream->subtype == FOURCC_soun) + qtdemux->Language_list = g_list_append (qtdemux->Language_list,(lang_code) ? lang_code : stream->lang_id); +#endif +#ifdef QTDEMUX_MODIFICATION + if (stream->subtype == FOURCC_subp || stream->subtype == FOURCC_text || stream->subtype == FOURCC_sbtl) { + QtDemuxLanguageStruct* new = NULL; + new = g_new0 (QtDemuxLanguageStruct, 1); + new->language_code = (lang_code ? lang_code : stream->lang_id); + new->language_key = (lang_code ? lang_code : stream->lang_id); + new->active = FALSE; + qtdemux->Subtitle_language_list = g_list_append (qtdemux->Subtitle_language_list, new); + } +#endif } /* now we are ready to add the stream */ if (qtdemux->n_streams >= GST_QTDEMUX_MAX_STREAMS) goto too_many_streams; - stream->pending_tags = list; - qtdemux->streams[qtdemux->n_streams] = stream; - qtdemux->n_streams++; - GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams); +#ifdef QTDEMUX_MODIFICATION + if(!qtdemux->got_moov) { + stream->pending_tags = list; + qtdemux->streams[qtdemux->n_streams] = stream; + qtdemux->n_streams++; + GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams); + } +#else + stream->pending_tags = list; + qtdemux->streams[qtdemux->n_streams] = stream; + qtdemux->n_streams++; + GST_DEBUG_OBJECT (qtdemux, "n_streams is now %d", qtdemux->n_streams); +#endif return TRUE; /* ERRORS */ +#ifdef QTDEMUX_MODIFICATION +skip_track: + { + GST_INFO_OBJECT (qtdemux, "skip track"); + g_free (stream); + return TRUE; + } +#endif corrupt_file: { GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX, (_("This file is corrupt and cannot be played.")), (NULL)); #ifdef QTDEMUX_MODIFICATION - g_free (stream->trickplay_info); + if(stream && stream->trickplay_info) + g_free (stream->trickplay_info); #endif g_free (stream); return FALSE; @@ -7889,6 +10716,14 @@ segments_failed: g_free (stream); return FALSE; } +#ifdef QTDEMUX_MODIFICATION +existing_stream: + { + GST_INFO_OBJECT (qtdemux, "stream with track id %i already exists", + track_id); + return TRUE; + } +#endif unknown_stream: { GST_INFO_OBJECT (qtdemux, "unknown subtype %" GST_FOURCC_FORMAT, @@ -8001,6 +10836,147 @@ gst_qtdemux_guess_bitrate (GstQTDemux * qtdemux) GST_TAG_BITRATE, bitrate, NULL); } +#ifdef QTDEMUX_MODIFICATION +static GstFlowReturn +qtdemux_prepare_streams (GstQTDemux * qtdemux) +{ + gint i; + GstFlowReturn ret = GST_FLOW_OK; + + GST_DEBUG_OBJECT (qtdemux, "preparing streams"); + + for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) { + QtDemuxStream *stream = qtdemux->streams[i]; + guint32 sample_num = 0; + guint samples = 20; + GArray *durations; + + GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT, + i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc)); + if (qtdemux->fragmented) { + /* need all moov samples first */ + GST_OBJECT_LOCK (qtdemux); + while (stream->n_samples == 0) + if ((ret = qtdemux_add_fragmented_samples (qtdemux)) != GST_FLOW_OK) + break; + GST_OBJECT_UNLOCK (qtdemux); + } else { + /* discard any stray moof */ + qtdemux->moof_offset = 0; + } + + /* prepare braking */ + if (ret != GST_FLOW_ERROR) + ret = GST_FLOW_OK; + + /* in pull mode, we should have parsed some sample info by now; + * and quite some code will not handle no samples. + * in push mode, we'll just have to deal with it */ + if (G_UNLIKELY (qtdemux->pullbased && !stream->n_samples)) { + GST_DEBUG_OBJECT (qtdemux, "no samples for stream; discarding"); + gst_qtdemux_stream_free (qtdemux, stream); + memmove (&(qtdemux->streams[i]), &(qtdemux->streams[i + 1]), + sizeof (QtDemuxStream *) * (GST_QTDEMUX_MAX_STREAMS - i - 1)); + qtdemux->streams[GST_QTDEMUX_MAX_STREAMS - 1] = NULL; + qtdemux->n_streams--; + i--; + continue; + } + + /* parse number of initial sample to set frame rate cap */ + while (sample_num < stream->n_samples && sample_num < samples) { + if (!qtdemux_parse_samples (qtdemux, stream, sample_num)) + break; + ++sample_num; + } + /* collect and sort durations */ + samples = MIN (stream->stbl_index + 1, samples); + GST_DEBUG_OBJECT (qtdemux, "%d samples for framerate", samples); + if (samples) { + durations = g_array_sized_new (FALSE, FALSE, sizeof (guint32), samples); + sample_num = 0; + while (sample_num < samples) { + g_array_append_val (durations, stream->samples[sample_num].duration); + sample_num++; + } + g_array_sort (durations, less_than); + stream->min_duration = g_array_index (durations, guint32, samples / 2); + g_array_free (durations, TRUE); + } + } + return ret; +} + +static GstFlowReturn +qtdemux_expose_streams (GstQTDemux * qtdemux) +{ + gint i; + GstFlowReturn ret = GST_FLOW_OK; + GSList *oldpads = NULL; + GSList *iter; + + GST_DEBUG_OBJECT (qtdemux, "exposing streams"); + + for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) { + QtDemuxStream *stream = qtdemux->streams[i]; + GstPad *oldpad = stream->pad; + GstTagList *list; + + GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT, + i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc)); + + /* now we have all info and can expose */ + list = stream->pending_tags; + stream->pending_tags = NULL; + if (oldpad) + oldpads = g_slist_prepend (oldpads, oldpad); + gst_qtdemux_add_stream (qtdemux, stream, list); +#ifdef DRM_ENABLE + if (qtdemux->encrypt_content && stream->subtype == FOURCC_vide) { + GstEvent* drm_custom_event = NULL; + drm_custom_event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, + gst_structure_new ("Content_Is_DRM_Playready", NULL)); + GST_DEBUG_OBJECT (qtdemux, "sending custom event to xvimagesink for playready content"); + if (!gst_pad_push_event (stream->pad, drm_custom_event)) + GST_ERROR_OBJECT (qtdemux, "failed to push custom event for drm playready content"); + } +#endif + } + + gst_qtdemux_guess_bitrate (qtdemux); + + gst_element_no_more_pads (GST_ELEMENT_CAST (qtdemux)); + + for (iter = oldpads; iter; iter = g_slist_next (iter)) { + GstPad *oldpad = iter->data; + + gst_pad_push_event (oldpad, gst_event_new_eos ()); + gst_pad_set_active (oldpad, FALSE); + gst_element_remove_pad (GST_ELEMENT (qtdemux), oldpad); + gst_object_unref (oldpad); + } + + /* check if we should post a redirect in case there is a single trak + * and it is a redirecting trak */ + if (qtdemux->n_streams == 1 && qtdemux->streams[0]->redirect_uri != NULL) { + GstMessage *m; + + qtdemux_post_global_tags (qtdemux); + + GST_INFO_OBJECT (qtdemux, "Issuing a redirect due to a single track with " + "an external content"); + m = gst_message_new_element (GST_OBJECT_CAST (qtdemux), + gst_structure_new ("redirect", + "new-location", G_TYPE_STRING, qtdemux->streams[0]->redirect_uri, + NULL)); + gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m); + qtdemux->posted_redirect = TRUE; + } + + return ret; +} +#else +/* Deprecated */ static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux) { @@ -8100,6 +11076,7 @@ qtdemux_expose_streams (GstQTDemux * qtdemux) return ret; } +#endif /* check if major or compatible brand is 3GP */ static inline gboolean qtdemux_is_brand_3gp (GstQTDemux * qtdemux, gboolean major) @@ -8926,8 +11903,6 @@ qtdemux_tag_add_blob (GNode * node, GstQTDemux * demux) if (QT_FOURCC (data + 4) == FOURCC_____ || (len > 8 + 12 && QT_FOURCC (data + 12) == FOURCC_data)) style = "itunes"; - else if (demux->major_brand == FOURCC_qt__) - style = "quicktime"; /* fall back to assuming iso/3gp tag style */ else style = "iso"; @@ -9116,82 +12091,6 @@ qtdemux_process_redirects (GstQTDemux * qtdemux, GList * references) static gboolean qtdemux_parse_redirects (GstQTDemux * qtdemux) { - GNode *rmra, *rmda, *rdrf; - - rmra = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_rmra); - if (rmra) { - GList *redirects = NULL; - - rmda = qtdemux_tree_get_child_by_type (rmra, FOURCC_rmda); - while (rmda) { - GstQtReference ref = { NULL, NULL, 0, 0 }; - GNode *rmdr, *rmvc; - - if ((rmdr = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmdr))) { - ref.min_req_bitrate = QT_UINT32 ((guint8 *) rmdr->data + 12); - GST_LOG_OBJECT (qtdemux, "data rate atom, required bitrate = %u", - ref.min_req_bitrate); - } - - if ((rmvc = qtdemux_tree_get_child_by_type (rmda, FOURCC_rmvc))) { - guint32 package = QT_FOURCC ((guint8 *) rmvc->data + 12); - guint version = QT_UINT32 ((guint8 *) rmvc->data + 16); - -#ifndef GST_DISABLE_GST_DEBUG - guint bitmask = QT_UINT32 ((guint8 *) rmvc->data + 20); -#endif - guint check_type = QT_UINT16 ((guint8 *) rmvc->data + 24); - - GST_LOG_OBJECT (qtdemux, - "version check atom [%" GST_FOURCC_FORMAT "], version=0x%08x" - ", mask=%08x, check_type=%u", GST_FOURCC_ARGS (package), version, - bitmask, check_type); - if (package == FOURCC_qtim && check_type == 0) { - ref.min_req_qt_version = version; - } - } - - rdrf = qtdemux_tree_get_child_by_type (rmda, FOURCC_rdrf); - if (rdrf) { - guint32 ref_type; - guint8 *ref_data; - - ref_type = QT_FOURCC ((guint8 *) rdrf->data + 12); - ref_data = (guint8 *) rdrf->data + 20; - if (ref_type == FOURCC_alis) { - guint record_len, record_version, fn_len; - - /* MacOSX alias record, google for alias-layout.txt */ - record_len = QT_UINT16 (ref_data + 4); - record_version = QT_UINT16 (ref_data + 4 + 2); - fn_len = QT_UINT8 (ref_data + 50); - if (record_len > 50 && record_version == 2 && fn_len > 0) { - ref.location = g_strndup ((gchar *) ref_data + 51, fn_len); - } - } else if (ref_type == FOURCC_url_) { - ref.location = g_strdup ((gchar *) ref_data); - } else { - GST_DEBUG_OBJECT (qtdemux, - "unknown rdrf reference type %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (ref_type)); - } - if (ref.location != NULL) { - GST_INFO_OBJECT (qtdemux, "New location: %s", ref.location); - redirects = g_list_prepend (redirects, g_memdup (&ref, sizeof (ref))); - } else { - GST_WARNING_OBJECT (qtdemux, - "Failed to extract redirect location from rdrf atom"); - } - } - - /* look for others */ - rmda = qtdemux_tree_get_sibling_by_type (rmda, FOURCC_rmda); - } - - if (redirects != NULL) { - qtdemux_process_redirects (qtdemux, redirects); - } - } return TRUE; } @@ -9207,8 +12106,6 @@ qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags) fmt = "Motion JPEG 2000"; else if ((qtdemux->major_brand & 0xffff) == GST_MAKE_FOURCC ('3', 'g', 0, 0)) fmt = "3GP"; - else if (qtdemux->major_brand == FOURCC_qt__) - fmt = "Quicktime"; else if (qtdemux->fragmented) fmt = "ISO fMP4"; else @@ -9230,14 +12127,17 @@ qtdemux_add_container_format (GstQTDemux * qtdemux, GstTagList * tags) static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux) { - GNode *mvhd; - GNode *trak; - GNode *udta; - GNode *mvex; - gint64 duration; - guint64 creation_time; + GNode *mvhd = NULL; + GNode *trak = NULL; + GNode *udta = NULL; + GNode *mvex = NULL; + gint64 duration = 0; + guint64 creation_time = 0; GstDateTime *datetime = NULL; - gint version; + gint version = 0; +#ifdef QTDEMUX_MODIFICATION + GNode *uuid_node; +#endif mvhd = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_mvhd); if (mvhd == NULL) { @@ -9322,8 +12222,33 @@ qtdemux_parse_tree (GstQTDemux * qtdemux) /* iterate all siblings */ trak = qtdemux_tree_get_sibling_by_type (trak, FOURCC_trak); } - - /* find tags */ +#ifdef MULTI_AUDIO + if(qtdemux->Language_list != NULL) + { + GstMessage *m; + m = gst_message_new_element(GST_OBJECT_CAST(qtdemux),gst_structure_new("Language_list","lang_list",G_TYPE_POINTER,qtdemux->Language_list,NULL)); + gst_element_post_message(GST_ELEMENT_CAST(qtdemux),m); + } +#endif +#ifdef QTDEMUX_MODIFICATION + if (qtdemux->Subtitle_language_list != NULL) { + QtDemuxLanguageStruct* First_Language = g_list_nth_data (qtdemux->Subtitle_language_list, 0); + First_Language->active = TRUE; + GstMessage *m; + m = gst_message_new_element (GST_OBJECT_CAST (qtdemux), + gst_structure_new ("Int_Sub_Language_List", "lang_list", + G_TYPE_POINTER, qtdemux->Subtitle_language_list, NULL)); + gst_element_post_message (GST_ELEMENT_CAST (qtdemux), m); + } + uuid_node = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_uuid); +#ifdef DRM_ENABLE + if (uuid_node && qtdemux->protection_header_present) { + GST_INFO_OBJECT(qtdemux,"calling qtdemux_playready_parse_uuid"); + return qtdemux_playready_parse_uuid(uuid_node,qtdemux); + } +#endif // DRM_ENABLE +#endif +/* find tags */ udta = qtdemux_tree_get_child_by_type (qtdemux->moov_node, FOURCC_udta); if (udta) { qtdemux_parse_udta (qtdemux, udta); @@ -9556,10 +12481,43 @@ gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream, /* Add the codec_data attribute to caps, if we have it */ if (data_ptr) { GstBuffer *buffer; +#ifdef QTDEMUX_MODIFICATION + gsize size_codec=0; + gint idx=0; + guint8 *vol1=NULL; + guint8 *vo_start=NULL; + guint8 *vol_start=NULL; + GstMpeg4VideoObjectLayer * vol; + GstMpeg4VisualObject * vo; +#endif buffer = gst_buffer_new_and_alloc (data_len); memcpy (GST_BUFFER_DATA (buffer), data_ptr, data_len); - +#ifdef QTDEMUX_MODIFICATION + GST_DEBUG_OBJECT (qtdemux,"found not glbl data in stsd"); + size_codec=data_len; + vol1 = GST_BUFFER_DATA (buffer); + while (idx < size_codec) { + if (vol1[idx] == 0x00 && vol1[idx+1] == 0x00 && vol1[idx+2] == 0x01 && (vol1[idx+3] >= 0x20 && vol1[idx+3] <= 0x2f)) { + vol_start = vol1+idx+3; + GST_DEBUG_OBJECT(qtdemux,"find the vol byte"); + break; + } + if (vol1[idx] == 0x00 && vol1[idx+1] == 0x00 && vol1[idx+2] == 0x01 && vol1[idx+3]==0xb5) { + vo_start = vol1+idx+3; + GST_DEBUG_OBJECT(qtdemux,"find the VO byte"); + } + idx++; + } + vol=g_malloc(sizeof(GstMpeg4VideoObjectLayer)); + vo=g_malloc(sizeof(GstMpeg4VisualObject)); + vol->data_partitioned=0; + if (vo_start!=NULL && vol_start!=NULL) { + if (gst_mpeg4_parse_visual_object(vo,vo_start,size_codec)==GST_MPEG4_PARSER_OK){ + gst_mpeg4_parse_video_object_layer (vol,vo, vol_start,size_codec); + } + } +#endif GST_DEBUG_OBJECT (qtdemux, "setting codec_data from esds"); GST_MEMDUMP_OBJECT (qtdemux, "codec_data from esds", data_ptr, data_len); @@ -9569,10 +12527,10 @@ gst_qtdemux_handle_esds (GstQTDemux * qtdemux, QtDemuxStream * stream, gboolean err = TRUE; int i; - GST_ERROR_OBJECT (qtdemux, "Checking for the Possibility of H263"); + GST_INFO_OBJECT (qtdemux, "Checking for the Possibility of H263"); for(i=0; icaps,"data_partitioned", G_TYPE_BOOLEAN, vol->data_partitioned, NULL); + g_free(vol); + g_free(vo); +#endif gst_caps_set_simple (stream->caps, "codec_data", GST_TYPE_BUFFER, buffer, NULL); gst_buffer_unref (buffer); @@ -9917,6 +12879,12 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'), NULL); break; +#ifdef QTDEMUX_MODIFICATION + case GST_MAKE_FOURCC ('h', 'v', 'c', '1'): + _codec ("HEVC"); + caps = gst_caps_from_string ("video/hevc"); + break; +#endif case GST_MAKE_FOURCC ('k', 'p', 'c', 'd'): default: { @@ -9929,6 +12897,12 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream, } } +#ifdef QTDEMUX_MODIFICATION + if(stream->orientation != -1) { + gst_caps_set_simple (caps, "orientation", G_TYPE_INT, stream->orientation, NULL); + } +#endif + /* enable clipping for raw video streams */ s = gst_caps_get_structure (caps, 0); name = gst_structure_get_name (s); @@ -10216,7 +13190,10 @@ gst_qtdemux_forward_trickplay (GstQTDemux * qtdemux, QtDemuxStream * stream, gui /* find no.of samples between present and previous key frames */ nsamples = stream->trickplay_info->next_kidx - stream->trickplay_info->prev_kidx; - + if(nsamples == 0){ + GST_LOG_OBJECT (qtdemux, "nsamples comes as 0.. not applying trickplay algo"); + return; + } /* find average duration between key frames */ stream->trickplay_info->kidxs_dur_diff = (QTSAMPLE_PTS (stream, &stream->samples[stream->trickplay_info->next_kidx]) - QTSAMPLE_PTS (stream, &stream->samples[stream->trickplay_info->prev_kidx])) / @@ -10316,5 +13293,193 @@ beach: return new_index; } +#ifdef DRM_ENABLE +/* Provides key to decrypt encrypted data.*/ +static gboolean +qtdemux_get_playready_licence (GstQTDemux * qtdemux, unsigned char * data, guint size, char* query_file_path) +{ + int ret = -1; + char *tok = NULL; + char *parse_string = NULL; + char *parse_sub_string = NULL; + int query_length = 0; + int count = 0; + drm_trusted_piff_get_license_info_s license_info; + drm_trusted_request_type_e request_type = DRM_TRUSTED_REQ_TYPE_PIFF_GET_LICENSE; + GST_DEBUG_OBJECT(qtdemux,"size is %d",size); + memset(&license_info, 0x00, sizeof(drm_trusted_piff_get_license_info_s)); + + query_length = strlen(query_file_path); + license_info.lic_header.header = (unsigned char*)data; + license_info.lic_header.header_len = size; + //license_info.content_provider = DRM_TRUSTED_CONTENT_PROVIDER_VIDEO_HUB; + + GST_DEBUG("filepath infor is %s and lenght is %d", query_file_path, query_length); + parse_string = (char*)malloc(sizeof(char) * (query_length + 1)); + if(parse_string == NULL) { + GST_ERROR_OBJECT(qtdemux, "Failed to allocate memory"); + return FALSE; + } + strncpy(parse_string, query_file_path, query_length); + parse_string[query_length] = '\0'; + GST_DEBUG("parse string infor is %s and lenght is %d", parse_string, query_length); + + parse_sub_string = strstr(parse_string, "[]<>"); + GST_DEBUG("filepath infor is %s", parse_sub_string); + + tok = strtok(parse_sub_string, "[]<>"); + while(tok != NULL) { + GST_DEBUG("some info %s", tok); + count++; + if(count == 1) + strcpy(license_info.custom_data.app_id, tok); + if(count == 2) + strcpy(license_info.custom_data.user_guid, tok); + if(count == 3) + strcpy(license_info.custom_data.device_id, tok); + if(count == 4) + strcpy(license_info.custom_data.order_id, tok); + if(count == 5) + strcpy(license_info.custom_data.mv_id, tok); + if(count == 6) + strcpy(license_info.custom_data.svr_id, tok); + tok = strtok(NULL, "[]<>"); + } + free (parse_string); + ret = drm_trusted_handle_request (request_type, (void *) &license_info, NULL); + GST_DEBUG_OBJECT(qtdemux, "return value from drm handle request is 0x%x", ret); + if (DRM_TRUSTED_RETURN_SUCCESS != ret) { + GST_ERROR_OBJECT (qtdemux,"failed to get license..."); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT_NOKEY, ("failed to get playready license"), (NULL)); + return FALSE; + } + + GST_INFO_OBJECT (qtdemux, "Got license....\n"); + + return TRUE; +} + +void +test_drm_trusted_operation_cb(drm_trusted_user_operation_info_s *operation_info, void *output_data) +{ + GST_DEBUG ("Callback Hit:test_drm_trusted_operation_cb\n"); + GST_DEBUG ("operation_status=%d\n",operation_info->operation_status); + GST_DEBUG ("operation_type=%d\n",operation_info->operation_type); +} + +/* Find out which uuid type is present in uuid node. + and returns uuid_type present in uuid node.*/ + +static uuid_type_t +qtdemux_get_uuid_type(GstQTDemux * qtdemux, GstByteReader *uuid_data, gint64 *uuid_offset) +{ + guint32 box_len = 0; + guint64 box_long_len = 0; + gchar uuid[16] = {0,}; + int i = 0; + + if (!gst_byte_reader_get_uint32_be (uuid_data, &box_len)) + goto invalid_uuid; + + /* Skipping fourcc */ + if (!gst_byte_reader_skip (uuid_data, 4)) + goto invalid_uuid; + if (box_len == 1) { + GST_WARNING_OBJECT (qtdemux, "TfxdBoxLongLength field is present..."); + if (!gst_byte_reader_get_uint64_be (uuid_data, &box_long_len)) + goto invalid_uuid; + + GST_DEBUG_OBJECT (qtdemux, "tfxd long length = %llu", box_long_len); + + *uuid_offset = box_long_len; + } else { + GST_DEBUG_OBJECT (qtdemux, "Box Len = %d", (box_len)); + *uuid_offset = box_len; + } + + for (i = 0; i < sizeof (uuid); i++) { + if (!gst_byte_reader_get_uint8 (uuid_data, &(uuid[i]))) + goto invalid_uuid; + } + + if (!memcmp(uuid, tfxd_uuid, sizeof (uuid_t))) { + GST_INFO_OBJECT (qtdemux, "Found TFXD box"); + return UUID_TFXD; + } else if (!memcmp(uuid, tfrf_uuid, sizeof (uuid_t))) { + GST_INFO_OBJECT (qtdemux, "Found TFRF box"); + return UUID_TFRF; + } else if (!memcmp(uuid, encrypt_uuid, sizeof (uuid_t))) { + GST_INFO_OBJECT (qtdemux,"Found sample encryption box"); + return UUID_SAMPLE_ENCRYPT; + } else if (!memcmp(uuid, protection_uuid, sizeof (uuid_t))) { + GST_INFO_OBJECT (qtdemux,"Found protection header box"); + for (i = 0; i < sizeof (uuid); i++) { + qtdemux->uuid_protection[i] = uuid[i]; + } + return UUID_PROTECTION_HEADER; + } else { + GST_WARNING_OBJECT (qtdemux, "Not an valid UUID box.."); + goto invalid_uuid; + } + +invalid_uuid: + GST_ERROR_OBJECT (qtdemux, "Error in parsing UUID atom..."); + return UUID_UNKNOWN; +} + +/*Authenticating whether valid play ready system ID is present in uuid node or not.*/ + +static gboolean +qtdemux_parse_playready_system_id(GstQTDemux * qtdemux, GstByteReader *uuid_data) +{ + //gchar uuid[16] = {0,}; + guint i; + if (!gst_byte_reader_skip (uuid_data, 4)) + goto invalid_uuid; + + for (i = 0; i < sizeof (qtdemux->uuid_playready); i++){ + if (!gst_byte_reader_get_uint8 (uuid_data, &(qtdemux->uuid_playready[i]))) + goto invalid_uuid; + } + + if (!memcmp(qtdemux->uuid_playready, playready_system_id, sizeof (uuid_t))){ + GST_INFO_OBJECT (qtdemux, "Found Play Ready System ID"); + return TRUE; + } + + if (!memcmp(qtdemux->uuid_playready, dash_playready_system_id, sizeof (uuid_t))){ + GST_INFO_OBJECT (qtdemux, "Found DASH Play Ready System ID"); + return TRUE; + } + +invalid_uuid: + GST_ERROR_OBJECT (qtdemux, "Invalid system ID..."); + return FALSE; +} + +static void +qtdemux_post_drm_error (GstQTDemux * qtdemux, int drm_error) +{ + switch (drm_error) { + case DRM_LICENSE_STATUS_EXPIRED: + GST_ERROR_OBJECT (qtdemux, "posting error as rights expired"); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT_NOKEY, ("rights expired"), (NULL)); + break; + case DRM_LICENSE_STATUS_NO_LICENSE: + GST_ERROR_OBJECT (qtdemux, "posting error as no rights"); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT_NOKEY, ("no rights"), (NULL)); + break; + case DRM_LICENSE_STATUS_FUTURE_USE: + GST_ERROR_OBJECT (qtdemux, "posting error as rights for future"); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT_NOKEY, ("has future rights"), (NULL)); + break; + case DRM_LICENSE_STATUS_UNDEFINED: + default: + GST_ERROR_OBJECT (qtdemux, "posting error as undefined"); + GST_ELEMENT_ERROR (qtdemux, STREAM, DECRYPT_NOKEY, ("undefind error"), (NULL)); + break; + } +} +#endif #endif diff --git a/gst/isomp4/qtdemux.h b/gst/isomp4/qtdemux.h old mode 100644 new mode 100755 index dd60dcc..71bd4a2 --- a/gst/isomp4/qtdemux.h +++ b/gst/isomp4/qtdemux.h @@ -25,9 +25,17 @@ #include #define QTDEMUX_MODIFICATION +#define MULTI_AUDIO 1 #ifdef QTDEMUX_MODIFICATION #include +#ifdef DRM_ENABLE +#include +#include +#include +#include +#endif +#include #endif G_BEGIN_DECLS @@ -58,6 +66,149 @@ typedef struct _GstQTDemux GstQTDemux; typedef struct _GstQTDemuxClass GstQTDemuxClass; typedef struct _QtDemuxStream QtDemuxStream; +#ifdef QTDEMUX_MODIFICATION +typedef struct _GstMpeg4VideoObjectLayer GstMpeg4VideoObjectLayer; +typedef struct _GstMpeg4VisualObject GstMpeg4VisualObject; +typedef struct _QtDemuxDrm QtDemuxDrm; +typedef enum { + GST_MPEG4_SQUARE = 0x01, + GST_MPEG4_625_TYPE_4_3 = 0x02, + GST_MPEG4_525_TYPE_4_3 = 0x03, + GST_MPEG4_625_TYPE_16_9 = 0x04, + GST_MPEG4_525_TYPE_16_9 = 0x05, + GST_MPEG4_EXTENDED_PAR = 0x0f, +} GstMpeg4AspectRatioInfo; +typedef enum { + GST_MPEG4_VIDEO_ID = 0x01, + GST_MPEG4_STILL_TEXTURE_ID = 0x02, + GST_MPEG4_STILL_MESH_ID = 0x03, + GST_MPEG4_STILL_FBA_ID = 0x04, + GST_MPEG4_STILL_3D_MESH_ID = 0x05, + /*... reserved */ + +} GstMpeg4VisualObjectType; +typedef enum { + /* Other value are reserved */ + GST_MPEG4_CHROMA_4_2_0 = 0x01 +} GstMpeg4ChromaFormat; +typedef enum { + GST_MPEG4_RECTANGULAR, + GST_MPEG4_BINARY, + GST_MPEG4_BINARY_ONLY, + GST_MPEG4_GRAYSCALE +} GstMpeg4VideoObjectLayerShape; +typedef enum { + GST_MPEG4_SPRITE_UNUSED, + GST_MPEG4_SPRITE_STATIC, + GST_MPEG4_SPRITE_GMG +} GstMpeg4SpriteEnable; +typedef enum { + GST_MPEG4_PARSER_OK, + GST_MPEG4_PARSER_BROKEN_DATA, + GST_MPEG4_PARSER_NO_PACKET, + GST_MPEG4_PARSER_NO_PACKET_END, + GST_MPEG4_PARSER_ERROR, +} GstMpeg4ParseResult; +struct _GstMpeg4VideoObjectLayer { + guint8 random_accessible_vol; + guint8 video_object_type_indication; + + guint8 is_object_layer_identifier; + /* if is_object_layer_identifier */ + guint8 verid; + guint8 priority; + + GstMpeg4AspectRatioInfo aspect_ratio_info; + guint8 par_width; + guint8 par_height; + + guint8 control_parameters; + /* if control_parameters */ + GstMpeg4ChromaFormat chroma_format; + guint8 low_delay; + guint8 vbv_parameters; + /* if vbv_parameters */ + guint16 first_half_bitrate; + guint16 latter_half_bitrate; + guint16 first_half_vbv_buffer_size; + guint16 latter_half_vbv_buffer_size; + guint16 first_half_vbv_occupancy; + guint16 latter_half_vbv_occupancy; + + /* Computed values */ + guint32 bit_rate; + guint32 vbv_buffer_size; + + GstMpeg4VideoObjectLayerShape shape; + /* if shape == GST_MPEG4_GRAYSCALE && verid =! 1 */ + guint8 shape_extension; + + guint16 vop_time_increment_resolution; + guint8 vop_time_increment_bits; + guint8 fixed_vop_rate; + /* if fixed_vop_rate */ + guint16 fixed_vop_time_increment; + + guint16 width; + guint16 height; + guint8 interlaced; + guint8 obmc_disable; + + GstMpeg4SpriteEnable sprite_enable; + /* if vol->sprite_enable == SPRITE_GMG or SPRITE_STATIC*/ + /* if vol->sprite_enable != GST_MPEG4_SPRITE_GMG */ + guint16 sprite_width; + guint16 sprite_height; + guint16 sprite_left_coordinate; + guint16 sprite_top_coordinate; + + guint8 no_of_sprite_warping_points; + guint8 sprite_warping_accuracy; + guint8 sprite_brightness_change; + /* if vol->sprite_enable != GST_MPEG4_SPRITE_GMG */ + guint8 low_latency_sprite_enable; + + /* if shape != GST_MPEG4_RECTANGULAR */ + guint8 sadct_disable; + + guint8 not_8_bit; + + /* if no_8_bit */ + guint8 quant_precision; + guint8 bits_per_pixel; + + /* if shape == GRAYSCALE */ + guint8 no_gray_quant_update; + guint8 composition_method; + guint8 linear_composition; + + guint8 quant_type; + /* if quant_type */ + guint8 load_intra_quant_mat; + guint8 intra_quant_mat[64]; + guint8 load_non_intra_quant_mat; + guint8 non_intra_quant_mat[64]; + + guint8 quarter_sample; + guint8 complexity_estimation_disable; + guint8 resync_marker_disable; + guint8 data_partitioned; + guint8 reversible_vlc; + guint8 newpred_enable; + guint8 reduced_resolution_vop_enable; + guint8 scalability; + guint8 enhancement_type; + + //GstMpeg4VideoPlaneShortHdr short_hdr; +}; +struct _GstMpeg4VisualObject { + guint8 is_identifier; + /* If is_identifier */ + guint8 verid; + guint8 priority; + GstMpeg4VisualObjectType type; +}; +#endif struct _GstQTDemux { GstElement element; @@ -77,7 +228,6 @@ struct _GstQTDemux { guint32 timescale; guint64 duration; - gboolean fragmented; /* offset of the mfra atom */ guint64 mfra_offset; @@ -118,14 +268,49 @@ struct _GstQTDemux { gboolean upstream_seekable; gboolean upstream_size; - +#ifdef MULTI_AUDIO + GList *Language_list; +#endif #ifdef QTDEMUX_MODIFICATION + /*For smooth streaming*/ + gboolean is_dash; + gint64 dash_init_offset; + gint video_max_width; + gint video_max_height; + + gboolean protection_header_present; FILE* file; FILE* ofile; gchar* filename; guint filesize; guint maxbuffersize; gboolean fwdtrick_mode; +#ifdef DRM_ENABLE + gboolean encrypt_content; + DRM_DECRYPT_HANDLE pr_handle; + gchar uuid_playready[16]; + gchar uuid_protection[16]; +#endif + gboolean piff_fragmented; + GstClockTime max_pop_ts; + gboolean playback_protected; + gboolean need_parsing_moov; + gint iv_size_video; + gint iv_size_audio; + gchar **iv_data_video; + gchar **iv_data_audio; + gint sequence_id; + gint sample_count[2]; + gint current_sample[2]; + gint *sub_sample_count; + gint *clear_data; + gint *encrypted_data; + gboolean dash_content; + gint no_of_audio_samples; + gint no_of_video_samples; + GArray *moof_offsets; + gboolean need_moof_parsing; + GList *Subtitle_language_list; #endif }; diff --git a/gst/isomp4/qtdemux_dump.c b/gst/isomp4/qtdemux_dump.c index fa66767..a45afd9 100644 --- a/gst/isomp4/qtdemux_dump.c +++ b/gst/isomp4/qtdemux_dump.c @@ -330,27 +330,6 @@ qtdemux_dump_stts (GstQTDemux * qtdemux, GstByteReader * data, int depth) } gboolean -qtdemux_dump_stps (GstQTDemux * qtdemux, GstByteReader * data, int depth) -{ - guint32 ver_flags = 0, num_entries = 0, i; - - if (!gst_byte_reader_get_uint32_be (data, &ver_flags) || - !gst_byte_reader_get_uint32_be (data, &num_entries)) - return FALSE; - - GST_LOG ("%*s version/flags: %08x", depth, "", ver_flags); - GST_LOG ("%*s n entries: %d", depth, "", num_entries); - - if (!qt_atom_parser_has_chunks (data, num_entries, 4)) - return FALSE; - - for (i = 0; i < num_entries; i++) { - GST_LOG ("%*s sample: %u", depth, "", GET_UINT32 (data)); - } - return TRUE; -} - -gboolean qtdemux_dump_stss (GstQTDemux * qtdemux, GstByteReader * data, int depth) { guint32 ver_flags = 0, num_entries = 0, i; @@ -489,27 +468,6 @@ qtdemux_dump_co64 (GstQTDemux * qtdemux, GstByteReader * data, int depth) } gboolean -qtdemux_dump_dcom (GstQTDemux * qtdemux, GstByteReader * data, int depth) -{ - if (!qt_atom_parser_has_remaining (data, 4)) - return FALSE; - - GST_LOG ("%*s compression type: %" GST_FOURCC_FORMAT, depth, "", - GST_FOURCC_ARGS (GET_FOURCC (data))); - return TRUE; -} - -gboolean -qtdemux_dump_cmvd (GstQTDemux * qtdemux, GstByteReader * data, int depth) -{ - if (!qt_atom_parser_has_remaining (data, 4)) - return FALSE; - - GST_LOG ("%*s length: %d", depth, "", GET_UINT32 (data)); - return TRUE; -} - -gboolean qtdemux_dump_mfro (GstQTDemux * qtdemux, GstByteReader * data, int depth) { if (!qt_atom_parser_has_remaining (data, 4)) @@ -721,6 +679,28 @@ qtdemux_dump_mehd (GstQTDemux * qtdemux, GstByteReader * data, int depth) } gboolean +qtdemux_dump_tfdt (GstQTDemux * qtdemux, GstByteReader * data, int depth) +{ + guint32 version = 0; + guint64 decode_time; + guint value_size; + + if (!gst_byte_reader_get_uint32_be (data, &version)) + return FALSE; + + GST_LOG ("%*s version/flags: %08x", depth, "", version); + + value_size = ((version >> 24) == 1) ? sizeof (guint64) : sizeof (guint32); + if (qt_atom_parser_get_offset (data, value_size, &decode_time)) { + GST_LOG ("%*s Track fragment decode time: %" G_GUINT64_FORMAT, + depth, "", decode_time); + return TRUE; + } + + return FALSE; +} + +gboolean qtdemux_dump_sdtp (GstQTDemux * qtdemux, GstByteReader * data, int depth) { guint32 version; diff --git a/gst/isomp4/qtdemux_dump.h b/gst/isomp4/qtdemux_dump.h index 9bb1f95..988c822 100644 --- a/gst/isomp4/qtdemux_dump.h +++ b/gst/isomp4/qtdemux_dump.h @@ -45,8 +45,6 @@ gboolean qtdemux_dump_stts (GstQTDemux * qtdemux, GstByteReader * data, int depth); gboolean qtdemux_dump_stss (GstQTDemux * qtdemux, GstByteReader * data, int depth); -gboolean qtdemux_dump_stps (GstQTDemux * qtdemux, GstByteReader * data, - int depth); gboolean qtdemux_dump_stsc (GstQTDemux * qtdemux, GstByteReader * data, int depth); gboolean qtdemux_dump_stsz (GstQTDemux * qtdemux, GstByteReader * data, @@ -55,10 +53,6 @@ gboolean qtdemux_dump_stco (GstQTDemux * qtdemux, GstByteReader * data, int depth); gboolean qtdemux_dump_co64 (GstQTDemux * qtdemux, GstByteReader * data, int depth); -gboolean qtdemux_dump_dcom (GstQTDemux * qtdemux, GstByteReader * data, - int depth); -gboolean qtdemux_dump_cmvd (GstQTDemux * qtdemux, GstByteReader * data, - int depth); gboolean qtdemux_dump_ctts (GstQTDemux * qtdemux, GstByteReader * data, int depth); gboolean qtdemux_dump_mfro (GstQTDemux * qtdemux, GstByteReader * data, @@ -75,6 +69,8 @@ gboolean qtdemux_dump_mehd (GstQTDemux * qtdemux, GstByteReader * data, int depth); gboolean qtdemux_dump_sdtp (GstQTDemux * qtdemux, GstByteReader * data, int depth); +gboolean qtdemux_dump_tfdt (GstQTDemux * qtdemux, GstByteReader * data, + int depth); gboolean qtdemux_dump_unknown (GstQTDemux * qtdemux, GstByteReader * data, int depth); diff --git a/gst/isomp4/qtdemux_fourcc.h b/gst/isomp4/qtdemux_fourcc.h old mode 100644 new mode 100755 index 6666a94..bc65dbc --- a/gst/isomp4/qtdemux_fourcc.h +++ b/gst/isomp4/qtdemux_fourcc.h @@ -31,14 +31,10 @@ G_BEGIN_DECLS #define FOURCC_clip GST_MAKE_FOURCC('c','l','i','p') #define FOURCC_trak GST_MAKE_FOURCC('t','r','a','k') #define FOURCC_udta GST_MAKE_FOURCC('u','d','t','a') -#define FOURCC_ctab GST_MAKE_FOURCC('c','t','a','b') #define FOURCC_tkhd GST_MAKE_FOURCC('t','k','h','d') #define FOURCC_crgn GST_MAKE_FOURCC('c','r','g','n') -#define FOURCC_matt GST_MAKE_FOURCC('m','a','t','t') -#define FOURCC_kmat GST_MAKE_FOURCC('k','m','a','t') #define FOURCC_edts GST_MAKE_FOURCC('e','d','t','s') #define FOURCC_elst GST_MAKE_FOURCC('e','l','s','t') -#define FOURCC_load GST_MAKE_FOURCC('l','o','a','d') #define FOURCC_tref GST_MAKE_FOURCC('t','r','e','f') #define FOURCC_imap GST_MAKE_FOURCC('i','m','a','p') #define FOURCC___in GST_MAKE_FOURCC(' ',' ','i','n') @@ -50,15 +46,12 @@ G_BEGIN_DECLS #define FOURCC_minf GST_MAKE_FOURCC('m','i','n','f') #define FOURCC_vmhd GST_MAKE_FOURCC('v','m','h','d') #define FOURCC_smhd GST_MAKE_FOURCC('s','m','h','d') -#define FOURCC_gmhd GST_MAKE_FOURCC('g','m','h','d') -#define FOURCC_gmin GST_MAKE_FOURCC('g','m','i','n') #define FOURCC_dinf GST_MAKE_FOURCC('d','i','n','f') #define FOURCC_dref GST_MAKE_FOURCC('d','r','e','f') #define FOURCC_stbl GST_MAKE_FOURCC('s','t','b','l') #define FOURCC_stsd GST_MAKE_FOURCC('s','t','s','d') #define FOURCC_stts GST_MAKE_FOURCC('s','t','t','s') #define FOURCC_stss GST_MAKE_FOURCC('s','t','s','s') -#define FOURCC_stps GST_MAKE_FOURCC('s','t','p','s') #define FOURCC_stsc GST_MAKE_FOURCC('s','t','s','c') #define FOURCC_stsz GST_MAKE_FOURCC('s','t','s','z') #define FOURCC_stco GST_MAKE_FOURCC('s','t','c','o') @@ -68,9 +61,6 @@ G_BEGIN_DECLS #define FOURCC_strm GST_MAKE_FOURCC('s','t','r','m') #define FOURCC_rtsp GST_MAKE_FOURCC('r','t','s','p') #define FOURCC_co64 GST_MAKE_FOURCC('c','o','6','4') -#define FOURCC_cmov GST_MAKE_FOURCC('c','m','o','v') -#define FOURCC_dcom GST_MAKE_FOURCC('d','c','o','m') -#define FOURCC_cmvd GST_MAKE_FOURCC('c','m','v','d') #define FOURCC_hint GST_MAKE_FOURCC('h','i','n','t') #define FOURCC_mp4a GST_MAKE_FOURCC('m','p','4','a') #define FOURCC_mp4v GST_MAKE_FOURCC('m','p','4','v') @@ -129,13 +119,7 @@ G_BEGIN_DECLS #define FOURCC_free GST_MAKE_FOURCC('f','r','e','e') #define FOURCC_data GST_MAKE_FOURCC('d','a','t','a') #define FOURCC_SVQ3 GST_MAKE_FOURCC('S','V','Q','3') -#define FOURCC_rmra GST_MAKE_FOURCC('r','m','r','a') -#define FOURCC_rmda GST_MAKE_FOURCC('r','m','d','a') -#define FOURCC_rdrf GST_MAKE_FOURCC('r','d','r','f') #define FOURCC__gen GST_MAKE_FOURCC(0xa9, 'g', 'e', 'n') -#define FOURCC_rmdr GST_MAKE_FOURCC('r','m','d','r') -#define FOURCC_rmvc GST_MAKE_FOURCC('r','m','v','c') -#define FOURCC_qtim GST_MAKE_FOURCC('q','t','i','m') #define FOURCC_drms GST_MAKE_FOURCC('d','r','m','s') #define FOURCC_drmi GST_MAKE_FOURCC('d','r','m','i') #define FOURCC_avc1 GST_MAKE_FOURCC('a','v','c','1') @@ -213,7 +197,13 @@ G_BEGIN_DECLS #define FOURCC_XMP_ GST_MAKE_FOURCC('X','M','P','_') #define FOURCC_uuid GST_MAKE_FOURCC('u','u','i','d') - +#define FOURCC_encv GST_MAKE_FOURCC('e','n','c','v') +#define FOURCC_enca GST_MAKE_FOURCC('e','n','c','a') +#define FOURCC_pssh GST_MAKE_FOURCC('p','s','s','h') +#define FOURCC_saiz GST_MAKE_FOURCC('s','a','i','z') +#define FOURCC_saio GST_MAKE_FOURCC('s','a','i','o') +#define FOURCC_senc GST_MAKE_FOURCC('s','e','n','c') +#define FOURCC_mfhd GST_MAKE_FOURCC('m','f','h','d') /* Fragmented MP4 */ #define FOURCC_mehd GST_MAKE_FOURCC('m','e','h','d') #define FOURCC_mfhd GST_MAKE_FOURCC('m','f','h','d') @@ -229,7 +219,12 @@ G_BEGIN_DECLS #define FOURCC_trun GST_MAKE_FOURCC('t','r','u','n') #define FOURCC_ovc1 GST_MAKE_FOURCC('o','v','c','1') #define FOURCC_owma GST_MAKE_FOURCC('o','w','m','a') +#ifdef QTDEMUX_MODIFICATION +#define FOURCC_sbtl GST_MAKE_FOURCC('s','b','t','l') +#endif +/* MPEG DASH */ +#define FOURCC_tfdt GST_MAKE_FOURCC('t','f','d','t') G_END_DECLS #endif /* __GST_QTDEMUX_FOURCC_H__ */ diff --git a/gst/isomp4/qtdemux_types.c b/gst/isomp4/qtdemux_types.c old mode 100644 new mode 100755 index 38da35b..3f0a574 --- a/gst/isomp4/qtdemux_types.c +++ b/gst/isomp4/qtdemux_types.c @@ -28,16 +28,12 @@ static const QtNodeType qt_node_types[] = { {FOURCC_clip, "clipping", QT_FLAG_CONTAINER,}, {FOURCC_trak, "track", QT_FLAG_CONTAINER,}, {FOURCC_udta, "user data", QT_FLAG_CONTAINER,}, /* special container */ - {FOURCC_ctab, "color table", 0,}, {FOURCC_tkhd, "track header", 0, qtdemux_dump_tkhd}, {FOURCC_crgn, "clipping region", 0,}, - {FOURCC_matt, "track matte", QT_FLAG_CONTAINER,}, - {FOURCC_kmat, "compressed matte", 0,}, {FOURCC_edts, "edit", QT_FLAG_CONTAINER,}, {FOURCC_elst, "edit list", 0, qtdemux_dump_elst}, - {FOURCC_load, "track load settings", 0,}, {FOURCC_tref, "track reference", QT_FLAG_CONTAINER,}, {FOURCC_imap, "track input map", QT_FLAG_CONTAINER,}, {FOURCC___in, "track input", 0,}, /* special container */ @@ -51,8 +47,6 @@ static const QtNodeType qt_node_types[] = { {FOURCC_vmhd, "video media information", 0, qtdemux_dump_vmhd}, {FOURCC_smhd, "sound media information", 0}, - {FOURCC_gmhd, "base media information header", 0}, - {FOURCC_gmin, "base media info", 0}, {FOURCC_dinf, "data information", QT_FLAG_CONTAINER}, {FOURCC_dref, "data reference", 0, qtdemux_dump_dref}, @@ -61,8 +55,6 @@ static const QtNodeType qt_node_types[] = { qtdemux_dump_stsd}, {FOURCC_stts, "time-to-sample", 0, qtdemux_dump_stts}, - {FOURCC_stps, "partial sync sample", 0, - qtdemux_dump_stps}, {FOURCC_stss, "sync sample", 0, qtdemux_dump_stss}, {FOURCC_stsc, "sample-to-chunk", 0, @@ -74,9 +66,6 @@ static const QtNodeType qt_node_types[] = { {FOURCC_co64, "64-bit chunk offset", 0, qtdemux_dump_co64}, {FOURCC_vide, "video media", 0}, - {FOURCC_cmov, "compressed movie", QT_FLAG_CONTAINER}, - {FOURCC_dcom, "compressed data", 0, qtdemux_dump_dcom}, - {FOURCC_cmvd, "compressed movie data", 0, qtdemux_dump_cmvd}, {FOURCC_hint, "hint", 0,}, {FOURCC_mp4a, "mp4a", 0,}, {FOURCC_mp4v, "mp4v", 0,}, @@ -141,9 +130,6 @@ static const QtNodeType qt_node_types[] = { {FOURCC_data, "data", 0, qtdemux_dump_unknown}, {FOURCC_free, "free", 0,}, {FOURCC_SVQ3, "SVQ3", 0,}, - {FOURCC_rmra, "rmra", QT_FLAG_CONTAINER,}, - {FOURCC_rmda, "rmda", QT_FLAG_CONTAINER,}, - {FOURCC_rdrf, "rdrf", 0,}, {FOURCC__gen, "Custom Genre", QT_FLAG_CONTAINER,}, {FOURCC_ctts, "Composition time to sample", 0, qtdemux_dump_ctts}, {FOURCC_XiTh, "XiTh", 0}, @@ -171,6 +157,8 @@ static const QtNodeType qt_node_types[] = { qtdemux_dump_mehd}, {FOURCC_ovc1, "ovc1", 0}, {FOURCC_owma, "owma", 0}, + {FOURCC_senc, "senc", 0}, + {FOURCC_tfdt, "Track fragment decode time", 0, qtdemux_dump_tfdt}, {0, "unknown", 0,}, }; diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c old mode 100644 new mode 100755 index 337640f..a81ad3c --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -189,6 +189,7 @@ static GstFlowReturn gst_matroska_demux_trickplay_parse_blockgroup_or_simplebloc 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); @@ -297,6 +298,12 @@ gst_matroska_demux_init (GstMatroskaDemux * demux, 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 */ @@ -508,6 +515,12 @@ gst_matroska_demux_reset (GstElement * element) } 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 * @@ -1318,6 +1331,17 @@ gst_matroska_demux_add_stream (GstMatroskaDemux * demux, GstEbmlRead * ebml) 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) { @@ -2044,17 +2068,28 @@ gst_matroska_demux_handle_seek_event (GstMatroskaDemux * demux, 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)); @@ -2128,7 +2163,8 @@ next: 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; @@ -3510,6 +3546,15 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, 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 @@ -3563,6 +3608,11 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, 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); @@ -3626,7 +3676,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, 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 "). " @@ -3675,9 +3725,15 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, 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); @@ -3771,7 +3827,7 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * 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 @@ -3791,8 +3847,58 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux, 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) { @@ -3824,6 +3930,7 @@ done: return ret; +#ifndef MKV_DEMUX_MODIFICATION /* EXITS */ eos: { @@ -3833,6 +3940,8 @@ 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")); @@ -3962,6 +4071,12 @@ gst_matroska_demux_parse_contents_seekentry (GstMatroskaDemux * demux, 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; } @@ -4318,6 +4433,10 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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; @@ -4356,6 +4475,17 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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)); } @@ -4440,8 +4570,9 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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)); @@ -4471,10 +4602,9 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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"); @@ -4482,7 +4612,7 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, } 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) { @@ -4495,12 +4625,12 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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); @@ -4512,7 +4642,7 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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; } @@ -4536,7 +4666,7 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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; @@ -4576,6 +4706,7 @@ gst_matroska_demux_parse_id (GstMatroskaDemux * demux, guint32 id, 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; @@ -4781,16 +4912,52 @@ gst_matroska_demux_loop (GstPad * pad) 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; } @@ -5252,9 +5419,41 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext * 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 @@ -6319,7 +6518,7 @@ gst_matroska_demux_trickplay_parse_blockgroup_or_simpleblock (GstMatroskaDemux * 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? */ @@ -6402,7 +6601,7 @@ gst_matroska_demux_trickplay_nframes2show_bw_keyframes (GstMatroskaDemux* demux, 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; @@ -6415,7 +6614,7 @@ gst_matroska_demux_trickplay_nframes2show_bw_keyframes (GstMatroskaDemux* demux, } 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)); @@ -6445,12 +6644,12 @@ gst_matroska_demux_trickplay_nframes2show_bw_keyframes (GstMatroskaDemux* demux, 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; } @@ -6462,19 +6661,19 @@ gst_matroska_demux_backward_trickplay (GstMatroskaDemux* demux, GstMatroskaTrack 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, @@ -6543,9 +6742,9 @@ gst_matroska_demux_forward_trickplay (GstMatroskaDemux* demux, GstMatroskaTrackC *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))); @@ -6636,6 +6835,41 @@ gst_matroska_demux_get_property (GObject * object, } } +#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) { diff --git a/gst/matroska/matroska-demux.h b/gst/matroska/matroska-demux.h old mode 100644 new mode 100755 index c59bc90..98e8cf6 --- a/gst/matroska/matroska-demux.h +++ b/gst/matroska/matroska-demux.h @@ -89,6 +89,12 @@ typedef struct _GstMatroskaDemux { 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 */ @@ -113,6 +119,7 @@ typedef struct _GstMatroskaDemux { gboolean is_eos_simpleblock; gint no_video_frame; gboolean video_keyframe_pushed; + GList* Subtitle_language_list; #endif /* gap handling */ diff --git a/gst/matroska/matroska-ids.h b/gst/matroska/matroska-ids.h index c88555c..e0ffddf 100644 --- a/gst/matroska/matroska-ids.h +++ b/gst/matroska/matroska-ids.h @@ -630,6 +630,12 @@ typedef struct _GstMatroskaTrackEncoding { 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); diff --git a/gst/matroska/matroska-read-common.c b/gst/matroska/matroska-read-common.c index 1104acc..1baa4d7 100644 --- a/gst/matroska/matroska-read-common.c +++ b/gst/matroska/matroska-read-common.c @@ -1117,8 +1117,9 @@ gst_matroska_read_common_parse_index (GstMatroskaReadCommon * common, 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); diff --git a/gst/multipart/multipartmux.c b/gst/multipart/multipartmux.c index 9d3e56a..ab02659 100644 --- a/gst/multipart/multipartmux.c +++ b/gst/multipart/multipartmux.c @@ -486,6 +486,12 @@ gst_multipart_mux_collected (GstCollectPads * pads, GstMultipartMux * mux) 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, @@ -495,6 +501,9 @@ gst_multipart_mux_collected (GstCollectPads * pads, GstMultipartMux * mux) 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 */ @@ -592,7 +601,14 @@ alloc_failed: { 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; } } diff --git a/gst/rtp/gstrtpdepay.c b/gst/rtp/gstrtpdepay.c old mode 100644 new mode 100755 index 1566f97..0e05ccd --- a/gst/rtp/gstrtpdepay.c +++ b/gst/rtp/gstrtpdepay.c @@ -158,5 +158,5 @@ gboolean 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); } diff --git a/gst/rtp/gstrtpmp2tpay.c b/gst/rtp/gstrtpmp2tpay.c index 55ef623..1fd9194 100644 --- a/gst/rtp/gstrtpmp2tpay.c +++ b/gst/rtp/gstrtpmp2tpay.c @@ -27,6 +27,13 @@ #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, @@ -45,6 +52,10 @@ GST_STATIC_PAD_TEMPLATE ("src", "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 * @@ -83,6 +94,18 @@ gst_rtp_mp2t_pay_class_init (GstRTPMP2TPayClass * klass) 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 @@ -92,6 +115,7 @@ gst_rtp_mp2t_pay_init (GstRTPMP2TPay * rtpmp2tpay, GstRTPMP2TPayClass * klass) GST_BASE_RTP_PAYLOAD_PT (rtpmp2tpay) = GST_RTP_PAYLOAD_MP2T; rtpmp2tpay->adapter = gst_adapter_new (); + rtpmp2tpay->rtp_flush = DEFAULT_RTP_FLUSH; } static void @@ -107,6 +131,38 @@ gst_rtp_mp2t_pay_finalize (GObject * object) 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) { @@ -159,13 +215,14 @@ gst_rtp_mp2t_pay_handle_buffer (GstBaseRTPPayload * basepayload, 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); @@ -182,7 +239,9 @@ gst_rtp_mp2t_pay_handle_buffer (GstBaseRTPPayload * basepayload, /* 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; @@ -196,6 +255,12 @@ gst_rtp_mp2t_pay_handle_buffer (GstBaseRTPPayload * basepayload, /* 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; } diff --git a/gst/rtp/gstrtpmp2tpay.h b/gst/rtp/gstrtpmp2tpay.h index e7a679b..d51ef09 100644 --- a/gst/rtp/gstrtpmp2tpay.h +++ b/gst/rtp/gstrtpmp2tpay.h @@ -48,6 +48,7 @@ struct _GstRTPMP2TPay GstAdapter *adapter; GstClockTime first_ts; GstClockTime duration; + gboolean rtp_flush; }; struct _GstRTPMP2TPayClass diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index de14225..20fb6e1 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -536,6 +536,10 @@ gst_rtspsrc_init (GstRTSPSrc * src, GstRTSPSrcClass * g_class) 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 (); @@ -4499,6 +4503,11 @@ again: 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) @@ -4507,6 +4516,15 @@ next: 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); @@ -4533,6 +4551,11 @@ next: 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; @@ -4571,7 +4594,11 @@ receive_error: 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 = @@ -6093,8 +6120,15 @@ gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment, gboolean async) 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; diff --git a/gst/rtsp/gstrtspsrc.h b/gst/rtsp/gstrtspsrc.h index cf8f81c..c4f4a73 100644 --- a/gst/rtsp/gstrtspsrc.h +++ b/gst/rtsp/gstrtspsrc.h @@ -227,6 +227,11 @@ struct _GstRTSPSrc { gchar *control; guint next_port_num; +#ifdef GST_EXT_RTSPSRC_MODIFICATION + gboolean wait_pause_response; +#endif + + /* supported methods */ gint methods; diff --git a/gst/udp/gstmultiudpsink.c b/gst/udp/gstmultiudpsink.c old mode 100644 new mode 100755 index b65ab8c..d1d1ad2 --- a/gst/udp/gstmultiudpsink.c +++ b/gst/udp/gstmultiudpsink.c @@ -960,6 +960,7 @@ gst_multiudpsink_init_send (GstMultiUDPSink * sink) } 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; @@ -976,7 +977,7 @@ gst_multiudpsink_init_send (GstMultiUDPSink * sink) 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) */ diff --git a/gst/wavparse/gstwavparse.c b/gst/wavparse/gstwavparse.c index cce76d0..818ebac 100644 --- a/gst/wavparse/gstwavparse.c +++ b/gst/wavparse/gstwavparse.c @@ -757,7 +757,7 @@ gst_wavparse_time_to_bytepos (GstWavParse * wav, gint64 ts, gint64 * bytepos) 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) { @@ -1122,7 +1122,7 @@ gst_wavparse_calculate_duration (GstWavParse * wav) 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); @@ -1268,8 +1268,38 @@ gst_wavparse_stream_headers (GstWavParse * wav) 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) @@ -1285,6 +1315,13 @@ gst_wavparse_stream_headers (GstWavParse * wav) 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); @@ -1678,6 +1715,14 @@ header_read_error: ("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 } /* @@ -1937,7 +1982,7 @@ iterate_adapter: } } - 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 = @@ -2234,7 +2279,11 @@ gst_wavparse_sink_event (GstPad * pad, GstEvent * event) 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) { @@ -2357,7 +2406,7 @@ gst_wavparse_pad_convert (GstPad * pad, 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) { @@ -2393,7 +2442,7 @@ gst_wavparse_pad_convert (GstPad * pad, 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 { diff --git a/packaging/gst-plugins-good.spec b/packaging/gst-plugins-good.spec old mode 100644 new mode 100755 index d5362a6..46ef78d --- a/packaging/gst-plugins-good.spec +++ b/packaging/gst-plugins-good.spec @@ -1,10 +1,10 @@ #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 @@ -16,10 +16,9 @@ Patch6 : gst-plugins-good-disable-gtk-doc.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) @@ -27,6 +26,11 @@ BuildRequires: pkgconfig(x11) 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 @@ -35,7 +39,6 @@ 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. @@ -50,12 +53,17 @@ 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\ @@ -75,7 +83,7 @@ export CFLAGS+=" -Wall -g -fPIC\ --disable-deinterlace\ --disable-effectv\ --disable-equalizer\ - --disable-icydemux\ + --enable-icydemux\ --disable-flx\ --disable-goom\ --disable-goom2k1\ @@ -114,15 +122,20 @@ make %{?jobs:-j%jobs} %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 @@ -130,7 +143,6 @@ rm -rf %{buildroot} %{_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 @@ -140,7 +152,6 @@ rm -rf %{buildroot} %{_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 @@ -149,3 +160,4 @@ rm -rf %{buildroot} %{_libdir}/gstreamer-0.10/libgstshapewipe.so %{_libdir}/gstreamer-0.10/libgstoss4audio.so %{_libdir}/gstreamer-0.10/libgstsouphttpsrc.so +/usr/share/license/%{name} diff --git a/sys/v4l2/gstv4l2bufferpool.c b/sys/v4l2/gstv4l2bufferpool.c index b81c6a4..51cc0ce 100644 --- a/sys/v4l2/gstv4l2bufferpool.c +++ b/sys/v4l2/gstv4l2bufferpool.c @@ -181,7 +181,6 @@ gst_v4l2_buffer_new (GstV4l2BufferPool * pool, guint index, GstCaps * caps) 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, diff --git a/sys/v4l2/v4l2_calls.c b/sys/v4l2/v4l2_calls.c index 309bfb6..ca10bc4 100644 --- a/sys/v4l2/v4l2_calls.c +++ b/sys/v4l2/v4l2_calls.c @@ -294,8 +294,12 @@ gst_v4l2_fill_lists (GstV4l2Object * v4l2object) 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