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
FragmentBuffer(uint32_t aTrackType,uint32_t aFragDuration)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
~FragmentBuffer()30 FragmentBuffer::~FragmentBuffer()
31 {
32 MOZ_COUNT_DTOR(FragmentBuffer);
33 }
34
35 bool
HasEnoughData()36 FragmentBuffer::HasEnoughData()
37 {
38 // Audio or video frame is enough to form a moof.
39 return (mFragArray.Length() > 1);
40 }
41
42 nsresult
GetCSD(nsTArray<uint8_t> & aCSD)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
AddFrame(EncodedFrame * aFrame)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 || type == EncodedFrame::EVRC_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
GetFirstFragment(nsTArray<RefPtr<EncodedFrame>> & aFragment,bool aFlush)92 FragmentBuffer::GetFirstFragment(nsTArray<RefPtr<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
GetFirstFragmentSampleNumber()111 FragmentBuffer::GetFirstFragmentSampleNumber()
112 {
113 return mFragArray.ElementAt(0).Length();
114 }
115
116 uint32_t
GetFirstFragmentSampleSize()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
ISOControl(uint32_t aMuxingType)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
~ISOControl()141 ISOControl::~ISOControl()
142 {
143 MOZ_COUNT_DTOR(ISOControl);
144 }
145
146 uint32_t
GetNextTrackID()147 ISOControl::GetNextTrackID()
148 {
149 return (mMetaArray.Length() + 1);
150 }
151
152 uint32_t
GetTrackID(TrackMetadataBase::MetadataKind aKind)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
SetMetadata(TrackMetadataBase * aTrackMeta)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 aTrackMeta->GetKind() == TrackMetadataBase::METADATA_EVRC) {
173 mMetaArray.AppendElement(aTrackMeta);
174 return NS_OK;
175 }
176 return NS_ERROR_FAILURE;
177 }
178
179 nsresult
GetAudioMetadata(RefPtr<AudioTrackMetadata> & aAudMeta)180 ISOControl::GetAudioMetadata(RefPtr<AudioTrackMetadata>& aAudMeta)
181 {
182 for (uint32_t i = 0; i < mMetaArray.Length() ; i++) {
183 if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AAC ||
184 mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AMR ||
185 mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_EVRC) {
186 aAudMeta = static_cast<AudioTrackMetadata*>(mMetaArray[i].get());
187 return NS_OK;
188 }
189 }
190 return NS_ERROR_FAILURE;
191 }
192
193 nsresult
GetVideoMetadata(RefPtr<VideoTrackMetadata> & aVidMeta)194 ISOControl::GetVideoMetadata(RefPtr<VideoTrackMetadata>& aVidMeta)
195 {
196 for (uint32_t i = 0; i < mMetaArray.Length() ; i++) {
197 if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AVC) {
198 aVidMeta = static_cast<VideoTrackMetadata*>(mMetaArray[i].get());
199 return NS_OK;
200 }
201 }
202 return NS_ERROR_FAILURE;
203 }
204
205 bool
HasAudioTrack()206 ISOControl::HasAudioTrack()
207 {
208 RefPtr<AudioTrackMetadata> audMeta;
209 GetAudioMetadata(audMeta);
210 return audMeta;
211 }
212
213 bool
HasVideoTrack()214 ISOControl::HasVideoTrack()
215 {
216 RefPtr<VideoTrackMetadata> vidMeta;
217 GetVideoMetadata(vidMeta);
218 return vidMeta;
219 }
220
221 nsresult
SetFragment(FragmentBuffer * aFragment)222 ISOControl::SetFragment(FragmentBuffer* aFragment)
223 {
224 if (aFragment->GetType() == Audio_Track) {
225 mAudioFragmentBuffer = aFragment;
226 } else {
227 mVideoFragmentBuffer = aFragment;
228 }
229 return NS_OK;
230 }
231
232 FragmentBuffer*
GetFragment(uint32_t aType)233 ISOControl::GetFragment(uint32_t aType)
234 {
235 if (aType == Audio_Track) {
236 return mAudioFragmentBuffer;
237 } else if (aType == Video_Track){
238 return mVideoFragmentBuffer;
239 }
240 MOZ_ASSERT(0);
241 return nullptr;
242 }
243
244 nsresult
GetBufs(nsTArray<nsTArray<uint8_t>> * aOutputBufs)245 ISOControl::GetBufs(nsTArray<nsTArray<uint8_t>>* aOutputBufs)
246 {
247 uint32_t len = mOutBuffers.Length();
248 for (uint32_t i = 0; i < len; i++) {
249 mOutBuffers[i].SwapElements(*aOutputBufs->AppendElement());
250 }
251 return FlushBuf();
252 }
253
254 nsresult
FlushBuf()255 ISOControl::FlushBuf()
256 {
257 mOutBuffers.SetLength(1);
258 return NS_OK;
259 }
260
261 uint32_t
WriteAVData(nsTArray<uint8_t> & aArray)262 ISOControl::WriteAVData(nsTArray<uint8_t>& aArray)
263 {
264 MOZ_ASSERT(!mBitCount);
265
266 uint32_t len = aArray.Length();
267 if (!len) {
268 return 0;
269 }
270
271 mOutputSize += len;
272
273 // The last element already has data, allocated a new element for pointer
274 // swapping.
275 if (mOutBuffers.LastElement().Length()) {
276 mOutBuffers.AppendElement();
277 }
278 // Swap the video/audio data pointer.
279 mOutBuffers.LastElement().SwapElements(aArray);
280 // Following data could be boxes, so appending a new uint8_t array here.
281 mOutBuffers.AppendElement();
282
283 return len;
284 }
285
286 uint32_t
WriteBits(uint64_t aBits,size_t aNumBits)287 ISOControl::WriteBits(uint64_t aBits, size_t aNumBits)
288 {
289 uint8_t output_byte = 0;
290
291 MOZ_ASSERT(aNumBits <= 64);
292 // TODO: rewritten following with bitset?
293 for (size_t i = aNumBits; i > 0; i--) {
294 mBit |= (((aBits >> (i - 1)) & 1) << (8 - ++mBitCount));
295 if (mBitCount == 8) {
296 Write(&mBit, sizeof(uint8_t));
297 mBit = 0;
298 mBitCount = 0;
299 output_byte++;
300 }
301 }
302 return output_byte;
303 }
304
305 uint32_t
Write(uint8_t * aBuf,uint32_t aSize)306 ISOControl::Write(uint8_t* aBuf, uint32_t aSize)
307 {
308 mOutBuffers.LastElement().AppendElements(aBuf, aSize);
309 mOutputSize += aSize;
310 return aSize;
311 }
312
313 uint32_t
Write(uint8_t aData)314 ISOControl::Write(uint8_t aData)
315 {
316 MOZ_ASSERT(!mBitCount);
317 Write((uint8_t*)&aData, sizeof(uint8_t));
318 return sizeof(uint8_t);
319 }
320
321 uint32_t
GetBufPos()322 ISOControl::GetBufPos()
323 {
324 uint32_t len = mOutBuffers.Length();
325 uint32_t pos = 0;
326 for (uint32_t i = 0; i < len; i++) {
327 pos += mOutBuffers.ElementAt(i).Length();
328 }
329 return pos;
330 }
331
332 uint32_t
WriteFourCC(const char * aType)333 ISOControl::WriteFourCC(const char* aType)
334 {
335 // Bit operation should be aligned to byte before writing any byte data.
336 MOZ_ASSERT(!mBitCount);
337
338 uint32_t size = strlen(aType);
339 if (size == 4) {
340 return Write((uint8_t*)aType, size);
341 }
342
343 return 0;
344 }
345
346 nsresult
GenerateFtyp()347 ISOControl::GenerateFtyp()
348 {
349 nsresult rv;
350 uint32_t size;
351 nsAutoPtr<FileTypeBox> type_box(new FileTypeBox(this));
352 rv = type_box->Generate(&size);
353 NS_ENSURE_SUCCESS(rv, rv);
354 rv = type_box->Write();
355 NS_ENSURE_SUCCESS(rv, rv);
356 return NS_OK;
357 }
358
359 nsresult
GenerateMoov()360 ISOControl::GenerateMoov()
361 {
362 nsresult rv;
363 uint32_t size;
364 nsAutoPtr<MovieBox> moov_box(new MovieBox(this));
365 rv = moov_box->Generate(&size);
366 NS_ENSURE_SUCCESS(rv, rv);
367 rv = moov_box->Write();
368 NS_ENSURE_SUCCESS(rv, rv);
369 return NS_OK;
370 }
371
372 nsresult
GenerateMoof(uint32_t aTrackType)373 ISOControl::GenerateMoof(uint32_t aTrackType)
374 {
375 mFragNum++;
376
377 nsresult rv;
378 uint32_t size;
379 uint64_t first_sample_offset = mOutputSize;
380 nsAutoPtr<MovieFragmentBox> moof_box(new MovieFragmentBox(aTrackType, this));
381 nsAutoPtr<MediaDataBox> mdat_box(new MediaDataBox(aTrackType, this));
382
383 rv = moof_box->Generate(&size);
384 NS_ENSURE_SUCCESS(rv, rv);
385 first_sample_offset += size;
386 rv = mdat_box->Generate(&size);
387 NS_ENSURE_SUCCESS(rv, rv);
388 first_sample_offset += mdat_box->FirstSampleOffsetInMediaDataBox();
389
390 // correct offset info
391 nsTArray<RefPtr<MuxerOperation>> tfhds;
392 rv = moof_box->Find(NS_LITERAL_CSTRING("tfhd"), tfhds);
393 NS_ENSURE_SUCCESS(rv, rv);
394 uint32_t len = tfhds.Length();
395 for (uint32_t i = 0; i < len; i++) {
396 TrackFragmentHeaderBox* tfhd = (TrackFragmentHeaderBox*) tfhds.ElementAt(i).get();
397 rv = tfhd->UpdateBaseDataOffset(first_sample_offset);
398 NS_ENSURE_SUCCESS(rv, rv);
399 }
400
401 rv = moof_box->Write();
402 NS_ENSURE_SUCCESS(rv, rv);
403 rv = mdat_box->Write();
404 NS_ENSURE_SUCCESS(rv, rv);
405
406 return NS_OK;
407 }
408
409 uint32_t
GetTime()410 ISOControl::GetTime()
411 {
412 return (uint64_t)time(nullptr) + iso_time_offset;
413 }
414
415 }
416