/src/mozilla-central/dom/media/ADTSDemuxer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "ADTSDemuxer.h" |
8 | | |
9 | | #include "TimeUnits.h" |
10 | | #include "VideoUtils.h" |
11 | | #include "mozilla/UniquePtr.h" |
12 | | #include <inttypes.h> |
13 | | |
14 | | extern mozilla::LazyLogModule gMediaDemuxerLog; |
15 | | #define ADTSLOG(msg, ...) \ |
16 | 0 | DDMOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, msg, ##__VA_ARGS__) |
17 | | #define ADTSLOGV(msg, ...) \ |
18 | 0 | DDMOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, msg, ##__VA_ARGS__) |
19 | | |
20 | | namespace mozilla { |
21 | | namespace adts { |
22 | | |
23 | | // adts::FrameHeader - Holds the ADTS frame header and its parsing |
24 | | // state. |
25 | | // |
26 | | // ADTS Frame Structure |
27 | | // |
28 | | // 11111111 1111BCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP(QQQQQQQQ QQQQQQQQ) |
29 | | // |
30 | | // Header consists of 7 or 9 bytes(without or with CRC). |
31 | | // Letter Length(bits) Description |
32 | | // { sync } 12 syncword 0xFFF, all bits must be 1 |
33 | | // B 1 MPEG Version: 0 for MPEG-4, 1 for MPEG-2 |
34 | | // C 2 Layer: always 0 |
35 | | // D 1 protection absent, Warning, set to 1 if there is no |
36 | | // CRC and 0 if there is CRC |
37 | | // E 2 profile, the MPEG-4 Audio Object Type minus 1 |
38 | | // F 4 MPEG-4 Sampling Frequency Index (15 is forbidden) |
39 | | // H 3 MPEG-4 Channel Configuration (in the case of 0, the |
40 | | // channel configuration is sent via an in-band PCE) |
41 | | // M 13 frame length, this value must include 7 or 9 bytes of |
42 | | // header length: FrameLength = |
43 | | // (ProtectionAbsent == 1 ? 7 : 9) + size(AACFrame) |
44 | | // O 11 Buffer fullness |
45 | | // P 2 Number of AAC frames(RDBs) in ADTS frame minus 1, for |
46 | | // maximum compatibility always use 1 AAC frame per ADTS |
47 | | // frame |
48 | | // Q 16 CRC if protection absent is 0 |
49 | | class FrameHeader |
50 | | { |
51 | | public: |
52 | | uint32_t mFrameLength; |
53 | | uint32_t mSampleRate; |
54 | | uint32_t mSamples; |
55 | | uint32_t mChannels; |
56 | | uint8_t mObjectType; |
57 | | uint8_t mSamplingIndex; |
58 | | uint8_t mChannelConfig; |
59 | | uint8_t mNumAACFrames; |
60 | | bool mHaveCrc; |
61 | | |
62 | | // Returns whether aPtr matches a valid ADTS header sync marker |
63 | | static bool MatchesSync(const uint8_t* aPtr) |
64 | 0 | { |
65 | 0 | return aPtr[0] == 0xFF && (aPtr[1] & 0xF6) == 0xF0; |
66 | 0 | } |
67 | | |
68 | 0 | FrameHeader() { Reset(); } |
69 | | |
70 | | // Header size |
71 | 0 | size_t HeaderSize() const { return (mHaveCrc) ? 9 : 7; } |
72 | | |
73 | 0 | bool IsValid() const { return mFrameLength > 0; } |
74 | | |
75 | | // Resets the state to allow for a new parsing session. |
76 | 0 | void Reset() { PodZero(this); } |
77 | | |
78 | | // Returns whether the byte creates a valid sequence up to this point. |
79 | | bool Parse(const uint8_t* aPtr) |
80 | 0 | { |
81 | 0 | const uint8_t* p = aPtr; |
82 | 0 |
|
83 | 0 | if (!MatchesSync(p)) { |
84 | 0 | return false; |
85 | 0 | } |
86 | 0 | |
87 | 0 | // AAC has 1024 samples per frame per channel. |
88 | 0 | mSamples = 1024; |
89 | 0 |
|
90 | 0 | mHaveCrc = !(p[1] & 0x01); |
91 | 0 | mObjectType = ((p[2] & 0xC0) >> 6) + 1; |
92 | 0 | mSamplingIndex = (p[2] & 0x3C) >> 2; |
93 | 0 | mChannelConfig = (p[2] & 0x01) << 2 | (p[3] & 0xC0) >> 6; |
94 | 0 | mFrameLength = |
95 | 0 | (p[3] & 0x03) << 11 | (p[4] & 0xFF) << 3 | (p[5] & 0xE0) >> 5; |
96 | 0 | mNumAACFrames = (p[6] & 0x03) + 1; |
97 | 0 |
|
98 | 0 | static const int32_t SAMPLE_RATES[16] = { |
99 | 0 | 96000, 88200, 64000, 48000, |
100 | 0 | 44100, 32000, 24000, 22050, |
101 | 0 | 16000, 12000, 11025, 8000, |
102 | 0 | 7350 |
103 | 0 | }; |
104 | 0 | mSampleRate = SAMPLE_RATES[mSamplingIndex]; |
105 | 0 |
|
106 | 0 | MOZ_ASSERT(mChannelConfig < 8); |
107 | 0 | mChannels = (mChannelConfig == 7) ? 8 : mChannelConfig; |
108 | 0 |
|
109 | 0 | return true; |
110 | 0 | } |
111 | | }; |
112 | | |
113 | | |
114 | | // adts::Frame - Frame meta container used to parse and hold a frame |
115 | | // header and side info. |
116 | | class Frame |
117 | | { |
118 | | public: |
119 | 0 | Frame() : mOffset(0), mHeader() { } |
120 | | |
121 | 0 | int64_t Offset() const { return mOffset; } |
122 | | size_t Length() const |
123 | 0 | { |
124 | 0 | // TODO: If fields are zero'd when invalid, this check wouldn't be |
125 | 0 | // necessary. |
126 | 0 | if (!mHeader.IsValid()) { |
127 | 0 | return 0; |
128 | 0 | } |
129 | 0 | |
130 | 0 | return mHeader.mFrameLength; |
131 | 0 | } |
132 | | |
133 | | // Returns the offset to the start of frame's raw data. |
134 | 0 | int64_t PayloadOffset() const { return mOffset + mHeader.HeaderSize(); } |
135 | | |
136 | | // Returns the length of the frame's raw data (excluding the header) in bytes. |
137 | | size_t PayloadLength() const |
138 | 0 | { |
139 | 0 | // TODO: If fields are zero'd when invalid, this check wouldn't be |
140 | 0 | // necessary. |
141 | 0 | if (!mHeader.IsValid()) { |
142 | 0 | return 0; |
143 | 0 | } |
144 | 0 | |
145 | 0 | return mHeader.mFrameLength - mHeader.HeaderSize(); |
146 | 0 | } |
147 | | |
148 | | // Returns the parsed frame header. |
149 | 0 | const FrameHeader& Header() const { return mHeader; } |
150 | | |
151 | 0 | bool IsValid() const { return mHeader.IsValid(); } |
152 | | |
153 | | // Resets the frame header and data. |
154 | | void Reset() |
155 | 0 | { |
156 | 0 | mHeader.Reset(); |
157 | 0 | mOffset = 0; |
158 | 0 | } |
159 | | |
160 | | // Returns whether the valid |
161 | | bool Parse(int64_t aOffset, const uint8_t* aStart, const uint8_t* aEnd) |
162 | 0 | { |
163 | 0 | MOZ_ASSERT(aStart && aEnd); |
164 | 0 |
|
165 | 0 | bool found = false; |
166 | 0 | const uint8_t* ptr = aStart; |
167 | 0 | // Require at least 7 bytes of data at the end of the buffer for the minimum |
168 | 0 | // ADTS frame header. |
169 | 0 | while (ptr < aEnd - 7 && !found) { |
170 | 0 | found = mHeader.Parse(ptr); |
171 | 0 | ptr++; |
172 | 0 | } |
173 | 0 |
|
174 | 0 | mOffset = aOffset + (ptr - aStart) - 1; |
175 | 0 |
|
176 | 0 | return found; |
177 | 0 | } |
178 | | |
179 | | private: |
180 | | // The offset to the start of the header. |
181 | | int64_t mOffset; |
182 | | |
183 | | // The currently parsed frame header. |
184 | | FrameHeader mHeader; |
185 | | }; |
186 | | |
187 | | |
188 | | class FrameParser |
189 | | { |
190 | | public: |
191 | | |
192 | | // Returns the currently parsed frame. Reset via Reset or EndFrameSession. |
193 | 0 | const Frame& CurrentFrame() const { return mFrame; } |
194 | | |
195 | | |
196 | | // Returns the first parsed frame. Reset via Reset. |
197 | 0 | const Frame& FirstFrame() const { return mFirstFrame; } |
198 | | |
199 | | // Resets the parser. Don't use between frames as first frame data is reset. |
200 | | void Reset() |
201 | 0 | { |
202 | 0 | EndFrameSession(); |
203 | 0 | mFirstFrame.Reset(); |
204 | 0 | } |
205 | | |
206 | | // Clear the last parsed frame to allow for next frame parsing, i.e.: |
207 | | // - sets PrevFrame to CurrentFrame |
208 | | // - resets the CurrentFrame |
209 | | // - resets ID3Header if no valid header was parsed yet |
210 | | void EndFrameSession() |
211 | 0 | { |
212 | 0 | mFrame.Reset(); |
213 | 0 | } |
214 | | |
215 | | // Parses contents of given ByteReader for a valid frame header and returns |
216 | | // true if one was found. After returning, the variable passed to |
217 | | // 'aBytesToSkip' holds the amount of bytes to be skipped (if any) in order to |
218 | | // jump across a large ID3v2 tag spanning multiple buffers. |
219 | | bool Parse(int64_t aOffset, const uint8_t* aStart, const uint8_t* aEnd) |
220 | 0 | { |
221 | 0 | const bool found = mFrame.Parse(aOffset, aStart, aEnd); |
222 | 0 |
|
223 | 0 | if (mFrame.Length() && !mFirstFrame.Length()) { |
224 | 0 | mFirstFrame = mFrame; |
225 | 0 | } |
226 | 0 |
|
227 | 0 | return found; |
228 | 0 | } |
229 | | |
230 | | private: |
231 | | // We keep the first parsed frame around for static info access, the |
232 | | // previously parsed frame for debugging and the currently parsed frame. |
233 | | Frame mFirstFrame; |
234 | | Frame mFrame; |
235 | | }; |
236 | | |
237 | | // Initialize the AAC AudioSpecificConfig. |
238 | | // Only handles two-byte version for AAC-LC. |
239 | | static void |
240 | | InitAudioSpecificConfig(const Frame& frame, |
241 | | MediaByteBuffer* aBuffer) |
242 | 0 | { |
243 | 0 | const FrameHeader& header = frame.Header(); |
244 | 0 | MOZ_ASSERT(header.IsValid()); |
245 | 0 |
|
246 | 0 | int audioObjectType = header.mObjectType; |
247 | 0 | int samplingFrequencyIndex = header.mSamplingIndex; |
248 | 0 | int channelConfig = header.mChannelConfig; |
249 | 0 |
|
250 | 0 | uint8_t asc[2]; |
251 | 0 | asc[0] = (audioObjectType & 0x1F) << 3 | (samplingFrequencyIndex & 0x0E) >> 1; |
252 | 0 | asc[1] = (samplingFrequencyIndex & 0x01) << 7 | (channelConfig & 0x0F) << 3; |
253 | 0 |
|
254 | 0 | aBuffer->AppendElements(asc, 2); |
255 | 0 | } |
256 | | |
257 | | } // namespace adts |
258 | | |
259 | | using media::TimeUnit; |
260 | | |
261 | | // ADTSDemuxer |
262 | | |
263 | | ADTSDemuxer::ADTSDemuxer(MediaResource* aSource) |
264 | | : mSource(aSource) |
265 | 0 | { |
266 | 0 | DDLINKCHILD("source", aSource); |
267 | 0 | } |
268 | | |
269 | | bool |
270 | | ADTSDemuxer::InitInternal() |
271 | 0 | { |
272 | 0 | if (!mTrackDemuxer) { |
273 | 0 | mTrackDemuxer = new ADTSTrackDemuxer(mSource); |
274 | 0 | DDLINKCHILD("track demuxer", mTrackDemuxer.get()); |
275 | 0 | } |
276 | 0 | return mTrackDemuxer->Init(); |
277 | 0 | } |
278 | | |
279 | | RefPtr<ADTSDemuxer::InitPromise> |
280 | | ADTSDemuxer::Init() |
281 | 0 | { |
282 | 0 | if (!InitInternal()) { |
283 | 0 | ADTSLOG("Init() failure: waiting for data"); |
284 | 0 |
|
285 | 0 | return InitPromise::CreateAndReject( |
286 | 0 | NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__); |
287 | 0 | } |
288 | 0 |
|
289 | 0 | ADTSLOG("Init() successful"); |
290 | 0 | return InitPromise::CreateAndResolve(NS_OK, __func__); |
291 | 0 | } |
292 | | |
293 | | uint32_t |
294 | | ADTSDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const |
295 | 0 | { |
296 | 0 | return (aType == TrackInfo::kAudioTrack) ? 1 : 0; |
297 | 0 | } |
298 | | |
299 | | already_AddRefed<MediaTrackDemuxer> |
300 | | ADTSDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber) |
301 | 0 | { |
302 | 0 | if (!mTrackDemuxer) { |
303 | 0 | return nullptr; |
304 | 0 | } |
305 | 0 | |
306 | 0 | return RefPtr<ADTSTrackDemuxer>(mTrackDemuxer).forget(); |
307 | 0 | } |
308 | | |
309 | | bool |
310 | | ADTSDemuxer::IsSeekable() const |
311 | 0 | { |
312 | 0 | int64_t length = mSource->GetLength(); |
313 | 0 | if (length > -1) |
314 | 0 | return true; |
315 | 0 | return false; |
316 | 0 | } |
317 | | |
318 | | |
319 | | // ADTSTrackDemuxer |
320 | | ADTSTrackDemuxer::ADTSTrackDemuxer(MediaResource* aSource) |
321 | | : mSource(aSource) |
322 | | , mParser(new adts::FrameParser()) |
323 | | , mOffset(0) |
324 | | , mNumParsedFrames(0) |
325 | | , mFrameIndex(0) |
326 | | , mTotalFrameLen(0) |
327 | | , mSamplesPerFrame(0) |
328 | | , mSamplesPerSecond(0) |
329 | | , mChannels(0) |
330 | 0 | { |
331 | 0 | DDLINKCHILD("source", aSource); |
332 | 0 | Reset(); |
333 | 0 | } |
334 | | |
335 | | ADTSTrackDemuxer::~ADTSTrackDemuxer() |
336 | 0 | { |
337 | 0 | delete mParser; |
338 | 0 | } |
339 | | |
340 | | bool |
341 | | ADTSTrackDemuxer::Init() |
342 | 0 | { |
343 | 0 | FastSeek(TimeUnit::Zero()); |
344 | 0 | // Read the first frame to fetch sample rate and other meta data. |
345 | 0 | RefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame(true))); |
346 | 0 |
|
347 | 0 | ADTSLOG("Init StreamLength()=%" PRId64 " first-frame-found=%d", |
348 | 0 | StreamLength(), !!frame); |
349 | 0 |
|
350 | 0 | if (!frame) { |
351 | 0 | return false; |
352 | 0 | } |
353 | 0 | |
354 | 0 | // Rewind back to the stream begin to avoid dropping the first frame. |
355 | 0 | FastSeek(TimeUnit::Zero()); |
356 | 0 |
|
357 | 0 | if (!mInfo) { |
358 | 0 | mInfo = MakeUnique<AudioInfo>(); |
359 | 0 | } |
360 | 0 |
|
361 | 0 | mInfo->mRate = mSamplesPerSecond; |
362 | 0 | mInfo->mChannels = mChannels; |
363 | 0 | mInfo->mBitDepth = 16; |
364 | 0 | mInfo->mDuration = Duration(); |
365 | 0 |
|
366 | 0 | // AAC Specific information |
367 | 0 | mInfo->mMimeType = "audio/mp4a-latm"; |
368 | 0 |
|
369 | 0 | // Configure AAC codec-specific values. |
370 | 0 | // For AAC, mProfile and mExtendedProfile contain the audioObjectType from |
371 | 0 | // Table 1.3 -- Audio Profile definition, ISO/IEC 14496-3. Eg. 2 == AAC LC |
372 | 0 | mInfo->mProfile = mInfo->mExtendedProfile = |
373 | 0 | mParser->FirstFrame().Header().mObjectType; |
374 | 0 | InitAudioSpecificConfig(mParser->FirstFrame(), mInfo->mCodecSpecificConfig); |
375 | 0 |
|
376 | 0 | ADTSLOG("Init mInfo={mRate=%u mChannels=%u mBitDepth=%u mDuration=%" PRId64 |
377 | 0 | "}", |
378 | 0 | mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth, |
379 | 0 | mInfo->mDuration.ToMicroseconds()); |
380 | 0 |
|
381 | 0 | return mSamplesPerSecond && mChannels; |
382 | 0 | } |
383 | | |
384 | | UniquePtr<TrackInfo> |
385 | | ADTSTrackDemuxer::GetInfo() const |
386 | 0 | { |
387 | 0 | return mInfo->Clone(); |
388 | 0 | } |
389 | | |
390 | | RefPtr<ADTSTrackDemuxer::SeekPromise> |
391 | | ADTSTrackDemuxer::Seek(const TimeUnit& aTime) |
392 | 0 | { |
393 | 0 | // Efficiently seek to the position. |
394 | 0 | FastSeek(aTime); |
395 | 0 | // Correct seek position by scanning the next frames. |
396 | 0 | const TimeUnit seekTime = ScanUntil(aTime); |
397 | 0 |
|
398 | 0 | return SeekPromise::CreateAndResolve(seekTime, __func__); |
399 | 0 | } |
400 | | |
401 | | TimeUnit |
402 | | ADTSTrackDemuxer::FastSeek(const TimeUnit& aTime) |
403 | 0 | { |
404 | 0 | ADTSLOG("FastSeek(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64 |
405 | 0 | " mFrameIndex=%" PRId64 " mOffset=%" PRIu64, |
406 | 0 | aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames, |
407 | 0 | mFrameIndex, mOffset); |
408 | 0 |
|
409 | 0 | const int64_t firstFrameOffset = mParser->FirstFrame().Offset(); |
410 | 0 | if (!aTime.ToMicroseconds()) { |
411 | 0 | // Quick seek to the beginning of the stream. |
412 | 0 | mOffset = firstFrameOffset; |
413 | 0 | } else if (AverageFrameLength() > 0) { |
414 | 0 | mOffset = firstFrameOffset + FrameIndexFromTime(aTime) * |
415 | 0 | AverageFrameLength(); |
416 | 0 | } |
417 | 0 |
|
418 | 0 | if (mOffset > firstFrameOffset && StreamLength() > 0) { |
419 | 0 | mOffset = std::min(StreamLength() - 1, mOffset); |
420 | 0 | } |
421 | 0 |
|
422 | 0 | mFrameIndex = FrameIndexFromOffset(mOffset); |
423 | 0 | mParser->EndFrameSession(); |
424 | 0 |
|
425 | 0 | ADTSLOG("FastSeek End avgFrameLen=%f mNumParsedFrames=%" PRIu64 |
426 | 0 | " mFrameIndex=%" PRId64 " mFirstFrameOffset=%" PRIu64 " mOffset=%" PRIu64 |
427 | 0 | " SL=%" PRIu64 "", |
428 | 0 | AverageFrameLength(), mNumParsedFrames, mFrameIndex, |
429 | 0 | firstFrameOffset, mOffset, StreamLength()); |
430 | 0 |
|
431 | 0 | return Duration(mFrameIndex); |
432 | 0 | } |
433 | | |
434 | | TimeUnit |
435 | | ADTSTrackDemuxer::ScanUntil(const TimeUnit& aTime) |
436 | 0 | { |
437 | 0 | ADTSLOG("ScanUntil(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64 |
438 | 0 | " mFrameIndex=%" PRId64 " mOffset=%" PRIu64, |
439 | 0 | aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames, |
440 | 0 | mFrameIndex, mOffset); |
441 | 0 |
|
442 | 0 | if (!aTime.ToMicroseconds()) { |
443 | 0 | return FastSeek(aTime); |
444 | 0 | } |
445 | 0 | |
446 | 0 | if (Duration(mFrameIndex) > aTime) { |
447 | 0 | FastSeek(aTime); |
448 | 0 | } |
449 | 0 |
|
450 | 0 | while (SkipNextFrame(FindNextFrame()) && Duration(mFrameIndex + 1) < aTime) { |
451 | 0 | ADTSLOGV("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64 |
452 | 0 | " mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64, |
453 | 0 | AverageFrameLength(), mNumParsedFrames, mFrameIndex, |
454 | 0 | mOffset, Duration(mFrameIndex + 1).ToMicroseconds()); |
455 | 0 | } |
456 | 0 |
|
457 | 0 | ADTSLOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64 |
458 | 0 | " mFrameIndex=%" PRId64 " mOffset=%" PRIu64, |
459 | 0 | AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset); |
460 | 0 |
|
461 | 0 | return Duration(mFrameIndex); |
462 | 0 | } |
463 | | |
464 | | RefPtr<ADTSTrackDemuxer::SamplesPromise> |
465 | | ADTSTrackDemuxer::GetSamples(int32_t aNumSamples) |
466 | 0 | { |
467 | 0 | ADTSLOGV("GetSamples(%d) Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 |
468 | 0 | " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 |
469 | 0 | " mSamplesPerFrame=%d " |
470 | 0 | "mSamplesPerSecond=%d mChannels=%d", |
471 | 0 | aNumSamples, mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, |
472 | 0 | mSamplesPerFrame, mSamplesPerSecond, mChannels); |
473 | 0 |
|
474 | 0 | MOZ_ASSERT(aNumSamples); |
475 | 0 |
|
476 | 0 | RefPtr<SamplesHolder> frames = new SamplesHolder(); |
477 | 0 |
|
478 | 0 | while (aNumSamples--) { |
479 | 0 | RefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame())); |
480 | 0 | if (!frame) |
481 | 0 | break; |
482 | 0 | |
483 | 0 | frames->mSamples.AppendElement(frame); |
484 | 0 | } |
485 | 0 |
|
486 | 0 | ADTSLOGV("GetSamples() End mSamples.Size()=%zu aNumSamples=%d mOffset=%" PRIu64 |
487 | 0 | " mNumParsedFrames=%" PRIu64 " mFrameIndex=%" PRId64 |
488 | 0 | " mTotalFrameLen=%" PRIu64 |
489 | 0 | " mSamplesPerFrame=%d mSamplesPerSecond=%d " |
490 | 0 | "mChannels=%d", |
491 | 0 | frames->mSamples.Length(), aNumSamples, mOffset, mNumParsedFrames, |
492 | 0 | mFrameIndex, mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, |
493 | 0 | mChannels); |
494 | 0 |
|
495 | 0 | if (frames->mSamples.IsEmpty()) { |
496 | 0 | return SamplesPromise::CreateAndReject( |
497 | 0 | NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__); |
498 | 0 | } |
499 | 0 | |
500 | 0 | return SamplesPromise::CreateAndResolve(frames, __func__); |
501 | 0 | } |
502 | | |
503 | | void |
504 | | ADTSTrackDemuxer::Reset() |
505 | 0 | { |
506 | 0 | ADTSLOG("Reset()"); |
507 | 0 | MOZ_ASSERT(mParser); |
508 | 0 | if (mParser) { |
509 | 0 | mParser->Reset(); |
510 | 0 | } |
511 | 0 | FastSeek(TimeUnit::Zero()); |
512 | 0 | } |
513 | | |
514 | | RefPtr<ADTSTrackDemuxer::SkipAccessPointPromise> |
515 | | ADTSTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) |
516 | 0 | { |
517 | 0 | // Will not be called for audio-only resources. |
518 | 0 | return SkipAccessPointPromise::CreateAndReject( |
519 | 0 | SkipFailureHolder(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 0), __func__); |
520 | 0 | } |
521 | | |
522 | | int64_t |
523 | | ADTSTrackDemuxer::GetResourceOffset() const |
524 | 0 | { |
525 | 0 | return mOffset; |
526 | 0 | } |
527 | | |
528 | | media::TimeIntervals |
529 | | ADTSTrackDemuxer::GetBuffered() |
530 | 0 | { |
531 | 0 | auto duration = Duration(); |
532 | 0 |
|
533 | 0 | if (!duration.IsPositive()) { |
534 | 0 | return media::TimeIntervals(); |
535 | 0 | } |
536 | 0 | |
537 | 0 | AutoPinned<MediaResource> stream(mSource.GetResource()); |
538 | 0 | return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds()); |
539 | 0 | } |
540 | | |
541 | | int64_t |
542 | | ADTSTrackDemuxer::StreamLength() const |
543 | 0 | { |
544 | 0 | return mSource.GetLength(); |
545 | 0 | } |
546 | | |
547 | | TimeUnit |
548 | | ADTSTrackDemuxer::Duration() const |
549 | 0 | { |
550 | 0 | if (!mNumParsedFrames) { |
551 | 0 | return TimeUnit::FromMicroseconds(-1); |
552 | 0 | } |
553 | 0 | |
554 | 0 | const int64_t streamLen = StreamLength(); |
555 | 0 | if (streamLen < 0) { |
556 | 0 | // Unknown length, we can't estimate duration. |
557 | 0 | return TimeUnit::FromMicroseconds(-1); |
558 | 0 | } |
559 | 0 | const int64_t firstFrameOffset = mParser->FirstFrame().Offset(); |
560 | 0 | int64_t numFrames = (streamLen - firstFrameOffset) / AverageFrameLength(); |
561 | 0 | return Duration(numFrames); |
562 | 0 | } |
563 | | |
564 | | TimeUnit |
565 | | ADTSTrackDemuxer::Duration(int64_t aNumFrames) const |
566 | 0 | { |
567 | 0 | if (!mSamplesPerSecond) { |
568 | 0 | return TimeUnit::FromMicroseconds(-1); |
569 | 0 | } |
570 | 0 | |
571 | 0 | return FramesToTimeUnit(aNumFrames * mSamplesPerFrame, mSamplesPerSecond); |
572 | 0 | } |
573 | | |
574 | | const adts::Frame& |
575 | | ADTSTrackDemuxer::FindNextFrame(bool findFirstFrame /*= false*/) |
576 | 0 | { |
577 | 0 | static const int BUFFER_SIZE = 4096; |
578 | 0 | static const int MAX_SKIPPED_BYTES = 10 * BUFFER_SIZE; |
579 | 0 |
|
580 | 0 | ADTSLOGV("FindNext() Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 |
581 | 0 | " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 |
582 | 0 | " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d", |
583 | 0 | mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, |
584 | 0 | mSamplesPerFrame, mSamplesPerSecond, mChannels); |
585 | 0 |
|
586 | 0 | uint8_t buffer[BUFFER_SIZE]; |
587 | 0 | int32_t read = 0; |
588 | 0 |
|
589 | 0 | bool foundFrame = false; |
590 | 0 | int64_t frameHeaderOffset = mOffset; |
591 | 0 |
|
592 | 0 | // Prepare the parser for the next frame parsing session. |
593 | 0 | mParser->EndFrameSession(); |
594 | 0 |
|
595 | 0 | // Check whether we've found a valid ADTS frame. |
596 | 0 | while (!foundFrame) { |
597 | 0 | if ((read = Read(buffer, frameHeaderOffset, BUFFER_SIZE)) == 0) { |
598 | 0 | ADTSLOG("FindNext() EOS without a frame"); |
599 | 0 | break; |
600 | 0 | } |
601 | 0 |
|
602 | 0 | if (frameHeaderOffset - mOffset > MAX_SKIPPED_BYTES) { |
603 | 0 | ADTSLOG("FindNext() exceeded MAX_SKIPPED_BYTES without a frame"); |
604 | 0 | break; |
605 | 0 | } |
606 | 0 |
|
607 | 0 | const adts::Frame& currentFrame = mParser->CurrentFrame(); |
608 | 0 | foundFrame = mParser->Parse(frameHeaderOffset, buffer, buffer + read); |
609 | 0 | if (findFirstFrame && foundFrame) { |
610 | 0 | // Check for sync marker after the found frame, since it's |
611 | 0 | // possible to find sync marker in AAC data. If sync marker |
612 | 0 | // exists after the current frame then we've found a frame |
613 | 0 | // header. |
614 | 0 | int64_t nextFrameHeaderOffset = |
615 | 0 | currentFrame.Offset() + currentFrame.Length(); |
616 | 0 | int32_t read = Read(buffer, nextFrameHeaderOffset, 2); |
617 | 0 | if (read != 2 || !adts::FrameHeader::MatchesSync(buffer)) { |
618 | 0 | frameHeaderOffset = currentFrame.Offset() + 1; |
619 | 0 | mParser->Reset(); |
620 | 0 | foundFrame = false; |
621 | 0 | continue; |
622 | 0 | } |
623 | 0 | } |
624 | 0 | |
625 | 0 | if (foundFrame) { |
626 | 0 | break; |
627 | 0 | } |
628 | 0 | |
629 | 0 | // Minimum header size is 7 bytes. |
630 | 0 | int64_t advance = read - 7; |
631 | 0 |
|
632 | 0 | // Check for offset overflow. |
633 | 0 | if (frameHeaderOffset + advance <= frameHeaderOffset) { |
634 | 0 | break; |
635 | 0 | } |
636 | 0 | |
637 | 0 | frameHeaderOffset += advance; |
638 | 0 | } |
639 | 0 |
|
640 | 0 | if (!foundFrame || !mParser->CurrentFrame().Length()) { |
641 | 0 | ADTSLOG("FindNext() Exit foundFrame=%d mParser->CurrentFrame().Length()=%zu ", |
642 | 0 | foundFrame, mParser->CurrentFrame().Length()); |
643 | 0 | mParser->Reset(); |
644 | 0 | return mParser->CurrentFrame(); |
645 | 0 | } |
646 | 0 |
|
647 | 0 | ADTSLOGV("FindNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 |
648 | 0 | " mFrameIndex=%" PRId64 " frameHeaderOffset=%" PRId64 |
649 | 0 | " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d mSamplesPerSecond=%d" |
650 | 0 | " mChannels=%d", |
651 | 0 | mOffset, mNumParsedFrames, mFrameIndex, frameHeaderOffset, |
652 | 0 | mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, mChannels); |
653 | 0 |
|
654 | 0 | return mParser->CurrentFrame(); |
655 | 0 | } |
656 | | |
657 | | bool |
658 | | ADTSTrackDemuxer::SkipNextFrame(const adts::Frame& aFrame) |
659 | 0 | { |
660 | 0 | if (!mNumParsedFrames || !aFrame.Length()) { |
661 | 0 | RefPtr<MediaRawData> frame(GetNextFrame(aFrame)); |
662 | 0 | return frame; |
663 | 0 | } |
664 | 0 | |
665 | 0 | UpdateState(aFrame); |
666 | 0 |
|
667 | 0 | ADTSLOGV("SkipNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 |
668 | 0 | " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 |
669 | 0 | " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d", |
670 | 0 | mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, |
671 | 0 | mSamplesPerFrame, mSamplesPerSecond, mChannels); |
672 | 0 |
|
673 | 0 | return true; |
674 | 0 | } |
675 | | |
676 | | already_AddRefed<MediaRawData> |
677 | | ADTSTrackDemuxer::GetNextFrame(const adts::Frame& aFrame) |
678 | 0 | { |
679 | 0 | ADTSLOG("GetNext() Begin({mOffset=%" PRId64 " HeaderSize()=%zu" |
680 | 0 | " Length()=%zu})", |
681 | 0 | aFrame.Offset(), aFrame.Header().HeaderSize(), aFrame.PayloadLength()); |
682 | 0 | if (!aFrame.IsValid()) |
683 | 0 | return nullptr; |
684 | 0 | |
685 | 0 | const int64_t offset = aFrame.PayloadOffset(); |
686 | 0 | const uint32_t length = aFrame.PayloadLength(); |
687 | 0 |
|
688 | 0 | RefPtr<MediaRawData> frame = new MediaRawData(); |
689 | 0 | frame->mOffset = offset; |
690 | 0 |
|
691 | 0 | UniquePtr<MediaRawDataWriter> frameWriter(frame->CreateWriter()); |
692 | 0 | if (!frameWriter->SetSize(length)) { |
693 | 0 | ADTSLOG("GetNext() Exit failed to allocated media buffer"); |
694 | 0 | return nullptr; |
695 | 0 | } |
696 | 0 |
|
697 | 0 | const uint32_t read = Read(frameWriter->Data(), offset, length); |
698 | 0 | if (read != length) { |
699 | 0 | ADTSLOG("GetNext() Exit read=%u frame->Size()=%zu", read, frame->Size()); |
700 | 0 | return nullptr; |
701 | 0 | } |
702 | 0 |
|
703 | 0 | UpdateState(aFrame); |
704 | 0 |
|
705 | 0 | frame->mTime = Duration(mFrameIndex - 1); |
706 | 0 | frame->mDuration = Duration(1); |
707 | 0 | frame->mTimecode = frame->mTime; |
708 | 0 | frame->mKeyframe = true; |
709 | 0 |
|
710 | 0 | MOZ_ASSERT(!frame->mTime.IsNegative()); |
711 | 0 | MOZ_ASSERT(frame->mDuration.IsPositive()); |
712 | 0 |
|
713 | 0 | ADTSLOGV("GetNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64 |
714 | 0 | " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 |
715 | 0 | " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d", |
716 | 0 | mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, |
717 | 0 | mSamplesPerFrame, mSamplesPerSecond, mChannels); |
718 | 0 |
|
719 | 0 | return frame.forget(); |
720 | 0 | } |
721 | | |
722 | | int64_t |
723 | | ADTSTrackDemuxer::FrameIndexFromOffset(int64_t aOffset) const |
724 | 0 | { |
725 | 0 | int64_t frameIndex = 0; |
726 | 0 |
|
727 | 0 | if (AverageFrameLength() > 0) { |
728 | 0 | frameIndex = |
729 | 0 | (aOffset - mParser->FirstFrame().Offset()) / AverageFrameLength(); |
730 | 0 | } |
731 | 0 |
|
732 | 0 | ADTSLOGV("FrameIndexFromOffset(%" PRId64 ") -> %" PRId64, aOffset, frameIndex); |
733 | 0 | return std::max<int64_t>(0, frameIndex); |
734 | 0 | } |
735 | | |
736 | | int64_t |
737 | | ADTSTrackDemuxer::FrameIndexFromTime(const TimeUnit& aTime) const |
738 | 0 | { |
739 | 0 | int64_t frameIndex = 0; |
740 | 0 | if (mSamplesPerSecond > 0 && mSamplesPerFrame > 0) { |
741 | 0 | frameIndex = aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerFrame - 1; |
742 | 0 | } |
743 | 0 |
|
744 | 0 | ADTSLOGV("FrameIndexFromOffset(%fs) -> %" PRId64, |
745 | 0 | aTime.ToSeconds(), frameIndex); |
746 | 0 | return std::max<int64_t>(0, frameIndex); |
747 | 0 | } |
748 | | |
749 | | void |
750 | | ADTSTrackDemuxer::UpdateState(const adts::Frame& aFrame) |
751 | 0 | { |
752 | 0 | int32_t frameLength = aFrame.Length(); |
753 | 0 | // Prevent overflow. |
754 | 0 | if (mTotalFrameLen + frameLength < mTotalFrameLen) { |
755 | 0 | // These variables have a linear dependency and are only used to derive the |
756 | 0 | // average frame length. |
757 | 0 | mTotalFrameLen /= 2; |
758 | 0 | mNumParsedFrames /= 2; |
759 | 0 | } |
760 | 0 |
|
761 | 0 | // Full frame parsed, move offset to its end. |
762 | 0 | mOffset = aFrame.Offset() + frameLength; |
763 | 0 | mTotalFrameLen += frameLength; |
764 | 0 |
|
765 | 0 | if (!mSamplesPerFrame) { |
766 | 0 | const adts::FrameHeader& header = aFrame.Header(); |
767 | 0 | mSamplesPerFrame = header.mSamples; |
768 | 0 | mSamplesPerSecond = header.mSampleRate; |
769 | 0 | mChannels = header.mChannels; |
770 | 0 | } |
771 | 0 |
|
772 | 0 | ++mNumParsedFrames; |
773 | 0 | ++mFrameIndex; |
774 | 0 | MOZ_ASSERT(mFrameIndex > 0); |
775 | 0 | } |
776 | | |
777 | | int32_t |
778 | | ADTSTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset, int32_t aSize) |
779 | 0 | { |
780 | 0 | ADTSLOGV("ADTSTrackDemuxer::Read(%p %" PRId64 " %d)", |
781 | 0 | aBuffer, aOffset, aSize); |
782 | 0 |
|
783 | 0 | const int64_t streamLen = StreamLength(); |
784 | 0 | if (mInfo && streamLen > 0) { |
785 | 0 | // Prevent blocking reads after successful initialization. |
786 | 0 | aSize = std::min<int64_t>(aSize, streamLen - aOffset); |
787 | 0 | } |
788 | 0 |
|
789 | 0 | uint32_t read = 0; |
790 | 0 | ADTSLOGV("ADTSTrackDemuxer::Read -> ReadAt(%d)", aSize); |
791 | 0 | const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast<char*>(aBuffer), |
792 | 0 | static_cast<uint32_t>(aSize), &read); |
793 | 0 | NS_ENSURE_SUCCESS(rv, 0); |
794 | 0 | return static_cast<int32_t>(read); |
795 | 0 | } |
796 | | |
797 | | double |
798 | | ADTSTrackDemuxer::AverageFrameLength() const |
799 | 0 | { |
800 | 0 | if (mNumParsedFrames) { |
801 | 0 | return static_cast<double>(mTotalFrameLen) / mNumParsedFrames; |
802 | 0 | } |
803 | 0 | |
804 | 0 | return 0.0; |
805 | 0 | } |
806 | | |
807 | | /* static */ bool |
808 | | ADTSDemuxer::ADTSSniffer(const uint8_t* aData, const uint32_t aLength) |
809 | 0 | { |
810 | 0 | if (aLength < 7) { |
811 | 0 | return false; |
812 | 0 | } |
813 | 0 | if (!adts::FrameHeader::MatchesSync(aData)) { |
814 | 0 | return false; |
815 | 0 | } |
816 | 0 | auto parser = MakeUnique<adts::FrameParser>(); |
817 | 0 |
|
818 | 0 | if (!parser->Parse(0, aData, aData + aLength)) { |
819 | 0 | return false; |
820 | 0 | } |
821 | 0 | const adts::Frame& currentFrame = parser->CurrentFrame(); |
822 | 0 | // Check for sync marker after the found frame, since it's |
823 | 0 | // possible to find sync marker in AAC data. If sync marker |
824 | 0 | // exists after the current frame then we've found a frame |
825 | 0 | // header. |
826 | 0 | int64_t nextFrameHeaderOffset = currentFrame.Offset() + currentFrame.Length(); |
827 | 0 | return int64_t(aLength) > nextFrameHeaderOffset && |
828 | 0 | aLength - nextFrameHeaderOffset >= 2 && |
829 | 0 | adts::FrameHeader::MatchesSync(aData + nextFrameHeaderOffset); |
830 | 0 | } |
831 | | |
832 | | } // namespace mozilla |