Line data Source code
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/. */
5 :
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"
14 :
15 : namespace mozilla {
16 :
17 : // Timecode scale in nanoseconds
18 : static const unsigned long TIME_CODE_SCALE = 1000000;
19 : // The WebM header size without audio CodecPrivateData
20 : static const int32_t DEFAULT_HEADER_SIZE = 1024;
21 :
22 0 : void EbmlComposer::GenerateHeader()
23 : {
24 : // Write the EBML header.
25 : EbmlGlobal ebml;
26 : // The WEbM header default size usually smaller than 1k.
27 : auto buffer = MakeUnique<uint8_t[]>(DEFAULT_HEADER_SIZE +
28 0 : mCodecPrivateData.Length());
29 0 : ebml.buf = buffer.get();
30 0 : ebml.offset = 0;
31 0 : writeHeader(&ebml);
32 : {
33 : EbmlLoc segEbmlLoc, ebmlLocseg, ebmlLoc;
34 0 : Ebml_StartSubElement(&ebml, &segEbmlLoc, Segment);
35 : {
36 0 : Ebml_StartSubElement(&ebml, &ebmlLocseg, SeekHead);
37 : // Todo: We don't know the exact sizes of encoded data and
38 : // ignore this section.
39 0 : Ebml_EndSubElement(&ebml, &ebmlLocseg);
40 0 : writeSegmentInformation(&ebml, &ebmlLoc, TIME_CODE_SCALE, 0);
41 : {
42 : EbmlLoc trackLoc;
43 0 : Ebml_StartSubElement(&ebml, &trackLoc, Tracks);
44 : {
45 : // Video
46 0 : if (mWidth > 0 && mHeight > 0) {
47 0 : writeVideoTrack(&ebml, 0x1, 0, "V_VP8",
48 0 : mWidth, mHeight,
49 0 : mDisplayWidth, mDisplayHeight);
50 : }
51 : // Audio
52 0 : if (mCodecPrivateData.Length() > 0) {
53 : // Extract the pre-skip from mCodecPrivateData
54 : // then convert it to nanoseconds.
55 : // Details in OpusTrackEncoder.cpp.
56 0 : mCodecDelay =
57 0 : (uint64_t)LittleEndian::readUint16(mCodecPrivateData.Elements() + 10)
58 0 : * PR_NSEC_PER_SEC / 48000;
59 : // Fixed 80ms, convert into nanoseconds.
60 0 : uint64_t seekPreRoll = 80 * PR_NSEC_PER_MSEC;
61 0 : writeAudioTrack(&ebml, 0x2, 0x0, "A_OPUS", mSampleFreq,
62 0 : mChannels, mCodecDelay, seekPreRoll,
63 : mCodecPrivateData.Elements(),
64 0 : mCodecPrivateData.Length());
65 : }
66 : }
67 0 : Ebml_EndSubElement(&ebml, &trackLoc);
68 : }
69 : }
70 : // The Recording length is unknown and
71 : // ignore write the whole Segment element size
72 : }
73 0 : MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(),
74 : "write more data > EBML_BUFFER_SIZE");
75 0 : auto block = mClusterBuffs.AppendElement();
76 0 : block->SetLength(ebml.offset);
77 0 : memcpy(block->Elements(), ebml.buf, ebml.offset);
78 0 : mFlushState |= FLUSH_METADATA;
79 0 : }
80 :
81 0 : void EbmlComposer::FinishMetadata()
82 : {
83 0 : if (mFlushState & FLUSH_METADATA) {
84 : // We don't remove the first element of mClusterBuffs because the
85 : // |mClusterHeaderIndex| may have value.
86 0 : mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[0]);
87 0 : mFlushState &= ~FLUSH_METADATA;
88 : }
89 0 : }
90 :
91 0 : void EbmlComposer::FinishCluster()
92 : {
93 0 : FinishMetadata();
94 0 : if (!(mFlushState & FLUSH_CLUSTER)) {
95 : // No completed cluster available.
96 0 : return;
97 : }
98 :
99 0 : MOZ_ASSERT(mClusterLengthLoc > 0);
100 : EbmlGlobal ebml;
101 : EbmlLoc ebmlLoc;
102 0 : ebmlLoc.offset = mClusterLengthLoc;
103 0 : ebml.offset = 0;
104 0 : for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) {
105 0 : ebml.offset += mClusterBuffs[i].Length();
106 : }
107 0 : ebml.buf = mClusterBuffs[mClusterHeaderIndex].Elements();
108 0 : Ebml_EndSubElement(&ebml, &ebmlLoc);
109 : // Move the mClusterBuffs data from mClusterHeaderIndex that we can skip
110 : // the metadata and the rest P-frames after ContainerWriter::FLUSH_NEEDED.
111 0 : for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) {
112 0 : mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]);
113 : }
114 :
115 0 : mClusterHeaderIndex = 0;
116 0 : mClusterLengthLoc = 0;
117 0 : mClusterBuffs.Clear();
118 0 : mFlushState &= ~FLUSH_CLUSTER;
119 : }
120 :
121 : void
122 0 : EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
123 : {
124 : EbmlGlobal ebml;
125 0 : ebml.offset = 0;
126 :
127 0 : auto frameType = aFrame->GetFrameType();
128 0 : bool flush = false;
129 0 : bool isVP8IFrame = (frameType == EncodedFrame::FrameType::VP8_I_FRAME);
130 0 : if (isVP8IFrame) {
131 0 : FinishCluster();
132 0 : flush = true;
133 : } else {
134 : // Force it to calculate timecode using signed math via cast
135 0 : int64_t timeCode = (aFrame->GetTimeStamp() / ((int) PR_USEC_PER_MSEC) - mClusterTimecode) +
136 0 : (mCodecDelay / PR_NSEC_PER_MSEC);
137 0 : if (timeCode < SHRT_MIN || timeCode > SHRT_MAX ) {
138 : // We're probably going to overflow (or underflow) the timeCode value later!
139 0 : FinishCluster();
140 0 : flush = true;
141 : }
142 : }
143 :
144 0 : auto block = mClusterBuffs.AppendElement();
145 0 : block->SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE);
146 0 : ebml.buf = block->Elements();
147 :
148 0 : if (flush) {
149 : EbmlLoc ebmlLoc;
150 0 : Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster);
151 0 : MOZ_ASSERT(mClusterBuffs.Length() > 0);
152 : // current cluster header array index
153 0 : mClusterHeaderIndex = mClusterBuffs.Length() - 1;
154 0 : mClusterLengthLoc = ebmlLoc.offset;
155 : // if timeCode didn't under/overflow before, it shouldn't after this
156 0 : mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC;
157 0 : Ebml_SerializeUnsigned(&ebml, Timecode, mClusterTimecode);
158 0 : mFlushState |= FLUSH_CLUSTER;
159 : }
160 :
161 0 : bool isOpus = (frameType == EncodedFrame::FrameType::OPUS_AUDIO_FRAME);
162 : // Can't underflow/overflow now
163 0 : int64_t timeCode = aFrame->GetTimeStamp() / ((int) PR_USEC_PER_MSEC) - mClusterTimecode;
164 0 : if (isOpus) {
165 0 : timeCode += mCodecDelay / PR_NSEC_PER_MSEC;
166 : }
167 0 : MOZ_ASSERT(timeCode >= SHRT_MIN && timeCode <= SHRT_MAX);
168 0 : writeSimpleBlock(&ebml, isOpus ? 0x2 : 0x1, static_cast<short>(timeCode), isVP8IFrame,
169 0 : 0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
170 0 : aFrame->GetFrameData().Length());
171 0 : MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE +
172 : aFrame->GetFrameData().Length(),
173 : "write more data > EBML_BUFFER_SIZE");
174 0 : block->SetLength(ebml.offset);
175 0 : }
176 :
177 : void
178 0 : EbmlComposer::SetVideoConfig(uint32_t aWidth, uint32_t aHeight,
179 : uint32_t aDisplayWidth, uint32_t aDisplayHeight)
180 : {
181 0 : MOZ_ASSERT(aWidth > 0, "Width should > 0");
182 0 : MOZ_ASSERT(aHeight > 0, "Height should > 0");
183 0 : MOZ_ASSERT(aDisplayWidth > 0, "DisplayWidth should > 0");
184 0 : MOZ_ASSERT(aDisplayHeight > 0, "DisplayHeight should > 0");
185 0 : mWidth = aWidth;
186 0 : mHeight = aHeight;
187 0 : mDisplayWidth = aDisplayWidth;
188 0 : mDisplayHeight = aDisplayHeight;
189 0 : }
190 :
191 : void
192 0 : EbmlComposer::SetAudioConfig(uint32_t aSampleFreq, uint32_t aChannels)
193 : {
194 0 : MOZ_ASSERT(aSampleFreq > 0, "SampleFreq should > 0");
195 0 : MOZ_ASSERT(aChannels > 0, "Channels should > 0");
196 0 : mSampleFreq = aSampleFreq;
197 0 : mChannels = aChannels;
198 0 : }
199 :
200 : void
201 0 : EbmlComposer::ExtractBuffer(nsTArray<nsTArray<uint8_t> >* aDestBufs,
202 : uint32_t aFlag)
203 : {
204 0 : if ((aFlag & ContainerWriter::FLUSH_NEEDED) ||
205 0 : (aFlag & ContainerWriter::GET_HEADER))
206 : {
207 0 : FinishMetadata();
208 : }
209 0 : if (aFlag & ContainerWriter::FLUSH_NEEDED)
210 : {
211 0 : FinishCluster();
212 : }
213 : // aDestBufs may have some element
214 0 : for (uint32_t i = 0; i < mClusterCanFlushBuffs.Length(); i++) {
215 0 : aDestBufs->AppendElement()->SwapElements(mClusterCanFlushBuffs[i]);
216 : }
217 0 : mClusterCanFlushBuffs.Clear();
218 0 : }
219 :
220 0 : EbmlComposer::EbmlComposer()
221 : : mFlushState(FLUSH_NONE)
222 : , mClusterHeaderIndex(0)
223 : , mClusterLengthLoc(0)
224 : , mCodecDelay(0)
225 : , mClusterTimecode(0)
226 : , mWidth(0)
227 : , mHeight(0)
228 : , mSampleFreq(0)
229 0 : , mChannels(0)
230 0 : {}
231 :
232 : } // namespace mozilla
|