|
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 "libmkv/EbmlIDs.h" |
|
8 #include "libmkv/EbmlWriter.h" |
|
9 #include "libmkv/WebMElement.h" |
|
10 #include "prtime.h" |
|
11 |
|
12 namespace mozilla { |
|
13 |
|
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; |
|
18 |
|
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 } |
|
68 |
|
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 } |
|
78 |
|
79 void EbmlComposer::FinishCluster() |
|
80 { |
|
81 FinishMetadata(); |
|
82 if (!(mFlushState & FLUSH_CLUSTER)) { |
|
83 // No completed cluster available. |
|
84 return; |
|
85 } |
|
86 |
|
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 } |
|
99 |
|
100 mClusterHeaderIndex = 0; |
|
101 mClusterLengthLoc = 0; |
|
102 mClusterBuffs.Clear(); |
|
103 mFlushState &= ~FLUSH_CLUSTER; |
|
104 } |
|
105 |
|
106 void |
|
107 EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame) |
|
108 { |
|
109 EbmlGlobal ebml; |
|
110 ebml.offset = 0; |
|
111 |
|
112 if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) { |
|
113 FinishCluster(); |
|
114 } |
|
115 |
|
116 auto block = mClusterBuffs.AppendElement(); |
|
117 block->SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE); |
|
118 ebml.buf = block->Elements(); |
|
119 |
|
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 } |
|
131 |
|
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 } |
|
149 |
|
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 } |
|
166 |
|
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 } |
|
178 |
|
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 } |
|
198 |
|
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 {} |
|
211 |
|
212 } |