Bug 1852754: part 9) Add tests for dynamically loading <link rel="prefetch"> elements...
[gecko.git] / dom / media / ADTSDemuxer.cpp
blobeb78014b737069bb4c303c7277673f010abc91ed
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/. */
7 #include "ADTSDemuxer.h"
9 #include "TimeUnits.h"
10 #include "VideoUtils.h"
11 #include "mozilla/Logging.h"
12 #include "mozilla/UniquePtr.h"
13 #include <inttypes.h>
15 extern mozilla::LazyLogModule gMediaDemuxerLog;
16 #define LOG(msg, ...) \
17 MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, msg, ##__VA_ARGS__)
18 #define ADTSLOG(msg, ...) \
19 DDMOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, msg, ##__VA_ARGS__)
20 #define ADTSLOGV(msg, ...) \
21 DDMOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, msg, ##__VA_ARGS__)
23 namespace mozilla {
24 namespace adts {
26 // adts::FrameHeader - Holds the ADTS frame header and its parsing
27 // state.
29 // ADTS Frame Structure
31 // 11111111 1111BCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP(QQQQQQQQ
32 // QQQQQQQQ)
34 // Header consists of 7 or 9 bytes(without or with CRC).
35 // Letter Length(bits) Description
36 // { sync } 12 syncword 0xFFF, all bits must be 1
37 // B 1 MPEG Version: 0 for MPEG-4, 1 for MPEG-2
38 // C 2 Layer: always 0
39 // D 1 protection absent, Warning, set to 1 if there is no
40 // CRC and 0 if there is CRC
41 // E 2 profile, the MPEG-4 Audio Object Type minus 1
42 // F 4 MPEG-4 Sampling Frequency Index (15 is forbidden)
43 // H 3 MPEG-4 Channel Configuration (in the case of 0, the
44 // channel configuration is sent via an in-band PCE)
45 // M 13 frame length, this value must include 7 or 9 bytes of
46 // header length: FrameLength =
47 // (ProtectionAbsent == 1 ? 7 : 9) + size(AACFrame)
48 // O 11 Buffer fullness
49 // P 2 Number of AAC frames(RDBs) in ADTS frame minus 1, for
50 // maximum compatibility always use 1 AAC frame per ADTS
51 // frame
52 // Q 16 CRC if protection absent is 0
53 class FrameHeader {
54 public:
55 uint32_t mFrameLength{};
56 uint32_t mSampleRate{};
57 uint32_t mSamples{};
58 uint32_t mChannels{};
59 uint8_t mObjectType{};
60 uint8_t mSamplingIndex{};
61 uint8_t mChannelConfig{};
62 uint8_t mNumAACFrames{};
63 bool mHaveCrc{};
65 // Returns whether aPtr matches a valid ADTS header sync marker
66 static bool MatchesSync(const uint8_t* aPtr) {
67 return aPtr[0] == 0xFF && (aPtr[1] & 0xF6) == 0xF0;
70 FrameHeader() { Reset(); }
72 // Header size
73 uint64_t HeaderSize() const { return (mHaveCrc) ? 9 : 7; }
75 bool IsValid() const { return mFrameLength > 0; }
77 // Resets the state to allow for a new parsing session.
78 void Reset() { PodZero(this); }
80 // Returns whether the byte creates a valid sequence up to this point.
81 bool Parse(const uint8_t* aPtr) {
82 const uint8_t* p = aPtr;
84 if (!MatchesSync(p)) {
85 return false;
88 // AAC has 1024 samples per frame per channel.
89 mSamples = 1024;
91 mHaveCrc = !(p[1] & 0x01);
92 mObjectType = ((p[2] & 0xC0) >> 6) + 1;
93 mSamplingIndex = (p[2] & 0x3C) >> 2;
94 mChannelConfig = (p[2] & 0x01) << 2 | (p[3] & 0xC0) >> 6;
95 mFrameLength = static_cast<uint32_t>(
96 (p[3] & 0x03) << 11 | (p[4] & 0xFF) << 3 | (p[5] & 0xE0) >> 5);
97 mNumAACFrames = (p[6] & 0x03) + 1;
99 static const uint32_t SAMPLE_RATES[] = {96000, 88200, 64000, 48000, 44100,
100 32000, 24000, 22050, 16000, 12000,
101 11025, 8000, 7350};
102 if (mSamplingIndex >= ArrayLength(SAMPLE_RATES)) {
103 LOG(("ADTS: Init() failure: invalid sample-rate index value: %" PRIu32
104 ".",
105 mSamplingIndex));
106 return false;
108 mSampleRate = SAMPLE_RATES[mSamplingIndex];
110 MOZ_ASSERT(mChannelConfig < 8);
111 mChannels = (mChannelConfig == 7) ? 8 : mChannelConfig;
113 return true;
117 // adts::Frame - Frame meta container used to parse and hold a frame
118 // header and side info.
119 class Frame {
120 public:
121 Frame() : mOffset(0), mHeader() {}
123 uint64_t Offset() const { return mOffset; }
124 size_t Length() const {
125 // TODO: If fields are zero'd when invalid, this check wouldn't be
126 // necessary.
127 if (!mHeader.IsValid()) {
128 return 0;
131 return mHeader.mFrameLength;
134 // Returns the offset to the start of frame's raw data.
135 uint64_t PayloadOffset() const { return mOffset + mHeader.HeaderSize(); }
137 // Returns the length of the frame's raw data (excluding the header) in bytes.
138 size_t PayloadLength() const {
139 // TODO: If fields are zero'd when invalid, this check wouldn't be
140 // necessary.
141 if (!mHeader.IsValid()) {
142 return 0;
145 return mHeader.mFrameLength - mHeader.HeaderSize();
148 // Returns the parsed frame header.
149 const FrameHeader& Header() const { return mHeader; }
151 bool IsValid() const { return mHeader.IsValid(); }
153 // Resets the frame header and data.
154 void Reset() {
155 mHeader.Reset();
156 mOffset = 0;
159 // Returns whether the valid
160 bool Parse(uint64_t aOffset, const uint8_t* aStart, const uint8_t* aEnd) {
161 MOZ_ASSERT(aStart && aEnd);
163 bool found = false;
164 const uint8_t* ptr = aStart;
165 // Require at least 7 bytes of data at the end of the buffer for the minimum
166 // ADTS frame header.
167 while (ptr < aEnd - 7 && !found) {
168 found = mHeader.Parse(ptr);
169 ptr++;
172 mOffset = aOffset + (static_cast<size_t>(ptr - aStart)) - 1u;
174 return found;
177 private:
178 // The offset to the start of the header.
179 uint64_t mOffset;
181 // The currently parsed frame header.
182 FrameHeader mHeader;
185 class FrameParser {
186 public:
187 // Returns the currently parsed frame. Reset via Reset or EndFrameSession.
188 const Frame& CurrentFrame() const { return mFrame; }
190 // Returns the first parsed frame. Reset via Reset.
191 const Frame& FirstFrame() const { return mFirstFrame; }
193 // Resets the parser. Don't use between frames as first frame data is reset.
194 void Reset() {
195 EndFrameSession();
196 mFirstFrame.Reset();
199 // Clear the last parsed frame to allow for next frame parsing, i.e.:
200 // - sets PrevFrame to CurrentFrame
201 // - resets the CurrentFrame
202 // - resets ID3Header if no valid header was parsed yet
203 void EndFrameSession() { mFrame.Reset(); }
205 // Parses contents of given ByteReader for a valid frame header and returns
206 // true if one was found. After returning, the variable passed to
207 // 'aBytesToSkip' holds the amount of bytes to be skipped (if any) in order to
208 // jump across a large ID3v2 tag spanning multiple buffers.
209 bool Parse(uint64_t aOffset, const uint8_t* aStart, const uint8_t* aEnd) {
210 const bool found = mFrame.Parse(aOffset, aStart, aEnd);
212 if (mFrame.Length() && !mFirstFrame.Length()) {
213 mFirstFrame = mFrame;
216 return found;
219 private:
220 // We keep the first parsed frame around for static info access, the
221 // previously parsed frame for debugging and the currently parsed frame.
222 Frame mFirstFrame;
223 Frame mFrame;
226 // Initialize the AAC AudioSpecificConfig.
227 // Only handles two-byte version for AAC-LC.
228 static void InitAudioSpecificConfig(const Frame& frame,
229 MediaByteBuffer* aBuffer) {
230 const FrameHeader& header = frame.Header();
231 MOZ_ASSERT(header.IsValid());
233 int audioObjectType = header.mObjectType;
234 int samplingFrequencyIndex = header.mSamplingIndex;
235 int channelConfig = header.mChannelConfig;
237 uint8_t asc[2];
238 asc[0] = (audioObjectType & 0x1F) << 3 | (samplingFrequencyIndex & 0x0E) >> 1;
239 asc[1] = (samplingFrequencyIndex & 0x01) << 7 | (channelConfig & 0x0F) << 3;
241 aBuffer->AppendElements(asc, 2);
244 } // namespace adts
246 using media::TimeUnit;
248 // ADTSDemuxer
250 ADTSDemuxer::ADTSDemuxer(MediaResource* aSource) : mSource(aSource) {
251 DDLINKCHILD("source", aSource);
254 bool ADTSDemuxer::InitInternal() {
255 if (!mTrackDemuxer) {
256 mTrackDemuxer = new ADTSTrackDemuxer(mSource);
257 DDLINKCHILD("track demuxer", mTrackDemuxer.get());
259 return mTrackDemuxer->Init();
262 RefPtr<ADTSDemuxer::InitPromise> ADTSDemuxer::Init() {
263 if (!InitInternal()) {
264 ADTSLOG("Init() failure: waiting for data");
266 return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_METADATA_ERR,
267 __func__);
270 ADTSLOG("Init() successful");
271 return InitPromise::CreateAndResolve(NS_OK, __func__);
274 uint32_t ADTSDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const {
275 return (aType == TrackInfo::kAudioTrack) ? 1 : 0;
278 already_AddRefed<MediaTrackDemuxer> ADTSDemuxer::GetTrackDemuxer(
279 TrackInfo::TrackType aType, uint32_t aTrackNumber) {
280 if (!mTrackDemuxer) {
281 return nullptr;
284 return RefPtr<ADTSTrackDemuxer>(mTrackDemuxer).forget();
287 bool ADTSDemuxer::IsSeekable() const {
288 int64_t length = mSource->GetLength();
289 return length > -1;
292 // ADTSTrackDemuxer
293 ADTSTrackDemuxer::ADTSTrackDemuxer(MediaResource* aSource)
294 : mSource(aSource),
295 mParser(new adts::FrameParser()),
296 mOffset(0),
297 mNumParsedFrames(0),
298 mFrameIndex(0),
299 mTotalFrameLen(0),
300 mSamplesPerFrame(0),
301 mSamplesPerSecond(0),
302 mChannels(0) {
303 DDLINKCHILD("source", aSource);
304 Reset();
307 ADTSTrackDemuxer::~ADTSTrackDemuxer() { delete mParser; }
309 bool ADTSTrackDemuxer::Init() {
310 FastSeek(TimeUnit::Zero());
311 // Read the first frame to fetch sample rate and other meta data.
312 RefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame(true)));
314 ADTSLOG("Init StreamLength()=%" PRId64 " first-frame-found=%d",
315 StreamLength(), !!frame);
317 if (!frame) {
318 return false;
321 // Rewind back to the stream begin to avoid dropping the first frame.
322 FastSeek(TimeUnit::Zero());
324 if (!mSamplesPerSecond) {
325 return false;
328 if (!mInfo) {
329 mInfo = MakeUnique<AudioInfo>();
332 mInfo->mRate = mSamplesPerSecond;
333 mInfo->mChannels = mChannels;
334 mInfo->mBitDepth = 16;
335 mInfo->mDuration = Duration();
337 // AAC Specific information
338 mInfo->mMimeType = "audio/mp4a-latm";
340 // Configure AAC codec-specific values.
341 // For AAC, mProfile and mExtendedProfile contain the audioObjectType from
342 // Table 1.3 -- Audio Profile definition, ISO/IEC 14496-3. Eg. 2 == AAC LC
343 mInfo->mProfile = mInfo->mExtendedProfile =
344 mParser->FirstFrame().Header().mObjectType;
345 AudioCodecSpecificBinaryBlob blob;
346 InitAudioSpecificConfig(mParser->FirstFrame(), blob.mBinaryBlob);
347 mInfo->mCodecSpecificConfig = AudioCodecSpecificVariant{std::move(blob)};
349 ADTSLOG("Init mInfo={mRate=%u mChannels=%u mBitDepth=%u mDuration=%" PRId64
350 "}",
351 mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth,
352 mInfo->mDuration.ToMicroseconds());
354 // AAC encoder delay can be 2112 (typical value when using Apple AAC encoder),
355 // or 1024 (typical value when encoding using fdk_aac, often via ffmpeg).
356 // See
357 // https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFAppenG/QTFFAppenG.html
358 // In an attempt to not trim valid audio data, and because ADTS doesn't
359 // provide a way to know this pre-roll value, this offets by 1024 frames.
360 mPreRoll = TimeUnit(1024, mSamplesPerSecond);
361 return mChannels;
364 UniquePtr<TrackInfo> ADTSTrackDemuxer::GetInfo() const {
365 return mInfo->Clone();
368 RefPtr<ADTSTrackDemuxer::SeekPromise> ADTSTrackDemuxer::Seek(
369 const TimeUnit& aTime) {
370 // Efficiently seek to the position.
371 const TimeUnit time = aTime > mPreRoll ? aTime - mPreRoll : TimeUnit::Zero();
372 FastSeek(time);
373 // Correct seek position by scanning the next frames.
374 const TimeUnit seekTime = ScanUntil(time);
376 return SeekPromise::CreateAndResolve(seekTime, __func__);
379 TimeUnit ADTSTrackDemuxer::FastSeek(const TimeUnit& aTime) {
380 ADTSLOG("FastSeek(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
381 " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
382 aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
383 mFrameIndex, mOffset);
385 const uint64_t firstFrameOffset = mParser->FirstFrame().Offset();
386 if (!aTime.ToMicroseconds()) {
387 // Quick seek to the beginning of the stream.
388 mOffset = firstFrameOffset;
389 } else if (AverageFrameLength() > 0) {
390 mOffset =
391 firstFrameOffset +
392 AssertedCast<uint64_t>(AssertedCast<double>(FrameIndexFromTime(aTime)) *
393 AverageFrameLength());
396 const int64_t streamLength = StreamLength();
397 if (mOffset > firstFrameOffset && streamLength > 0) {
398 mOffset = std::min(static_cast<uint64_t>(streamLength - 1), mOffset);
401 mFrameIndex = FrameIndexFromOffset(mOffset);
402 mParser->EndFrameSession();
404 ADTSLOG("FastSeek End avgFrameLen=%f mNumParsedFrames=%" PRIu64
405 " mFrameIndex=%" PRId64 " mFirstFrameOffset=%" PRIu64
406 " mOffset=%" PRIu64 " SL=%" PRIu64 "",
407 AverageFrameLength(), mNumParsedFrames, mFrameIndex, firstFrameOffset,
408 mOffset, streamLength);
410 return Duration(mFrameIndex);
413 TimeUnit ADTSTrackDemuxer::ScanUntil(const TimeUnit& aTime) {
414 ADTSLOG("ScanUntil(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
415 " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
416 aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
417 mFrameIndex, mOffset);
419 if (!aTime.ToMicroseconds()) {
420 return FastSeek(aTime);
423 if (Duration(mFrameIndex) > aTime) {
424 FastSeek(aTime);
427 while (SkipNextFrame(FindNextFrame()) && Duration(mFrameIndex + 1) < aTime) {
428 ADTSLOGV("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64
429 " mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64,
430 AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset,
431 Duration(mFrameIndex + 1).ToMicroseconds());
434 ADTSLOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64
435 " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
436 AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
438 return Duration(mFrameIndex);
441 RefPtr<ADTSTrackDemuxer::SamplesPromise> ADTSTrackDemuxer::GetSamples(
442 int32_t aNumSamples) {
443 ADTSLOGV("GetSamples(%d) Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
444 " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
445 " mSamplesPerFrame=%d "
446 "mSamplesPerSecond=%d mChannels=%d",
447 aNumSamples, mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
448 mSamplesPerFrame, mSamplesPerSecond, mChannels);
450 MOZ_ASSERT(aNumSamples);
452 RefPtr<SamplesHolder> frames = new SamplesHolder();
454 while (aNumSamples--) {
455 RefPtr<MediaRawData> frame(GetNextFrame(FindNextFrame()));
456 if (!frame) break;
457 frames->AppendSample(frame);
460 ADTSLOGV(
461 "GetSamples() End mSamples.Size()=%zu aNumSamples=%d mOffset=%" PRIu64
462 " mNumParsedFrames=%" PRIu64 " mFrameIndex=%" PRId64
463 " mTotalFrameLen=%" PRIu64
464 " mSamplesPerFrame=%d mSamplesPerSecond=%d "
465 "mChannels=%d",
466 frames->GetSamples().Length(), aNumSamples, mOffset, mNumParsedFrames,
467 mFrameIndex, mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond,
468 mChannels);
470 if (frames->GetSamples().IsEmpty()) {
471 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM,
472 __func__);
475 return SamplesPromise::CreateAndResolve(frames, __func__);
478 void ADTSTrackDemuxer::Reset() {
479 ADTSLOG("Reset()");
480 MOZ_ASSERT(mParser);
481 if (mParser) {
482 mParser->Reset();
484 FastSeek(TimeUnit::Zero());
487 RefPtr<ADTSTrackDemuxer::SkipAccessPointPromise>
488 ADTSTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold) {
489 // Will not be called for audio-only resources.
490 return SkipAccessPointPromise::CreateAndReject(
491 SkipFailureHolder(NS_ERROR_DOM_MEDIA_DEMUXER_ERR, 0), __func__);
494 int64_t ADTSTrackDemuxer::GetResourceOffset() const {
495 return AssertedCast<int64_t>(mOffset);
498 media::TimeIntervals ADTSTrackDemuxer::GetBuffered() {
499 auto duration = Duration();
501 if (duration.IsInfinite()) {
502 return media::TimeIntervals();
505 AutoPinned<MediaResource> stream(mSource.GetResource());
506 return GetEstimatedBufferedTimeRanges(stream, duration.ToMicroseconds());
509 int64_t ADTSTrackDemuxer::StreamLength() const { return mSource.GetLength(); }
511 TimeUnit ADTSTrackDemuxer::Duration() const {
512 if (!mNumParsedFrames) {
513 return TimeUnit::Invalid();
516 const int64_t streamLen = StreamLength();
517 if (streamLen < 0) {
518 // Unknown length, we can't estimate duration, this is probably a live
519 // stream.
520 return TimeUnit::FromInfinity();
522 const int64_t firstFrameOffset =
523 AssertedCast<int64_t>(mParser->FirstFrame().Offset());
524 int64_t numFrames =
525 AssertedCast<int64_t>(AssertedCast<double>(streamLen - firstFrameOffset) /
526 AverageFrameLength());
527 return Duration(numFrames);
530 TimeUnit ADTSTrackDemuxer::Duration(int64_t aNumFrames) const {
531 if (!mSamplesPerSecond) {
532 return TimeUnit::Invalid();
535 return TimeUnit(aNumFrames * mSamplesPerFrame, mSamplesPerSecond);
538 const adts::Frame& ADTSTrackDemuxer::FindNextFrame(
539 bool findFirstFrame /*= false*/) {
540 static const int BUFFER_SIZE = 4096;
541 static const int MAX_SKIPPED_BYTES = 10 * BUFFER_SIZE;
543 ADTSLOGV("FindNext() Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
544 " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
545 " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
546 mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
547 mSamplesPerFrame, mSamplesPerSecond, mChannels);
549 uint8_t buffer[BUFFER_SIZE];
550 uint32_t read = 0;
552 bool foundFrame = false;
553 uint64_t frameHeaderOffset = mOffset;
555 // Prepare the parser for the next frame parsing session.
556 mParser->EndFrameSession();
558 // Check whether we've found a valid ADTS frame.
559 while (!foundFrame) {
560 if ((read = Read(buffer, AssertedCast<int64_t>(frameHeaderOffset),
561 BUFFER_SIZE)) == 0) {
562 ADTSLOG("FindNext() EOS without a frame");
563 break;
566 if (frameHeaderOffset - mOffset > MAX_SKIPPED_BYTES) {
567 ADTSLOG("FindNext() exceeded MAX_SKIPPED_BYTES without a frame");
568 break;
571 const adts::Frame& currentFrame = mParser->CurrentFrame();
572 foundFrame = mParser->Parse(frameHeaderOffset, buffer, buffer + read);
573 if (findFirstFrame && foundFrame) {
574 // Check for sync marker after the found frame, since it's
575 // possible to find sync marker in AAC data. If sync marker
576 // exists after the current frame then we've found a frame
577 // header.
578 uint64_t nextFrameHeaderOffset =
579 currentFrame.Offset() + currentFrame.Length();
580 uint32_t read =
581 Read(buffer, AssertedCast<int64_t>(nextFrameHeaderOffset), 2);
582 if (read != 2 || !adts::FrameHeader::MatchesSync(buffer)) {
583 frameHeaderOffset = currentFrame.Offset() + 1;
584 mParser->Reset();
585 foundFrame = false;
586 continue;
590 if (foundFrame) {
591 break;
594 // Minimum header size is 7 bytes.
595 uint64_t advance = read - 7;
597 // Check for offset overflow.
598 if (frameHeaderOffset + advance <= frameHeaderOffset) {
599 break;
602 frameHeaderOffset += advance;
605 if (!foundFrame || !mParser->CurrentFrame().Length()) {
606 ADTSLOG(
607 "FindNext() Exit foundFrame=%d mParser->CurrentFrame().Length()=%zu ",
608 foundFrame, mParser->CurrentFrame().Length());
609 mParser->Reset();
610 return mParser->CurrentFrame();
613 ADTSLOGV("FindNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
614 " mFrameIndex=%" PRId64 " frameHeaderOffset=%" PRId64
615 " mTotalFrameLen=%" PRIu64
616 " mSamplesPerFrame=%d mSamplesPerSecond=%d"
617 " mChannels=%d",
618 mOffset, mNumParsedFrames, mFrameIndex, frameHeaderOffset,
619 mTotalFrameLen, mSamplesPerFrame, mSamplesPerSecond, mChannels);
621 return mParser->CurrentFrame();
624 bool ADTSTrackDemuxer::SkipNextFrame(const adts::Frame& aFrame) {
625 if (!mNumParsedFrames || !aFrame.Length()) {
626 RefPtr<MediaRawData> frame(GetNextFrame(aFrame));
627 return frame;
630 UpdateState(aFrame);
632 ADTSLOGV("SkipNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
633 " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
634 " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
635 mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
636 mSamplesPerFrame, mSamplesPerSecond, mChannels);
638 return true;
641 already_AddRefed<MediaRawData> ADTSTrackDemuxer::GetNextFrame(
642 const adts::Frame& aFrame) {
643 ADTSLOG("GetNext() Begin({mOffset=%" PRIu64 " HeaderSize()=%" PRIu64
644 " Length()=%zu})",
645 aFrame.Offset(), aFrame.Header().HeaderSize(),
646 aFrame.PayloadLength());
647 if (!aFrame.IsValid()) return nullptr;
649 const int64_t offset = AssertedCast<int64_t>(aFrame.PayloadOffset());
650 const uint32_t length = aFrame.PayloadLength();
652 RefPtr<MediaRawData> frame = new MediaRawData();
653 frame->mOffset = offset;
655 UniquePtr<MediaRawDataWriter> frameWriter(frame->CreateWriter());
656 if (!frameWriter->SetSize(length)) {
657 ADTSLOG("GetNext() Exit failed to allocated media buffer");
658 return nullptr;
661 const uint32_t read =
662 Read(frameWriter->Data(), offset, AssertedCast<int32_t>(length));
663 if (read != length) {
664 ADTSLOG("GetNext() Exit read=%u frame->Size()=%zu", read, frame->Size());
665 return nullptr;
668 UpdateState(aFrame);
670 TimeUnit rawpts = Duration(mFrameIndex - 1) - mPreRoll;
671 TimeUnit rawDuration = Duration(1);
672 TimeUnit rawend = rawpts + rawDuration;
674 frame->mTime = std::max(TimeUnit::Zero(), rawpts);
675 frame->mDuration = Duration(1);
676 frame->mTimecode = frame->mTime;
677 frame->mKeyframe = true;
679 // Handle decoder delay. A packet must be trimmed if its pts, adjusted for
680 // decoder delay, is negative. A packet can be trimmed entirely.
681 if (rawpts.IsNegative()) {
682 frame->mDuration = std::max(TimeUnit::Zero(), rawend - frame->mTime);
685 // ADTS frames can have a presentation duration of zero, e.g. when a frame is
686 // part of preroll.
687 MOZ_ASSERT(frame->mDuration.IsPositiveOrZero());
689 ADTSLOG("ADTS packet demuxed: pts [%lf, %lf] (duration: %lf)",
690 frame->mTime.ToSeconds(), frame->GetEndTime().ToSeconds(),
691 frame->mDuration.ToSeconds());
693 // Indicate original packet information to trim after decoding.
694 if (frame->mDuration != rawDuration) {
695 frame->mOriginalPresentationWindow =
696 Some(media::TimeInterval{rawpts, rawend});
697 ADTSLOG("Total packet time excluding trimming: [%lf, %lf]",
698 rawpts.ToSeconds(), rawend.ToSeconds());
701 ADTSLOGV("GetNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
702 " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
703 " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
704 mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
705 mSamplesPerFrame, mSamplesPerSecond, mChannels);
707 return frame.forget();
710 int64_t ADTSTrackDemuxer::FrameIndexFromOffset(uint64_t aOffset) const {
711 int64_t frameIndex = 0;
713 if (AverageFrameLength() > 0) {
714 frameIndex = AssertedCast<int64_t>(
715 AssertedCast<double>(aOffset - mParser->FirstFrame().Offset()) /
716 AverageFrameLength());
717 MOZ_ASSERT(frameIndex >= 0);
720 ADTSLOGV("FrameIndexFromOffset(%" PRId64 ") -> %" PRId64, aOffset,
721 frameIndex);
722 return frameIndex;
725 int64_t ADTSTrackDemuxer::FrameIndexFromTime(const TimeUnit& aTime) const {
726 int64_t frameIndex = 0;
727 if (mSamplesPerSecond > 0 && mSamplesPerFrame > 0) {
728 frameIndex = AssertedCast<int64_t>(aTime.ToSeconds() * mSamplesPerSecond /
729 mSamplesPerFrame) -
733 ADTSLOGV("FrameIndexFromOffset(%fs) -> %" PRId64, aTime.ToSeconds(),
734 frameIndex);
735 return std::max<int64_t>(0, frameIndex);
738 void ADTSTrackDemuxer::UpdateState(const adts::Frame& aFrame) {
739 uint32_t frameLength = aFrame.Length();
740 // Prevent overflow.
741 if (mTotalFrameLen + frameLength < mTotalFrameLen) {
742 // These variables have a linear dependency and are only used to derive the
743 // average frame length.
744 mTotalFrameLen /= 2;
745 mNumParsedFrames /= 2;
748 // Full frame parsed, move offset to its end.
749 mOffset = aFrame.Offset() + frameLength;
750 mTotalFrameLen += frameLength;
752 if (!mSamplesPerFrame) {
753 const adts::FrameHeader& header = aFrame.Header();
754 mSamplesPerFrame = header.mSamples;
755 mSamplesPerSecond = header.mSampleRate;
756 mChannels = header.mChannels;
759 ++mNumParsedFrames;
760 ++mFrameIndex;
761 MOZ_ASSERT(mFrameIndex > 0);
764 uint32_t ADTSTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset,
765 int32_t aSize) {
766 ADTSLOGV("ADTSTrackDemuxer::Read(%p %" PRId64 " %d)", aBuffer, aOffset,
767 aSize);
769 const int64_t streamLen = StreamLength();
770 if (mInfo && streamLen > 0) {
771 int64_t max = streamLen > aOffset ? streamLen - aOffset : 0;
772 // Prevent blocking reads after successful initialization.
773 aSize = std::min<int32_t>(aSize, AssertedCast<int32_t>(max));
776 uint32_t read = 0;
777 ADTSLOGV("ADTSTrackDemuxer::Read -> ReadAt(%d)", aSize);
778 const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast<char*>(aBuffer),
779 static_cast<uint32_t>(aSize), &read);
780 NS_ENSURE_SUCCESS(rv, 0);
781 return read;
784 double ADTSTrackDemuxer::AverageFrameLength() const {
785 if (mNumParsedFrames) {
786 return AssertedCast<double>(mTotalFrameLen) /
787 AssertedCast<double>(mNumParsedFrames);
790 return 0.0;
793 /* static */
794 bool ADTSDemuxer::ADTSSniffer(const uint8_t* aData, const uint32_t aLength) {
795 if (aLength < 7) {
796 return false;
798 if (!adts::FrameHeader::MatchesSync(aData)) {
799 return false;
801 auto parser = MakeUnique<adts::FrameParser>();
803 if (!parser->Parse(0, aData, aData + aLength)) {
804 return false;
806 const adts::Frame& currentFrame = parser->CurrentFrame();
807 // Check for sync marker after the found frame, since it's
808 // possible to find sync marker in AAC data. If sync marker
809 // exists after the current frame then we've found a frame
810 // header.
811 uint64_t nextFrameHeaderOffset =
812 currentFrame.Offset() + currentFrame.Length();
813 return aLength > nextFrameHeaderOffset &&
814 aLength - nextFrameHeaderOffset >= 2 &&
815 adts::FrameHeader::MatchesSync(aData + nextFrameHeaderOffset);
818 } // namespace mozilla