Fri, 16 Jan 2015 04:50:19 +0100
Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32
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 "libmkv/EbmlIDs.h"
8 #include "libmkv/EbmlWriter.h"
9 #include "libmkv/WebMElement.h"
10 #include "prtime.h"
12 namespace mozilla {
14 // Timecode scale in nanoseconds
15 static const unsigned long TIME_CODE_SCALE = 1000000;
16 // The WebM header size without audio CodecPrivateData
17 static const int32_t DEFAULT_HEADER_SIZE = 1024;
19 void EbmlComposer::GenerateHeader()
20 {
21 // Write the EBML header.
22 EbmlGlobal ebml;
23 // The WEbM header default size usually smaller than 1k.
24 nsAutoArrayPtr<uint8_t> buffer(new uint8_t[DEFAULT_HEADER_SIZE +
25 mCodecPrivateData.Length()]);
26 ebml.buf = buffer.get();
27 ebml.offset = 0;
28 writeHeader(&ebml);
29 {
30 EbmlLoc segEbmlLoc, ebmlLocseg, ebmlLoc;
31 Ebml_StartSubElement(&ebml, &segEbmlLoc, Segment);
32 {
33 Ebml_StartSubElement(&ebml, &ebmlLocseg, SeekHead);
34 // Todo: We don't know the exact sizes of encoded data and
35 // ignore this section.
36 Ebml_EndSubElement(&ebml, &ebmlLocseg);
37 writeSegmentInformation(&ebml, &ebmlLoc, TIME_CODE_SCALE, 0);
38 {
39 EbmlLoc trackLoc;
40 Ebml_StartSubElement(&ebml, &trackLoc, Tracks);
41 {
42 // Video
43 if (mWidth > 0 && mHeight > 0) {
44 writeVideoTrack(&ebml, 0x1, 0, "V_VP8",
45 mWidth, mHeight,
46 mDisplayWidth, mDisplayHeight, mFrameRate);
47 }
48 // Audio
49 if (mCodecPrivateData.Length() > 0) {
50 writeAudioTrack(&ebml, 0x2, 0x0, "A_VORBIS", mSampleFreq,
51 mChannels, mCodecPrivateData.Elements(),
52 mCodecPrivateData.Length());
53 }
54 }
55 Ebml_EndSubElement(&ebml, &trackLoc);
56 }
57 }
58 // The Recording length is unknown and
59 // ignore write the whole Segment element size
60 }
61 MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(),
62 "write more data > EBML_BUFFER_SIZE");
63 auto block = mClusterBuffs.AppendElement();
64 block->SetLength(ebml.offset);
65 memcpy(block->Elements(), ebml.buf, ebml.offset);
66 mFlushState |= FLUSH_METADATA;
67 }
69 void EbmlComposer::FinishMetadata()
70 {
71 if (mFlushState & FLUSH_METADATA) {
72 // We don't remove the first element of mClusterBuffs because the
73 // |mClusterHeaderIndex| may have value.
74 mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[0]);
75 mFlushState &= ~FLUSH_METADATA;
76 }
77 }
79 void EbmlComposer::FinishCluster()
80 {
81 FinishMetadata();
82 if (!(mFlushState & FLUSH_CLUSTER)) {
83 // No completed cluster available.
84 return;
85 }
87 MOZ_ASSERT(mClusterLengthLoc > 0);
88 EbmlGlobal ebml;
89 EbmlLoc ebmlLoc;
90 ebmlLoc.offset = mClusterLengthLoc;
91 ebml.offset = mClusterBuffs[mClusterHeaderIndex].Length();
92 ebml.buf = mClusterBuffs[mClusterHeaderIndex].Elements();
93 Ebml_EndSubElement(&ebml, &ebmlLoc);
94 // Move the mClusterBuffs data from mClusterHeaderIndex that we can skip
95 // the metadata and the rest P-frames after ContainerWriter::FLUSH_NEEDED.
96 for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) {
97 mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]);
98 }
100 mClusterHeaderIndex = 0;
101 mClusterLengthLoc = 0;
102 mClusterBuffs.Clear();
103 mFlushState &= ~FLUSH_CLUSTER;
104 }
106 void
107 EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
108 {
109 EbmlGlobal ebml;
110 ebml.offset = 0;
112 if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
113 FinishCluster();
114 }
116 auto block = mClusterBuffs.AppendElement();
117 block->SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE);
118 ebml.buf = block->Elements();
120 if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
121 EbmlLoc ebmlLoc;
122 Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster);
123 MOZ_ASSERT(mClusterBuffs.Length() > 0);
124 // current cluster header array index
125 mClusterHeaderIndex = mClusterBuffs.Length() - 1;
126 mClusterLengthLoc = ebmlLoc.offset;
127 mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC;
128 Ebml_SerializeUnsigned(&ebml, Timecode, mClusterTimecode);
129 mFlushState |= FLUSH_CLUSTER;
130 }
132 if (aFrame->GetFrameType() != EncodedFrame::FrameType::VORBIS_AUDIO_FRAME) {
133 short timeCode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC
134 - mClusterTimecode;
135 writeSimpleBlock(&ebml, 0x1, timeCode, aFrame->GetFrameType() ==
136 EncodedFrame::FrameType::VP8_I_FRAME,
137 0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
138 aFrame->GetFrameData().Length());
139 } else {
140 writeSimpleBlock(&ebml, 0x2, 0, false,
141 0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
142 aFrame->GetFrameData().Length());
143 }
144 MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE +
145 aFrame->GetFrameData().Length(),
146 "write more data > EBML_BUFFER_SIZE");
147 block->SetLength(ebml.offset);
148 }
150 void
151 EbmlComposer::SetVideoConfig(uint32_t aWidth, uint32_t aHeight,
152 uint32_t aDisplayWidth, uint32_t aDisplayHeight,
153 float aFrameRate)
154 {
155 MOZ_ASSERT(aWidth > 0, "Width should > 0");
156 MOZ_ASSERT(aHeight > 0, "Height should > 0");
157 MOZ_ASSERT(aDisplayWidth > 0, "DisplayWidth should > 0");
158 MOZ_ASSERT(aDisplayHeight > 0, "DisplayHeight should > 0");
159 MOZ_ASSERT(aFrameRate > 0, "FrameRate should > 0");
160 mWidth = aWidth;
161 mHeight = aHeight;
162 mDisplayWidth = aDisplayWidth;
163 mDisplayHeight = aDisplayHeight;
164 mFrameRate = aFrameRate;
165 }
167 void
168 EbmlComposer::SetAudioConfig(uint32_t aSampleFreq, uint32_t aChannels,
169 uint32_t aBitDepth)
170 {
171 MOZ_ASSERT(aSampleFreq > 0, "SampleFreq should > 0");
172 MOZ_ASSERT(aBitDepth > 0, "BitDepth should > 0");
173 MOZ_ASSERT(aChannels > 0, "Channels should > 0");
174 mSampleFreq = aSampleFreq;
175 mBitDepth = aBitDepth;
176 mChannels = aChannels;
177 }
179 void
180 EbmlComposer::ExtractBuffer(nsTArray<nsTArray<uint8_t> >* aDestBufs,
181 uint32_t aFlag)
182 {
183 if ((aFlag & ContainerWriter::FLUSH_NEEDED) ||
184 (aFlag & ContainerWriter::GET_HEADER))
185 {
186 FinishMetadata();
187 }
188 if (aFlag & ContainerWriter::FLUSH_NEEDED)
189 {
190 FinishCluster();
191 }
192 // aDestBufs may have some element
193 for (uint32_t i = 0; i < mClusterCanFlushBuffs.Length(); i++) {
194 aDestBufs->AppendElement()->SwapElements(mClusterCanFlushBuffs[i]);
195 }
196 mClusterCanFlushBuffs.Clear();
197 }
199 EbmlComposer::EbmlComposer()
200 : mFlushState(FLUSH_NONE)
201 , mClusterHeaderIndex(0)
202 , mClusterLengthLoc(0)
203 , mClusterTimecode(0)
204 , mWidth(0)
205 , mHeight(0)
206 , mFrameRate(0)
207 , mSampleFreq(0)
208 , mBitDepth(0)
209 , mChannels(0)
210 {}
212 }