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