From 09c58004dc9c86771e7891fa6a16568531e1053c Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Thu, 25 Aug 2016 08:57:50 -0700 Subject: [PATCH] Add an AC3 bitstream extractor Defined in ATSC Standard: Digital Audio Compression (AC-3, E-AC-3). Link: http://atsc.org/wp-content/uploads/2015/03/A52-201212-17.pdf. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=131294260 --- library/src/androidTest/assets/ts/sample.ac3 | Bin 0 -> 13354 bytes .../androidTest/assets/ts/sample.ac3.0.dump | 61 +++++++ .../extractor/ts/Ac3ExtractorTest.java | 36 ++++ .../extractor/DefaultExtractorsFactory.java | 7 + .../exoplayer2/extractor/ts/Ac3Extractor.java | 156 ++++++++++++++++++ .../exoplayer2/extractor/ts/Ac3Reader.java | 1 + .../extractor/ts/AdtsExtractor.java | 18 +- 7 files changed, 270 insertions(+), 9 deletions(-) create mode 100644 library/src/androidTest/assets/ts/sample.ac3 create mode 100644 library/src/androidTest/assets/ts/sample.ac3.0.dump create mode 100644 library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/Ac3ExtractorTest.java create mode 100644 library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java diff --git a/library/src/androidTest/assets/ts/sample.ac3 b/library/src/androidTest/assets/ts/sample.ac3 new file mode 100644 index 0000000000000000000000000000000000000000..0e39f0ec988606dbc31198b6c12ec124eaf88143 GIT binary patch literal 13354 zcmeI2c|6qH|HscPj5Q-dSw=lx1`8`1qf9YiBWhwo`@hw1Q8ZnEEX$Afh8q}+117?O(y^F z)!g+=@;|;tH}~!M;hupNec_+*__5Xj5&YUtdesmSd7ZsIGbOc5+<*tnNh1vwB#c{t z_IqYY28Wix@zb*oYU(eowT1C|mK?w28JB$Bo~H{xe7*^0^p_wsSeuqvdh~f3jL?3E z)<(;W*R$@AV0;$>qgn;egb$gM{k7zvLHjZ|yb@nG7yt|I-q&^wR*yE9DS32+w=)F| z$$-`F@9$SL>||>#Eu*VV)GOFO1U^d~EP5L3K@e;wv+I&C1prJg+N}%@+RQ;h;A2&^ z(I~Vp{r$>@owN?(&~lm2(^)VA7HdIGgV%$}CESM^n>0n?8g*<|T(Q2&MfYHnFj>(F(AzAeJZq=aX+je^$Pz&_l%eqWH+uNirpLrw{$XI|Ch|F2wMZp~hPHbE;Pz9hHG=M3f z3`_YH`Y`>Fv!c5d#D)|d*WOg?=J zCgx2p23e~qlTTeeN(SRHi(Y*@HatelGe&EdEHJqYCIReeV2t2K038VHVStVd23VPYXuF(M8K5Z> ze!wbVsfmdSLPekFsxAxOK0G`^XO{olhp{TgauF;pz_YkG zhh>>#*<2v|H_H-791Z9bv4|P$hD^yrr|AoJ`6C+$lqK*b!gbEv5GFG?I7Fv{5YD2i z!)ugTZI4fXcp6USElknB{o8%weG^3-axMToxA~;L%z+3BS){4>hW29}eFvi&C^%h& znWmOnM{D(}|%#gTgEjf6~Ikc)YK;+TBvxFC4zSbXj3?79#S zd^-V5`-k9^WZrV;QIuYXU1+(+2OC$q^^p$wcGGR6r!TkLyibiysGhC%GDo{1V)ab> zUt@0MlH%m?=`*i(AARalZsF8Dtf&#go%w&P>qLEJ;|u&9-&iwcJrCX*q z&0Aml$=H;PfWs*|u z{G+I0GWk7Z9OtO{;hJT3%bcb!31pO&|6}~v&ZT^rGor^&*?YG23X@ZF>(aebM>9D5 zdN~d)L^th=LW5~eS#73rPm5l8vr14tf0nNN$MuA!vK*2FH&l_E8_Nz3u${)7Y_wUI z1#(Fb?i^nE!1l;r+);-Xo`G+|;%PYjaM7q478f>;v7krzrZtZNUnVLtj*Ue5FE8YO zn@2?=%fahRc^VNP8m_oUY1tE1^WXJlu{Qj6uVO-RIGKXeLzu7Cco9IW9ruo#JnH}` zyh^f;>N>F9a4ynSJh3?Php5`G~7sMSonr3E-N-|Q^UD}PMMcCY*H&eBV@2J?zM z#CNuG0pJ3nYzMnp4v=Y&HqRjC3#vmuY@GQVEUikFP}tQcyvP80)&S_hm2hs_I8{I> z$=TU*g!`e^Et!i5BLE;J^Bco0HzM)XU--gnbAJ;0 z+uW19Ca@UkLGeqvS9a9;f2#UBW||=20^xi!EpM^%^rN@u`m2@WGqG_X zvB9ail#sKZbz4W4ne-j9IOUVdu%X-=MXNnWC2$+eFDDr4P?y;&a-L^A3Gdu==a1!A!knr1)Vyzd6LHo5Oe3Gu8MAhw9U*&p zMq$`I_4=0Ot|?}!Hv@vFW;j0XFf}Moc*Dxw$RZX+bcQ!2O>ah4(B)K4pHBHhN1GQq zD}C36jXM|*ZODzAJeKClt*c$ELkZ<6X;(ICB^%|-A2GNAeo66swV?Ur;SX`%<2M-` zKYDEb`aP=S9$t1uY~FNkc(Q5Ui5r!sJzuWp9`7L@NnQ59EN|y$_cz<)K1?B=K%y#_ z9khJ1u03+PCT(T}rK@BC$BFO&VDUIdIQB*@tlj5EPntqJL(vBSJfv6t_M|H=HT`{h z!qWt0t(sQcQTt#1*ymG5D>!((c*P#QoTa^Lb`;I5Jig}HIdV#>kLFhFu)4#ThSY)izLh^Q>wzeO{&-Y)P4_Yv7=u>$lN=8tElF zb=E?bMw`ar60dbvoc;=A#rp76cI!MHck;NeV`S*4gAHyu3qC*HGT~wcl8eW6_Ktl? zp(dE0nY|+=KwI~?^9oL=iH_OTHAP$Pf;6+PM|?59xpjSdl%lhxHNEnbGX9L4g?3Zg zs@Bz&&Nhc62M9d%@BV(Qlf^wBs8u^qXfHh}*09=$&PK2zgP7xA@2+`Tq?XBO#>ITH4E zkJBcDCwX;u_N}DvpW+tGx!i>Rm7{ZEw%OrOjZ(!C@igl_!SatUT2C1@)oRm*hBcmL z?zFHu@oSO$GnMzD^S1*mhDDwKvIJg z8E(%8@V}K`k@(iZ;H)(s&^^>fCXih8i9`i;b#;xD|C|BTmiM4}z>E38ZOUo>nElPm z|1JVLP!D>>V)1fB{RLoV3)L@~+;2z-jS_)Nf|?x_{nn4b1XBHCVBW?nLH{A%Y6-j_ zcnMlWPk0V@B~|slzp5*r9;7L}{ZRO06}pz_E)VVv(?>p7ergLds|&q{Bl3sP?VrF6 zf(=1UrkU)n!ootwYh%Zb9x-OjXbl!ZrMl`H7#IN{+oz-~sg=;q5UBMl&f}3Zl@7b3nRv`$156QGIG49m7U7Bo(K3;4d2~8mw z1!EA{>Q0iCm6=wbiLsFZn@Yt6$iW#dDq2Pc!A)XocbdWd(-0MWHRxbfyF-auU2Osj z&JWs7Lf=bhtkE*yXm0X4T_CBdK(uveTiS`GkHY8lN~mJLAOyaZN)Me`6jQ%kp=AaI zpexvPqnd!$n()oRF@o;gT^SktO8}Vu2M-=pErZ^{Wayzn?*L~bD=Vp$&_;y!!$*j9 z_n1(CBatOM3I+=FIr-D-H4<7Ys;WwF1e2J%&DBY&D?H9eO1L zzF!JzIjJPs+h?WXa2VuC&6Crc@pvo_I>NSaM-+r-8FF)zZhKwX<`wa4_JQfcPb-xk z{h`w;d4+?dv=UgT74S`UaAPX~6DL8aBF*O(jNHXlp-R&FcWD(%AjrN~RaI5i($dmo zQK=MHb#*N*fEk6*49RiDYCcAf?5PF~s+C8Qo*qkH{;TtnegAW9j=jUqh%H6bzroIv zJ9k`;9RBc@@udu}(I?lThxf2ds0Y7Hl0y2$=-$fCE97?)qz*5*ZLY}h3qkp>EL{J8 zBvmA`;eNe6De2I`_!0Y+%OL;9$SEi&rTpjk&*y!$ng)ShS&KU~Y>L z-sow8$OK2B)_(wy{ETu#K%YSm05aXknGZ!FbNYQhH0!?F|L%W|U0mHJNIdZj0OJOP zpeF>%Ej9^MXh$t_b#+~V`cgj7E>{*9SpX9jIea71E{_l+oB>fj_cv&uZ-2J3=&q2z z1Lyz)2Uz>r2@JA_C1iyFApI`n8`Oq`3_)$kH%Sj|Oa+7)Fnz+n0U>3uTYlkfIBZ13 zlY)YR2hdBCf#3l~=-CqhihWwjQr?RgoI0IW=AvLBq2a>>kcKJW_AdR>gpMV~!pV;& zg7|kKT>1Quq7aFvC{1_Qb&1bTB!(aYbk-$7qKrv|rOeFCY&Pr_1|2lmJ&zWa-_Y+x z+^aHPetx_tSUA1GPgH?l&#N{~yuNH>2}57IXj=gc8K&MGrVslK5i8({@bX2qp1b6txIyr~d?MLGM8YDJM`t zHRe6y5J@`D8EX|P7O6z3vtSRZa9xh*lK$M?yLZdc89N@9GJyv1WX#2lqcLqor^n3n zTjlMVp+T9#&s8~*GR4$V{~z-p|N93x0uJo5*4EZ;bb$XY{GZb4GJO8~)|W})|DHF*R1w@I z=1pcI$yA(LbNXdE0PgOEs_}0{=o<8d81g@nN~hCztzElz-8^{YVAR>!*$clh5G@`B z;o?+~cwQ@BW7^p`O5?vteYBLk$tml~6^B5H3d;bvS5o4VhspoQ$jB!(H8l-zLX)2l zH#+t34>(w|rZ7b)M0bSVBO4$6Pf@owqgvexe}d2V3V^70z8 z0{&7v*u3{ysrgOShkD25`-sEmzecyQ6piWySxeLCd)dcQG};#VZde)xcH0yg7Qrb0 zb;bU_jEM)nv;NiNN9Gik~7A(|s4xjL|o!%8ld zq`~lS1HJO0-^&2}$5-o1!GHE+x-|ULO`4_P{~M7Gf&Vaj$#{TUP=4g_`5zS~FNOaO zxqGGYKll_QL<;}E5$O>89~A!-4DIip%W1cCDmP49`O#z7-UZR^N#q+is#P)O_Myuz zCk4lE!o?J{qF`E%B4cYE%71eq|J%d< z?fgg27DY&xxLX#V#`)N_AX50hs{X2%2<{T|CNozpObY*BD`pRi|KvgOUzI<6`^T0P zg&jI=PG51bBZ=59F>P|eak3Qr*RQJ@9{$GQi{{= MAX_SNIFF_BYTES) { + return false; + } + input.advancePeekPosition(headerPosition); + } else { + if (++validFramesCount >= 4) { + return true; + } + int frameSize = Ac3Util.parseAc3SyncframeSize(scratch.data); + input.advancePeekPosition(frameSize - 5); + } + } + } + + @Override + public void init(ExtractorOutput output) { + reader = new Ac3Reader(output.track(0), false); // TODO: Add support for embedded ID3. + output.endTracks(); + output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET)); + } + + @Override + public void seek(long position) { + startedPacket = false; + reader.seek(); + } + + @Override + public void release() { + // Do nothing. + } + + @Override + public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException, + InterruptedException { + int bytesRead = input.read(sampleData.data, 0, MAX_SYNC_FRAME_SIZE); + if (bytesRead == C.RESULT_END_OF_INPUT) { + return RESULT_END_OF_INPUT; + } + + // Feed whatever data we have to the reader, regardless of whether the read finished or not. + sampleData.setPosition(0); + sampleData.setLimit(bytesRead); + + if (!startedPacket) { + // Pass data to the reader as though it's contained within a single infinitely long packet. + reader.packetStarted(firstSampleTimestampUs, true); + startedPacket = true; + } + // TODO: Make it possible for the reader to consume the dataSource directly, so that it becomes + // unnecessary to copy the data through packetBuffer. + reader.consume(sampleData); + return RESULT_CONTINUE; + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java index 09d2d1ec61..b64c56ab07 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java @@ -51,6 +51,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; // Used when reading the samples. private long timeUs; + // TODO: Remove the isEac3 parameter by reading the BSID field. /** * Constructs a new reader for (E-)AC-3 elementary streams. * diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java index 7556623021..f131d8997b 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java @@ -57,7 +57,7 @@ public final class AdtsExtractor implements Extractor { private final ParsableByteArray packetBuffer; // Accessed only by the loading thread. - private AdtsReader adtsReader; + private AdtsReader reader; private boolean startedPacket; public AdtsExtractor() { @@ -81,8 +81,8 @@ public final class AdtsExtractor implements Extractor { if (scratch.readUnsignedInt24() != ID3_TAG) { break; } - int length = (scratch.data[6] & 0x7F) << 21 | ((scratch.data[7] & 0x7F) << 14) - | ((scratch.data[8] & 0x7F) << 7) | (scratch.data[9] & 0x7F); + scratch.skipBytes(3); + int length = scratch.readSynchSafeInt(); startPosition += 10 + length; input.advancePeekPosition(length); } @@ -126,7 +126,7 @@ public final class AdtsExtractor implements Extractor { @Override public void init(ExtractorOutput output) { - adtsReader = new AdtsReader(output.track(0), output.track(1)); + reader = new AdtsReader(output.track(0), output.track(1)); output.endTracks(); output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET)); } @@ -134,7 +134,7 @@ public final class AdtsExtractor implements Extractor { @Override public void seek(long position) { startedPacket = false; - adtsReader.seek(); + reader.seek(); } @Override @@ -154,14 +154,14 @@ public final class AdtsExtractor implements Extractor { packetBuffer.setPosition(0); packetBuffer.setLimit(bytesRead); - // TODO: Make it possible for adtsReader to consume the dataSource directly, so that it becomes - // unnecessary to copy the data through packetBuffer. if (!startedPacket) { // Pass data to the reader as though it's contained within a single infinitely long packet. - adtsReader.packetStarted(firstSampleTimestampUs, true); + reader.packetStarted(firstSampleTimestampUs, true); startedPacket = true; } - adtsReader.consume(packetBuffer); + // TODO: Make it possible for reader to consume the dataSource directly, so that it becomes + // unnecessary to copy the data through packetBuffer. + reader.consume(packetBuffer); return RESULT_CONTINUE; }