|
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 <time.h> |
|
7 #include "nsAutoPtr.h" |
|
8 #include "ISOControl.h" |
|
9 #include "ISOMediaBoxes.h" |
|
10 #include "EncodedFrameContainer.h" |
|
11 |
|
12 namespace mozilla { |
|
13 |
|
14 // For MP4 creation_time and modification_time offset from January 1, 1904 to |
|
15 // January 1, 1970. |
|
16 #define iso_time_offset 2082844800 |
|
17 |
|
18 FragmentBuffer::FragmentBuffer(uint32_t aTrackType, uint32_t aFragDuration) |
|
19 : mTrackType(aTrackType) |
|
20 , mFragDuration(aFragDuration) |
|
21 , mMediaStartTime(0) |
|
22 , mFragmentNumber(0) |
|
23 , mLastFrameTimeOfLastFragment(0) |
|
24 , mEOS(false) |
|
25 { |
|
26 mFragArray.AppendElement(); |
|
27 MOZ_COUNT_CTOR(FragmentBuffer); |
|
28 } |
|
29 |
|
30 FragmentBuffer::~FragmentBuffer() |
|
31 { |
|
32 MOZ_COUNT_DTOR(FragmentBuffer); |
|
33 } |
|
34 |
|
35 bool |
|
36 FragmentBuffer::HasEnoughData() |
|
37 { |
|
38 // Audio or video frame is enough to form a moof. |
|
39 return (mFragArray.Length() > 1); |
|
40 } |
|
41 |
|
42 nsresult |
|
43 FragmentBuffer::GetCSD(nsTArray<uint8_t>& aCSD) |
|
44 { |
|
45 if (!mCSDFrame) { |
|
46 return NS_ERROR_FAILURE; |
|
47 } |
|
48 aCSD.AppendElements(mCSDFrame->GetFrameData().Elements(), |
|
49 mCSDFrame->GetFrameData().Length()); |
|
50 |
|
51 return NS_OK; |
|
52 } |
|
53 |
|
54 nsresult |
|
55 FragmentBuffer::AddFrame(EncodedFrame* aFrame) |
|
56 { |
|
57 // already EOS, it rejects all new data. |
|
58 if (mEOS) { |
|
59 MOZ_ASSERT(0); |
|
60 return NS_OK; |
|
61 } |
|
62 |
|
63 EncodedFrame::FrameType type = aFrame->GetFrameType(); |
|
64 if (type == EncodedFrame::AAC_CSD || type == EncodedFrame::AVC_CSD || |
|
65 type == EncodedFrame::AMR_AUDIO_CSD) { |
|
66 mCSDFrame = aFrame; |
|
67 // Use CSD's timestamp as the start time. Encoder should send CSD frame first |
|
68 // and then data frames. |
|
69 mMediaStartTime = aFrame->GetTimeStamp(); |
|
70 mFragmentNumber = 1; |
|
71 return NS_OK; |
|
72 } |
|
73 |
|
74 // if the timestamp is incorrect, abort it. |
|
75 if (aFrame->GetTimeStamp() < mMediaStartTime) { |
|
76 MOZ_ASSERT(false); |
|
77 return NS_ERROR_FAILURE; |
|
78 } |
|
79 |
|
80 mFragArray.LastElement().AppendElement(aFrame); |
|
81 |
|
82 // check if current fragment is reach the fragment duration. |
|
83 if ((aFrame->GetTimeStamp() - mMediaStartTime) >= (mFragDuration * mFragmentNumber)) { |
|
84 mFragArray.AppendElement(); |
|
85 mFragmentNumber++; |
|
86 } |
|
87 |
|
88 return NS_OK; |
|
89 } |
|
90 |
|
91 nsresult |
|
92 FragmentBuffer::GetFirstFragment(nsTArray<nsRefPtr<EncodedFrame>>& aFragment, |
|
93 bool aFlush) |
|
94 { |
|
95 // It should be called only if there is a complete fragment in mFragArray. |
|
96 if (mFragArray.Length() <= 1 && !mEOS) { |
|
97 MOZ_ASSERT(false); |
|
98 return NS_ERROR_FAILURE; |
|
99 } |
|
100 |
|
101 if (aFlush) { |
|
102 aFragment.SwapElements(mFragArray.ElementAt(0)); |
|
103 mFragArray.RemoveElementAt(0); |
|
104 } else { |
|
105 aFragment.AppendElements(mFragArray.ElementAt(0)); |
|
106 } |
|
107 return NS_OK; |
|
108 } |
|
109 |
|
110 uint32_t |
|
111 FragmentBuffer::GetFirstFragmentSampleNumber() |
|
112 { |
|
113 return mFragArray.ElementAt(0).Length(); |
|
114 } |
|
115 |
|
116 uint32_t |
|
117 FragmentBuffer::GetFirstFragmentSampleSize() |
|
118 { |
|
119 uint32_t size = 0; |
|
120 uint32_t len = mFragArray.ElementAt(0).Length(); |
|
121 for (uint32_t i = 0; i < len; i++) { |
|
122 size += mFragArray.ElementAt(0).ElementAt(i)->GetFrameData().Length(); |
|
123 } |
|
124 return size; |
|
125 } |
|
126 |
|
127 ISOControl::ISOControl(uint32_t aMuxingType) |
|
128 : mMuxingType(aMuxingType) |
|
129 , mAudioFragmentBuffer(nullptr) |
|
130 , mVideoFragmentBuffer(nullptr) |
|
131 , mFragNum(0) |
|
132 , mOutputSize(0) |
|
133 , mBitCount(0) |
|
134 , mBit(0) |
|
135 { |
|
136 // Create a data array for first mp4 Box, ftyp. |
|
137 mOutBuffers.SetLength(1); |
|
138 MOZ_COUNT_CTOR(ISOControl); |
|
139 } |
|
140 |
|
141 ISOControl::~ISOControl() |
|
142 { |
|
143 MOZ_COUNT_DTOR(ISOControl); |
|
144 } |
|
145 |
|
146 uint32_t |
|
147 ISOControl::GetNextTrackID() |
|
148 { |
|
149 return (mMetaArray.Length() + 1); |
|
150 } |
|
151 |
|
152 uint32_t |
|
153 ISOControl::GetTrackID(TrackMetadataBase::MetadataKind aKind) |
|
154 { |
|
155 for (uint32_t i = 0; i < mMetaArray.Length(); i++) { |
|
156 if (mMetaArray[i]->GetKind() == aKind) { |
|
157 return (i + 1); |
|
158 } |
|
159 } |
|
160 |
|
161 // Track ID shouldn't be 0. It must be something wrong here. |
|
162 MOZ_ASSERT(0); |
|
163 return 0; |
|
164 } |
|
165 |
|
166 nsresult |
|
167 ISOControl::SetMetadata(TrackMetadataBase* aTrackMeta) |
|
168 { |
|
169 if (aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AAC || |
|
170 aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AMR || |
|
171 aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AVC) { |
|
172 mMetaArray.AppendElement(aTrackMeta); |
|
173 return NS_OK; |
|
174 } |
|
175 return NS_ERROR_FAILURE; |
|
176 } |
|
177 |
|
178 nsresult |
|
179 ISOControl::GetAudioMetadata(nsRefPtr<AudioTrackMetadata>& aAudMeta) |
|
180 { |
|
181 for (uint32_t i = 0; i < mMetaArray.Length() ; i++) { |
|
182 if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AAC || |
|
183 mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AMR) { |
|
184 aAudMeta = static_cast<AudioTrackMetadata*>(mMetaArray[i].get()); |
|
185 return NS_OK; |
|
186 } |
|
187 } |
|
188 return NS_ERROR_FAILURE; |
|
189 } |
|
190 |
|
191 nsresult |
|
192 ISOControl::GetVideoMetadata(nsRefPtr<VideoTrackMetadata>& aVidMeta) |
|
193 { |
|
194 for (uint32_t i = 0; i < mMetaArray.Length() ; i++) { |
|
195 if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AVC) { |
|
196 aVidMeta = static_cast<VideoTrackMetadata*>(mMetaArray[i].get()); |
|
197 return NS_OK; |
|
198 } |
|
199 } |
|
200 return NS_ERROR_FAILURE; |
|
201 } |
|
202 |
|
203 bool |
|
204 ISOControl::HasAudioTrack() |
|
205 { |
|
206 nsRefPtr<AudioTrackMetadata> audMeta; |
|
207 GetAudioMetadata(audMeta); |
|
208 return audMeta; |
|
209 } |
|
210 |
|
211 bool |
|
212 ISOControl::HasVideoTrack() |
|
213 { |
|
214 nsRefPtr<VideoTrackMetadata> vidMeta; |
|
215 GetVideoMetadata(vidMeta); |
|
216 return vidMeta; |
|
217 } |
|
218 |
|
219 nsresult |
|
220 ISOControl::SetFragment(FragmentBuffer* aFragment) |
|
221 { |
|
222 if (aFragment->GetType() == Audio_Track) { |
|
223 mAudioFragmentBuffer = aFragment; |
|
224 } else { |
|
225 mVideoFragmentBuffer = aFragment; |
|
226 } |
|
227 return NS_OK; |
|
228 } |
|
229 |
|
230 FragmentBuffer* |
|
231 ISOControl::GetFragment(uint32_t aType) |
|
232 { |
|
233 if (aType == Audio_Track) { |
|
234 return mAudioFragmentBuffer; |
|
235 } else if (aType == Video_Track){ |
|
236 return mVideoFragmentBuffer; |
|
237 } |
|
238 MOZ_ASSERT(0); |
|
239 return nullptr; |
|
240 } |
|
241 |
|
242 nsresult |
|
243 ISOControl::GetBufs(nsTArray<nsTArray<uint8_t>>* aOutputBufs) |
|
244 { |
|
245 uint32_t len = mOutBuffers.Length(); |
|
246 for (uint32_t i = 0; i < len; i++) { |
|
247 mOutBuffers[i].SwapElements(*aOutputBufs->AppendElement()); |
|
248 } |
|
249 return FlushBuf(); |
|
250 } |
|
251 |
|
252 nsresult |
|
253 ISOControl::FlushBuf() |
|
254 { |
|
255 mOutBuffers.SetLength(1); |
|
256 return NS_OK; |
|
257 } |
|
258 |
|
259 uint32_t |
|
260 ISOControl::WriteAVData(nsTArray<uint8_t>& aArray) |
|
261 { |
|
262 MOZ_ASSERT(!mBitCount); |
|
263 |
|
264 uint32_t len = aArray.Length(); |
|
265 if (!len) { |
|
266 return 0; |
|
267 } |
|
268 |
|
269 mOutputSize += len; |
|
270 |
|
271 // The last element already has data, allocated a new element for pointer |
|
272 // swapping. |
|
273 if (mOutBuffers.LastElement().Length()) { |
|
274 mOutBuffers.AppendElement(); |
|
275 } |
|
276 // Swap the video/audio data pointer. |
|
277 mOutBuffers.LastElement().SwapElements(aArray); |
|
278 // Following data could be boxes, so appending a new uint8_t array here. |
|
279 mOutBuffers.AppendElement(); |
|
280 |
|
281 return len; |
|
282 } |
|
283 |
|
284 uint32_t |
|
285 ISOControl::WriteBits(uint64_t aBits, size_t aNumBits) |
|
286 { |
|
287 uint8_t output_byte = 0; |
|
288 |
|
289 MOZ_ASSERT(aNumBits <= 64); |
|
290 // TODO: rewritten following with bitset? |
|
291 for (size_t i = aNumBits; i > 0; i--) { |
|
292 mBit |= (((aBits >> (i - 1)) & 1) << (8 - ++mBitCount)); |
|
293 if (mBitCount == 8) { |
|
294 Write(&mBit, sizeof(uint8_t)); |
|
295 mBit = 0; |
|
296 mBitCount = 0; |
|
297 output_byte++; |
|
298 } |
|
299 } |
|
300 return output_byte; |
|
301 } |
|
302 |
|
303 uint32_t |
|
304 ISOControl::Write(uint8_t* aBuf, uint32_t aSize) |
|
305 { |
|
306 mOutBuffers.LastElement().AppendElements(aBuf, aSize); |
|
307 mOutputSize += aSize; |
|
308 return aSize; |
|
309 } |
|
310 |
|
311 uint32_t |
|
312 ISOControl::Write(uint8_t aData) |
|
313 { |
|
314 MOZ_ASSERT(!mBitCount); |
|
315 Write((uint8_t*)&aData, sizeof(uint8_t)); |
|
316 return sizeof(uint8_t); |
|
317 } |
|
318 |
|
319 uint32_t |
|
320 ISOControl::GetBufPos() |
|
321 { |
|
322 uint32_t len = mOutBuffers.Length(); |
|
323 uint32_t pos = 0; |
|
324 for (uint32_t i = 0; i < len; i++) { |
|
325 pos += mOutBuffers.ElementAt(i).Length(); |
|
326 } |
|
327 return pos; |
|
328 } |
|
329 |
|
330 uint32_t |
|
331 ISOControl::WriteFourCC(const char* aType) |
|
332 { |
|
333 // Bit operation should be aligned to byte before writing any byte data. |
|
334 MOZ_ASSERT(!mBitCount); |
|
335 |
|
336 uint32_t size = strlen(aType); |
|
337 if (size == 4) { |
|
338 return Write((uint8_t*)aType, size); |
|
339 } |
|
340 |
|
341 return 0; |
|
342 } |
|
343 |
|
344 nsresult |
|
345 ISOControl::GenerateFtyp() |
|
346 { |
|
347 nsresult rv; |
|
348 uint32_t size; |
|
349 nsAutoPtr<FileTypeBox> type_box(new FileTypeBox(this)); |
|
350 rv = type_box->Generate(&size); |
|
351 NS_ENSURE_SUCCESS(rv, rv); |
|
352 rv = type_box->Write(); |
|
353 NS_ENSURE_SUCCESS(rv, rv); |
|
354 return NS_OK; |
|
355 } |
|
356 |
|
357 nsresult |
|
358 ISOControl::GenerateMoov() |
|
359 { |
|
360 nsresult rv; |
|
361 uint32_t size; |
|
362 nsAutoPtr<MovieBox> moov_box(new MovieBox(this)); |
|
363 rv = moov_box->Generate(&size); |
|
364 NS_ENSURE_SUCCESS(rv, rv); |
|
365 rv = moov_box->Write(); |
|
366 NS_ENSURE_SUCCESS(rv, rv); |
|
367 return NS_OK; |
|
368 } |
|
369 |
|
370 nsresult |
|
371 ISOControl::GenerateMoof(uint32_t aTrackType) |
|
372 { |
|
373 mFragNum++; |
|
374 |
|
375 nsresult rv; |
|
376 uint32_t size; |
|
377 uint64_t first_sample_offset = mOutputSize; |
|
378 nsAutoPtr<MovieFragmentBox> moof_box(new MovieFragmentBox(aTrackType, this)); |
|
379 nsAutoPtr<MediaDataBox> mdat_box(new MediaDataBox(aTrackType, this)); |
|
380 |
|
381 rv = moof_box->Generate(&size); |
|
382 NS_ENSURE_SUCCESS(rv, rv); |
|
383 first_sample_offset += size; |
|
384 rv = mdat_box->Generate(&size); |
|
385 NS_ENSURE_SUCCESS(rv, rv); |
|
386 first_sample_offset += mdat_box->FirstSampleOffsetInMediaDataBox(); |
|
387 |
|
388 // correct offset info |
|
389 nsTArray<nsRefPtr<MuxerOperation>> tfhds; |
|
390 rv = moof_box->Find(NS_LITERAL_CSTRING("tfhd"), tfhds); |
|
391 NS_ENSURE_SUCCESS(rv, rv); |
|
392 uint32_t len = tfhds.Length(); |
|
393 for (uint32_t i = 0; i < len; i++) { |
|
394 TrackFragmentHeaderBox* tfhd = (TrackFragmentHeaderBox*) tfhds.ElementAt(i).get(); |
|
395 rv = tfhd->UpdateBaseDataOffset(first_sample_offset); |
|
396 NS_ENSURE_SUCCESS(rv, rv); |
|
397 } |
|
398 |
|
399 rv = moof_box->Write(); |
|
400 NS_ENSURE_SUCCESS(rv, rv); |
|
401 rv = mdat_box->Write(); |
|
402 NS_ENSURE_SUCCESS(rv, rv); |
|
403 |
|
404 return NS_OK; |
|
405 } |
|
406 |
|
407 uint32_t |
|
408 ISOControl::GetTime() |
|
409 { |
|
410 return (uint64_t)time(nullptr) + iso_time_offset; |
|
411 } |
|
412 |
|
413 } |