Line data Source code
1 : /* This Source Code Form is subject to the terms of the Mozilla Public
2 : * License, v. 2.0. If a copy of the MPL was not distributed with this
3 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 :
5 : #include "mozilla/ArrayUtils.h"
6 : #include "mozilla/EndianUtils.h"
7 : #include "mp4_demuxer/AnnexB.h"
8 : #include "mp4_demuxer/ByteReader.h"
9 : #include "mp4_demuxer/ByteWriter.h"
10 : #include "MediaData.h"
11 : #include "nsAutoPtr.h"
12 :
13 : using namespace mozilla;
14 :
15 : namespace mp4_demuxer
16 : {
17 :
18 : static const uint8_t kAnnexBDelimiter[] = { 0, 0, 0, 1 };
19 :
20 : bool
21 0 : AnnexB::ConvertSampleToAnnexB(mozilla::MediaRawData* aSample, bool aAddSPS)
22 : {
23 0 : MOZ_ASSERT(aSample);
24 :
25 0 : if (!IsAVCC(aSample)) {
26 0 : return true;
27 : }
28 0 : MOZ_ASSERT(aSample->Data());
29 :
30 0 : if (!ConvertSampleTo4BytesAVCC(aSample)) {
31 0 : return false;
32 : }
33 :
34 0 : if (aSample->Size() < 4) {
35 : // Nothing to do, it's corrupted anyway.
36 0 : return true;
37 : }
38 :
39 0 : ByteReader reader(aSample->Data(), aSample->Size());
40 :
41 0 : nsTArray<uint8_t> tmp;
42 0 : ByteWriter writer(tmp);
43 :
44 0 : while (reader.Remaining() >= 4) {
45 0 : uint32_t nalLen = reader.ReadU32();
46 0 : const uint8_t* p = reader.Read(nalLen);
47 :
48 0 : if (!writer.Write(kAnnexBDelimiter, ArrayLength(kAnnexBDelimiter))) {
49 0 : return false;
50 : }
51 0 : if (!p) {
52 0 : break;
53 : }
54 0 : if (!writer.Write(p, nalLen)) {
55 0 : return false;
56 : }
57 : }
58 :
59 0 : nsAutoPtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
60 :
61 0 : if (!samplewriter->Replace(tmp.Elements(), tmp.Length())) {
62 0 : return false;
63 : }
64 :
65 : // Prepend the Annex B NAL with SPS and PPS tables to keyframes.
66 0 : if (aAddSPS && aSample->mKeyframe) {
67 : RefPtr<MediaByteBuffer> annexB =
68 0 : ConvertExtraDataToAnnexB(aSample->mExtraData);
69 0 : if (!samplewriter->Prepend(annexB->Elements(), annexB->Length())) {
70 0 : return false;
71 : }
72 :
73 : // Prepending the NAL with SPS/PPS will mess up the encryption subsample
74 : // offsets. So we need to account for the extra bytes by increasing
75 : // the length of the first clear data subsample. Otherwise decryption
76 : // will fail.
77 0 : if (aSample->mCrypto.mValid) {
78 0 : MOZ_ASSERT(samplewriter->mCrypto.mPlainSizes.Length() > 0);
79 0 : samplewriter->mCrypto.mPlainSizes[0] += annexB->Length();
80 : }
81 : }
82 :
83 0 : return true;
84 : }
85 :
86 : already_AddRefed<mozilla::MediaByteBuffer>
87 0 : AnnexB::ConvertExtraDataToAnnexB(const mozilla::MediaByteBuffer* aExtraData)
88 : {
89 : // AVCC 6 byte header looks like:
90 : // +------+------+------+------+------+------+------+------+
91 : // [0] | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
92 : // +------+------+------+------+------+------+------+------+
93 : // [1] | profile |
94 : // +------+------+------+------+------+------+------+------+
95 : // [2] | compatiblity |
96 : // +------+------+------+------+------+------+------+------+
97 : // [3] | level |
98 : // +------+------+------+------+------+------+------+------+
99 : // [4] | unused | nalLenSiz-1 |
100 : // +------+------+------+------+------+------+------+------+
101 : // [5] | unused | numSps |
102 : // +------+------+------+------+------+------+------+------+
103 :
104 0 : RefPtr<mozilla::MediaByteBuffer> annexB = new mozilla::MediaByteBuffer;
105 :
106 0 : ByteReader reader(*aExtraData);
107 0 : const uint8_t* ptr = reader.Read(5);
108 0 : if (ptr && ptr[0] == 1) {
109 : // Append SPS then PPS
110 0 : ConvertSPSOrPPS(reader, reader.ReadU8() & 31, annexB);
111 0 : ConvertSPSOrPPS(reader, reader.ReadU8(), annexB);
112 :
113 : // MP4Box adds extra bytes that we ignore. I don't know what they do.
114 : }
115 :
116 0 : return annexB.forget();
117 : }
118 :
119 : void
120 0 : AnnexB::ConvertSPSOrPPS(ByteReader& aReader, uint8_t aCount,
121 : mozilla::MediaByteBuffer* aAnnexB)
122 : {
123 0 : for (int i = 0; i < aCount; i++) {
124 0 : uint16_t length = aReader.ReadU16();
125 :
126 0 : const uint8_t* ptr = aReader.Read(length);
127 0 : if (!ptr) {
128 0 : MOZ_ASSERT(false);
129 : return;
130 : }
131 0 : aAnnexB->AppendElements(kAnnexBDelimiter, ArrayLength(kAnnexBDelimiter));
132 0 : aAnnexB->AppendElements(ptr, length);
133 : }
134 0 : }
135 :
136 : static bool
137 0 : FindStartCodeInternal(ByteReader& aBr) {
138 0 : size_t offset = aBr.Offset();
139 :
140 0 : for (uint32_t i = 0; i < aBr.Align() && aBr.Remaining() >= 3; i++) {
141 0 : if (aBr.PeekU24() == 0x000001) {
142 0 : return true;
143 : }
144 0 : aBr.Read(1);
145 : }
146 :
147 0 : while (aBr.Remaining() >= 6) {
148 0 : uint32_t x32 = aBr.PeekU32();
149 0 : if ((x32 - 0x01010101) & (~x32) & 0x80808080) {
150 0 : if ((x32 >> 8) == 0x000001) {
151 0 : return true;
152 : }
153 0 : if (x32 == 0x000001) {
154 0 : aBr.Read(1);
155 0 : return true;
156 : }
157 0 : if ((x32 & 0xff) == 0) {
158 0 : const uint8_t* p = aBr.Peek(1);
159 0 : if ((x32 & 0xff00) == 0 && p[4] == 1) {
160 0 : aBr.Read(2);
161 0 : return true;
162 : }
163 0 : if (p[4] == 0 && p[5] == 1) {
164 0 : aBr.Read(3);
165 0 : return true;
166 : }
167 : }
168 : }
169 0 : aBr.Read(4);
170 : }
171 :
172 0 : while (aBr.Remaining() >= 3) {
173 0 : if (aBr.PeekU24() == 0x000001) {
174 0 : return true;
175 : }
176 0 : aBr.Read(1);
177 : }
178 :
179 : // No start code were found; Go back to the beginning.
180 0 : aBr.Seek(offset);
181 0 : return false;
182 : }
183 :
184 : static bool
185 0 : FindStartCode(ByteReader& aBr, size_t& aStartSize)
186 : {
187 0 : if (!FindStartCodeInternal(aBr)) {
188 0 : aStartSize = 0;
189 0 : return false;
190 : }
191 :
192 0 : aStartSize = 3;
193 0 : if (aBr.Offset()) {
194 : // Check if it's 4-bytes start code
195 0 : aBr.Rewind(1);
196 0 : if (aBr.ReadU8() == 0) {
197 0 : aStartSize = 4;
198 : }
199 : }
200 0 : aBr.Read(3);
201 0 : return true;
202 : }
203 :
204 : static bool
205 0 : ParseNALUnits(ByteWriter& aBw, ByteReader& aBr)
206 : {
207 : size_t startSize;
208 :
209 0 : bool rv = FindStartCode(aBr, startSize);
210 0 : if (rv) {
211 0 : size_t startOffset = aBr.Offset();
212 0 : while (FindStartCode(aBr, startSize)) {
213 0 : size_t offset = aBr.Offset();
214 0 : size_t sizeNAL = offset - startOffset - startSize;
215 0 : aBr.Seek(startOffset);
216 0 : if (!aBw.WriteU32(sizeNAL)
217 0 : || !aBw.Write(aBr.Read(sizeNAL), sizeNAL)) {
218 0 : return false;
219 : }
220 0 : aBr.Read(startSize);
221 0 : startOffset = offset;
222 : }
223 : }
224 0 : size_t sizeNAL = aBr.Remaining();
225 0 : if (sizeNAL) {
226 0 : if (!aBw.WriteU32(sizeNAL)
227 0 : || !aBw.Write(aBr.Read(sizeNAL), sizeNAL)) {
228 0 : return false;
229 : }
230 : }
231 0 : return true;
232 : }
233 :
234 : bool
235 0 : AnnexB::ConvertSampleToAVCC(mozilla::MediaRawData* aSample)
236 : {
237 0 : if (IsAVCC(aSample)) {
238 0 : return ConvertSampleTo4BytesAVCC(aSample);
239 : }
240 0 : if (!IsAnnexB(aSample)) {
241 : // Not AnnexB, nothing to convert.
242 0 : return true;
243 : }
244 :
245 0 : nsTArray<uint8_t> nalu;
246 0 : ByteWriter writer(nalu);
247 0 : ByteReader reader(aSample->Data(), aSample->Size());
248 :
249 0 : if (!ParseNALUnits(writer, reader)) {
250 0 : return false;
251 : }
252 0 : nsAutoPtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
253 0 : if (!samplewriter->Replace(nalu.Elements(), nalu.Length())) {
254 0 : return false;
255 : }
256 : // Create the AVCC header.
257 0 : RefPtr<mozilla::MediaByteBuffer> extradata = new mozilla::MediaByteBuffer;
258 : static const uint8_t kFakeExtraData[] = {
259 : 1 /* version */,
260 : 0x64 /* profile (High) */,
261 : 0 /* profile compat (0) */,
262 : 40 /* level (40) */,
263 : 0xfc | 3 /* nal size - 1 */,
264 : 0xe0 /* num SPS (0) */,
265 : 0 /* num PPS (0) */
266 : };
267 0 : if (!extradata->AppendElements(kFakeExtraData, ArrayLength(kFakeExtraData))) {
268 0 : return false;
269 : }
270 0 : aSample->mExtraData = extradata;
271 0 : return true;
272 : }
273 :
274 : bool
275 0 : AnnexB::ConvertSampleTo4BytesAVCC(mozilla::MediaRawData* aSample)
276 : {
277 0 : MOZ_ASSERT(IsAVCC(aSample));
278 :
279 0 : int nalLenSize = ((*aSample->mExtraData)[4] & 3) + 1;
280 :
281 0 : if (nalLenSize == 4) {
282 0 : return true;
283 : }
284 0 : nsTArray<uint8_t> dest;
285 0 : ByteWriter writer(dest);
286 0 : ByteReader reader(aSample->Data(), aSample->Size());
287 0 : while (reader.Remaining() > nalLenSize) {
288 : uint32_t nalLen;
289 0 : switch (nalLenSize) {
290 0 : case 1: nalLen = reader.ReadU8(); break;
291 0 : case 2: nalLen = reader.ReadU16(); break;
292 0 : case 3: nalLen = reader.ReadU24(); break;
293 0 : case 4: nalLen = reader.ReadU32(); break;
294 : }
295 0 : const uint8_t* p = reader.Read(nalLen);
296 0 : if (!p) {
297 0 : return true;
298 : }
299 0 : if (!writer.WriteU32(nalLen)
300 0 : || !writer.Write(p, nalLen)) {
301 0 : return false;
302 : }
303 : }
304 0 : nsAutoPtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
305 0 : return samplewriter->Replace(dest.Elements(), dest.Length());
306 : }
307 :
308 : bool
309 0 : AnnexB::IsAVCC(const mozilla::MediaRawData* aSample)
310 : {
311 0 : return aSample->Size() >= 3 && aSample->mExtraData &&
312 0 : aSample->mExtraData->Length() >= 7 && (*aSample->mExtraData)[0] == 1;
313 : }
314 :
315 : bool
316 0 : AnnexB::IsAnnexB(const mozilla::MediaRawData* aSample)
317 : {
318 0 : if (aSample->Size() < 4) {
319 0 : return false;
320 : }
321 0 : uint32_t header = mozilla::BigEndian::readUint32(aSample->Data());
322 0 : return header == 0x00000001 || (header >> 8) == 0x000001;
323 : }
324 :
325 : } // namespace mp4_demuxer
|