Backed out 7 changesets (bug 1942424) for causing frequent crashes. a=backout
[gecko.git] / dom / media / webm / EbmlComposer.cpp
blobe3f04fd89b0dcc9f8eceb5982c1d92b51df3d86f
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "EbmlComposer.h"
7 #include "mozilla/UniquePtr.h"
8 #include "mozilla/EndianUtils.h"
9 #include "libmkv/EbmlIDs.h"
10 #include "libmkv/EbmlWriter.h"
11 #include "libmkv/WebMElement.h"
12 #include "prtime.h"
13 #include "limits.h"
15 namespace mozilla {
17 // Timecode scale in nanoseconds
18 constexpr unsigned long TIME_CODE_SCALE = 1000000;
19 // The WebM header size without audio CodecPrivateData
20 constexpr int32_t DEFAULT_HEADER_SIZE = 1024;
21 // Number of milliseconds after which we flush audio-only clusters
22 constexpr int32_t FLUSH_AUDIO_ONLY_AFTER_MS = 1000;
24 void EbmlComposer::GenerateHeader() {
25 MOZ_RELEASE_ASSERT(!mMetadataFinished);
26 MOZ_RELEASE_ASSERT(mHasAudio || mHasVideo);
28 // Write the EBML header.
29 EbmlGlobal ebml;
30 // The WEbM header default size usually smaller than 1k.
31 auto buffer =
32 MakeUnique<uint8_t[]>(DEFAULT_HEADER_SIZE + mCodecPrivateData.Length());
33 ebml.buf = buffer.get();
34 ebml.offset = 0;
35 writeHeader(&ebml);
37 EbmlLoc segEbmlLoc, ebmlLocseg, ebmlLoc;
38 Ebml_StartSubElement(&ebml, &segEbmlLoc, Segment);
40 Ebml_StartSubElement(&ebml, &ebmlLocseg, SeekHead);
41 // Todo: We don't know the exact sizes of encoded data and
42 // ignore this section.
43 Ebml_EndSubElement(&ebml, &ebmlLocseg);
44 writeSegmentInformation(&ebml, &ebmlLoc, TIME_CODE_SCALE, 0);
46 EbmlLoc trackLoc;
47 Ebml_StartSubElement(&ebml, &trackLoc, Tracks);
49 // Video
50 if (mWidth > 0 && mHeight > 0) {
51 writeVideoTrack(&ebml, 0x1, 0, "V_VP8", mWidth, mHeight,
52 mDisplayWidth, mDisplayHeight);
54 // Audio
55 if (mCodecPrivateData.Length() > 0) {
56 // Extract the pre-skip from mCodecPrivateData
57 // then convert it to nanoseconds.
58 // For more details see
59 // https://tools.ietf.org/html/rfc7845#section-4.2
60 uint64_t codecDelay = (uint64_t)LittleEndian::readUint16(
61 mCodecPrivateData.Elements() + 10) *
62 PR_NSEC_PER_SEC / 48000;
63 // Fixed 80ms, convert into nanoseconds.
64 uint64_t seekPreRoll = 80 * PR_NSEC_PER_MSEC;
65 writeAudioTrack(&ebml, 0x2, 0x0, "A_OPUS", mSampleFreq, mChannels,
66 codecDelay, seekPreRoll,
67 mCodecPrivateData.Elements(),
68 mCodecPrivateData.Length());
71 Ebml_EndSubElement(&ebml, &trackLoc);
74 // The Recording length is unknown and
75 // ignore write the whole Segment element size
77 MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(),
78 "write more data > EBML_BUFFER_SIZE");
79 auto block = mBuffer.AppendElement();
80 block->SetLength(ebml.offset);
81 memcpy(block->Elements(), ebml.buf, ebml.offset);
82 mMetadataFinished = true;
85 nsresult EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame) {
86 MOZ_RELEASE_ASSERT(mMetadataFinished);
87 auto frameType = aFrame->mFrameType;
88 const bool isVP8IFrame = (frameType == EncodedFrame::FrameType::VP8_I_FRAME);
89 const bool isVP8PFrame = (frameType == EncodedFrame::FrameType::VP8_P_FRAME);
90 const bool isOpus = (frameType == EncodedFrame::FrameType::OPUS_AUDIO_FRAME);
92 MOZ_ASSERT_IF(isVP8IFrame, mHasVideo);
93 MOZ_ASSERT_IF(isVP8PFrame, mHasVideo);
94 MOZ_ASSERT_IF(isOpus, mHasAudio);
96 if (isVP8PFrame && !mHasWrittenCluster) {
97 // We ensure there is a cluster header and an I-frame prior to any P-frame.
98 return NS_ERROR_INVALID_ARG;
101 int64_t timeCode = aFrame->mTime.ToMicroseconds() / PR_USEC_PER_MSEC -
102 mCurrentClusterTimecode;
104 const bool needClusterHeader =
105 !mHasWrittenCluster ||
106 (!mHasVideo && timeCode >= FLUSH_AUDIO_ONLY_AFTER_MS) || isVP8IFrame;
108 auto block = mBuffer.AppendElement();
109 block->SetLength(aFrame->mFrameData->Length() + DEFAULT_HEADER_SIZE);
111 EbmlGlobal ebml;
112 ebml.offset = 0;
113 ebml.buf = block->Elements();
115 if (needClusterHeader) {
116 mHasWrittenCluster = true;
117 EbmlLoc ebmlLoc;
118 // This starts the Cluster element. Note that we never end this element
119 // through Ebml_EndSubElement. What the ending would allow us to do is write
120 // the full length of the cluster in the element header. That would also
121 // force us to keep the entire cluster in memory until we know where it
122 // ends. Now it instead ends through the start of the next cluster. This
123 // allows us to stream the muxed data with much lower latency than if we
124 // would have to wait for clusters to end.
125 Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster);
126 // if timeCode didn't under/overflow before, it shouldn't after this
127 mCurrentClusterTimecode = aFrame->mTime.ToMicroseconds() / PR_USEC_PER_MSEC;
128 Ebml_SerializeUnsigned(&ebml, Timecode, mCurrentClusterTimecode);
130 // Can't under-/overflow now
131 timeCode = 0;
134 if (MOZ_UNLIKELY(timeCode < SHRT_MIN || timeCode > SHRT_MAX)) {
135 MOZ_CRASH_UNSAFE_PRINTF(
136 "Invalid cluster timecode! audio=%d, video=%d, timeCode=%" PRId64
137 "ms, currentClusterTimecode=%" PRIu64 "ms",
138 mHasAudio, mHasVideo, timeCode, mCurrentClusterTimecode);
141 writeSimpleBlock(&ebml, isOpus ? 0x2 : 0x1, static_cast<short>(timeCode),
142 isVP8IFrame, 0, 0,
143 (unsigned char*)aFrame->mFrameData->Elements(),
144 aFrame->mFrameData->Length());
145 MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + aFrame->mFrameData->Length(),
146 "write more data > EBML_BUFFER_SIZE");
147 block->SetLength(ebml.offset);
149 return NS_OK;
152 void EbmlComposer::SetVideoConfig(uint32_t aWidth, uint32_t aHeight,
153 uint32_t aDisplayWidth,
154 uint32_t aDisplayHeight) {
155 MOZ_RELEASE_ASSERT(!mMetadataFinished);
156 MOZ_ASSERT(aWidth > 0, "Width should > 0");
157 MOZ_ASSERT(aHeight > 0, "Height should > 0");
158 MOZ_ASSERT(aDisplayWidth > 0, "DisplayWidth should > 0");
159 MOZ_ASSERT(aDisplayHeight > 0, "DisplayHeight should > 0");
160 mWidth = aWidth;
161 mHeight = aHeight;
162 mDisplayWidth = aDisplayWidth;
163 mDisplayHeight = aDisplayHeight;
164 mHasVideo = true;
167 void EbmlComposer::SetAudioConfig(uint32_t aSampleFreq, uint32_t aChannels) {
168 MOZ_RELEASE_ASSERT(!mMetadataFinished);
169 MOZ_ASSERT(aSampleFreq > 0, "SampleFreq should > 0");
170 MOZ_ASSERT(aChannels > 0, "Channels should > 0");
171 mSampleFreq = aSampleFreq;
172 mChannels = aChannels;
173 mHasAudio = true;
176 void EbmlComposer::ExtractBuffer(nsTArray<nsTArray<uint8_t> >* aDestBufs,
177 uint32_t aFlag) {
178 if (!mMetadataFinished) {
179 return;
181 aDestBufs->AppendElements(std::move(mBuffer));
182 MOZ_ASSERT(mBuffer.IsEmpty());
185 } // namespace mozilla