1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/encoder/fmp4_muxer/ISOControl.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,413 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include <time.h> 1.10 +#include "nsAutoPtr.h" 1.11 +#include "ISOControl.h" 1.12 +#include "ISOMediaBoxes.h" 1.13 +#include "EncodedFrameContainer.h" 1.14 + 1.15 +namespace mozilla { 1.16 + 1.17 +// For MP4 creation_time and modification_time offset from January 1, 1904 to 1.18 +// January 1, 1970. 1.19 +#define iso_time_offset 2082844800 1.20 + 1.21 +FragmentBuffer::FragmentBuffer(uint32_t aTrackType, uint32_t aFragDuration) 1.22 + : mTrackType(aTrackType) 1.23 + , mFragDuration(aFragDuration) 1.24 + , mMediaStartTime(0) 1.25 + , mFragmentNumber(0) 1.26 + , mLastFrameTimeOfLastFragment(0) 1.27 + , mEOS(false) 1.28 +{ 1.29 + mFragArray.AppendElement(); 1.30 + MOZ_COUNT_CTOR(FragmentBuffer); 1.31 +} 1.32 + 1.33 +FragmentBuffer::~FragmentBuffer() 1.34 +{ 1.35 + MOZ_COUNT_DTOR(FragmentBuffer); 1.36 +} 1.37 + 1.38 +bool 1.39 +FragmentBuffer::HasEnoughData() 1.40 +{ 1.41 + // Audio or video frame is enough to form a moof. 1.42 + return (mFragArray.Length() > 1); 1.43 +} 1.44 + 1.45 +nsresult 1.46 +FragmentBuffer::GetCSD(nsTArray<uint8_t>& aCSD) 1.47 +{ 1.48 + if (!mCSDFrame) { 1.49 + return NS_ERROR_FAILURE; 1.50 + } 1.51 + aCSD.AppendElements(mCSDFrame->GetFrameData().Elements(), 1.52 + mCSDFrame->GetFrameData().Length()); 1.53 + 1.54 + return NS_OK; 1.55 +} 1.56 + 1.57 +nsresult 1.58 +FragmentBuffer::AddFrame(EncodedFrame* aFrame) 1.59 +{ 1.60 + // already EOS, it rejects all new data. 1.61 + if (mEOS) { 1.62 + MOZ_ASSERT(0); 1.63 + return NS_OK; 1.64 + } 1.65 + 1.66 + EncodedFrame::FrameType type = aFrame->GetFrameType(); 1.67 + if (type == EncodedFrame::AAC_CSD || type == EncodedFrame::AVC_CSD || 1.68 + type == EncodedFrame::AMR_AUDIO_CSD) { 1.69 + mCSDFrame = aFrame; 1.70 + // Use CSD's timestamp as the start time. Encoder should send CSD frame first 1.71 + // and then data frames. 1.72 + mMediaStartTime = aFrame->GetTimeStamp(); 1.73 + mFragmentNumber = 1; 1.74 + return NS_OK; 1.75 + } 1.76 + 1.77 + // if the timestamp is incorrect, abort it. 1.78 + if (aFrame->GetTimeStamp() < mMediaStartTime) { 1.79 + MOZ_ASSERT(false); 1.80 + return NS_ERROR_FAILURE; 1.81 + } 1.82 + 1.83 + mFragArray.LastElement().AppendElement(aFrame); 1.84 + 1.85 + // check if current fragment is reach the fragment duration. 1.86 + if ((aFrame->GetTimeStamp() - mMediaStartTime) >= (mFragDuration * mFragmentNumber)) { 1.87 + mFragArray.AppendElement(); 1.88 + mFragmentNumber++; 1.89 + } 1.90 + 1.91 + return NS_OK; 1.92 +} 1.93 + 1.94 +nsresult 1.95 +FragmentBuffer::GetFirstFragment(nsTArray<nsRefPtr<EncodedFrame>>& aFragment, 1.96 + bool aFlush) 1.97 +{ 1.98 + // It should be called only if there is a complete fragment in mFragArray. 1.99 + if (mFragArray.Length() <= 1 && !mEOS) { 1.100 + MOZ_ASSERT(false); 1.101 + return NS_ERROR_FAILURE; 1.102 + } 1.103 + 1.104 + if (aFlush) { 1.105 + aFragment.SwapElements(mFragArray.ElementAt(0)); 1.106 + mFragArray.RemoveElementAt(0); 1.107 + } else { 1.108 + aFragment.AppendElements(mFragArray.ElementAt(0)); 1.109 + } 1.110 + return NS_OK; 1.111 +} 1.112 + 1.113 +uint32_t 1.114 +FragmentBuffer::GetFirstFragmentSampleNumber() 1.115 +{ 1.116 + return mFragArray.ElementAt(0).Length(); 1.117 +} 1.118 + 1.119 +uint32_t 1.120 +FragmentBuffer::GetFirstFragmentSampleSize() 1.121 +{ 1.122 + uint32_t size = 0; 1.123 + uint32_t len = mFragArray.ElementAt(0).Length(); 1.124 + for (uint32_t i = 0; i < len; i++) { 1.125 + size += mFragArray.ElementAt(0).ElementAt(i)->GetFrameData().Length(); 1.126 + } 1.127 + return size; 1.128 +} 1.129 + 1.130 +ISOControl::ISOControl(uint32_t aMuxingType) 1.131 + : mMuxingType(aMuxingType) 1.132 + , mAudioFragmentBuffer(nullptr) 1.133 + , mVideoFragmentBuffer(nullptr) 1.134 + , mFragNum(0) 1.135 + , mOutputSize(0) 1.136 + , mBitCount(0) 1.137 + , mBit(0) 1.138 +{ 1.139 + // Create a data array for first mp4 Box, ftyp. 1.140 + mOutBuffers.SetLength(1); 1.141 + MOZ_COUNT_CTOR(ISOControl); 1.142 +} 1.143 + 1.144 +ISOControl::~ISOControl() 1.145 +{ 1.146 + MOZ_COUNT_DTOR(ISOControl); 1.147 +} 1.148 + 1.149 +uint32_t 1.150 +ISOControl::GetNextTrackID() 1.151 +{ 1.152 + return (mMetaArray.Length() + 1); 1.153 +} 1.154 + 1.155 +uint32_t 1.156 +ISOControl::GetTrackID(TrackMetadataBase::MetadataKind aKind) 1.157 +{ 1.158 + for (uint32_t i = 0; i < mMetaArray.Length(); i++) { 1.159 + if (mMetaArray[i]->GetKind() == aKind) { 1.160 + return (i + 1); 1.161 + } 1.162 + } 1.163 + 1.164 + // Track ID shouldn't be 0. It must be something wrong here. 1.165 + MOZ_ASSERT(0); 1.166 + return 0; 1.167 +} 1.168 + 1.169 +nsresult 1.170 +ISOControl::SetMetadata(TrackMetadataBase* aTrackMeta) 1.171 +{ 1.172 + if (aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AAC || 1.173 + aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AMR || 1.174 + aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AVC) { 1.175 + mMetaArray.AppendElement(aTrackMeta); 1.176 + return NS_OK; 1.177 + } 1.178 + return NS_ERROR_FAILURE; 1.179 +} 1.180 + 1.181 +nsresult 1.182 +ISOControl::GetAudioMetadata(nsRefPtr<AudioTrackMetadata>& aAudMeta) 1.183 +{ 1.184 + for (uint32_t i = 0; i < mMetaArray.Length() ; i++) { 1.185 + if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AAC || 1.186 + mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AMR) { 1.187 + aAudMeta = static_cast<AudioTrackMetadata*>(mMetaArray[i].get()); 1.188 + return NS_OK; 1.189 + } 1.190 + } 1.191 + return NS_ERROR_FAILURE; 1.192 +} 1.193 + 1.194 +nsresult 1.195 +ISOControl::GetVideoMetadata(nsRefPtr<VideoTrackMetadata>& aVidMeta) 1.196 +{ 1.197 + for (uint32_t i = 0; i < mMetaArray.Length() ; i++) { 1.198 + if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AVC) { 1.199 + aVidMeta = static_cast<VideoTrackMetadata*>(mMetaArray[i].get()); 1.200 + return NS_OK; 1.201 + } 1.202 + } 1.203 + return NS_ERROR_FAILURE; 1.204 +} 1.205 + 1.206 +bool 1.207 +ISOControl::HasAudioTrack() 1.208 +{ 1.209 + nsRefPtr<AudioTrackMetadata> audMeta; 1.210 + GetAudioMetadata(audMeta); 1.211 + return audMeta; 1.212 +} 1.213 + 1.214 +bool 1.215 +ISOControl::HasVideoTrack() 1.216 +{ 1.217 + nsRefPtr<VideoTrackMetadata> vidMeta; 1.218 + GetVideoMetadata(vidMeta); 1.219 + return vidMeta; 1.220 +} 1.221 + 1.222 +nsresult 1.223 +ISOControl::SetFragment(FragmentBuffer* aFragment) 1.224 +{ 1.225 + if (aFragment->GetType() == Audio_Track) { 1.226 + mAudioFragmentBuffer = aFragment; 1.227 + } else { 1.228 + mVideoFragmentBuffer = aFragment; 1.229 + } 1.230 + return NS_OK; 1.231 +} 1.232 + 1.233 +FragmentBuffer* 1.234 +ISOControl::GetFragment(uint32_t aType) 1.235 +{ 1.236 + if (aType == Audio_Track) { 1.237 + return mAudioFragmentBuffer; 1.238 + } else if (aType == Video_Track){ 1.239 + return mVideoFragmentBuffer; 1.240 + } 1.241 + MOZ_ASSERT(0); 1.242 + return nullptr; 1.243 +} 1.244 + 1.245 +nsresult 1.246 +ISOControl::GetBufs(nsTArray<nsTArray<uint8_t>>* aOutputBufs) 1.247 +{ 1.248 + uint32_t len = mOutBuffers.Length(); 1.249 + for (uint32_t i = 0; i < len; i++) { 1.250 + mOutBuffers[i].SwapElements(*aOutputBufs->AppendElement()); 1.251 + } 1.252 + return FlushBuf(); 1.253 +} 1.254 + 1.255 +nsresult 1.256 +ISOControl::FlushBuf() 1.257 +{ 1.258 + mOutBuffers.SetLength(1); 1.259 + return NS_OK; 1.260 +} 1.261 + 1.262 +uint32_t 1.263 +ISOControl::WriteAVData(nsTArray<uint8_t>& aArray) 1.264 +{ 1.265 + MOZ_ASSERT(!mBitCount); 1.266 + 1.267 + uint32_t len = aArray.Length(); 1.268 + if (!len) { 1.269 + return 0; 1.270 + } 1.271 + 1.272 + mOutputSize += len; 1.273 + 1.274 + // The last element already has data, allocated a new element for pointer 1.275 + // swapping. 1.276 + if (mOutBuffers.LastElement().Length()) { 1.277 + mOutBuffers.AppendElement(); 1.278 + } 1.279 + // Swap the video/audio data pointer. 1.280 + mOutBuffers.LastElement().SwapElements(aArray); 1.281 + // Following data could be boxes, so appending a new uint8_t array here. 1.282 + mOutBuffers.AppendElement(); 1.283 + 1.284 + return len; 1.285 +} 1.286 + 1.287 +uint32_t 1.288 +ISOControl::WriteBits(uint64_t aBits, size_t aNumBits) 1.289 +{ 1.290 + uint8_t output_byte = 0; 1.291 + 1.292 + MOZ_ASSERT(aNumBits <= 64); 1.293 + // TODO: rewritten following with bitset? 1.294 + for (size_t i = aNumBits; i > 0; i--) { 1.295 + mBit |= (((aBits >> (i - 1)) & 1) << (8 - ++mBitCount)); 1.296 + if (mBitCount == 8) { 1.297 + Write(&mBit, sizeof(uint8_t)); 1.298 + mBit = 0; 1.299 + mBitCount = 0; 1.300 + output_byte++; 1.301 + } 1.302 + } 1.303 + return output_byte; 1.304 +} 1.305 + 1.306 +uint32_t 1.307 +ISOControl::Write(uint8_t* aBuf, uint32_t aSize) 1.308 +{ 1.309 + mOutBuffers.LastElement().AppendElements(aBuf, aSize); 1.310 + mOutputSize += aSize; 1.311 + return aSize; 1.312 +} 1.313 + 1.314 +uint32_t 1.315 +ISOControl::Write(uint8_t aData) 1.316 +{ 1.317 + MOZ_ASSERT(!mBitCount); 1.318 + Write((uint8_t*)&aData, sizeof(uint8_t)); 1.319 + return sizeof(uint8_t); 1.320 +} 1.321 + 1.322 +uint32_t 1.323 +ISOControl::GetBufPos() 1.324 +{ 1.325 + uint32_t len = mOutBuffers.Length(); 1.326 + uint32_t pos = 0; 1.327 + for (uint32_t i = 0; i < len; i++) { 1.328 + pos += mOutBuffers.ElementAt(i).Length(); 1.329 + } 1.330 + return pos; 1.331 +} 1.332 + 1.333 +uint32_t 1.334 +ISOControl::WriteFourCC(const char* aType) 1.335 +{ 1.336 + // Bit operation should be aligned to byte before writing any byte data. 1.337 + MOZ_ASSERT(!mBitCount); 1.338 + 1.339 + uint32_t size = strlen(aType); 1.340 + if (size == 4) { 1.341 + return Write((uint8_t*)aType, size); 1.342 + } 1.343 + 1.344 + return 0; 1.345 +} 1.346 + 1.347 +nsresult 1.348 +ISOControl::GenerateFtyp() 1.349 +{ 1.350 + nsresult rv; 1.351 + uint32_t size; 1.352 + nsAutoPtr<FileTypeBox> type_box(new FileTypeBox(this)); 1.353 + rv = type_box->Generate(&size); 1.354 + NS_ENSURE_SUCCESS(rv, rv); 1.355 + rv = type_box->Write(); 1.356 + NS_ENSURE_SUCCESS(rv, rv); 1.357 + return NS_OK; 1.358 +} 1.359 + 1.360 +nsresult 1.361 +ISOControl::GenerateMoov() 1.362 +{ 1.363 + nsresult rv; 1.364 + uint32_t size; 1.365 + nsAutoPtr<MovieBox> moov_box(new MovieBox(this)); 1.366 + rv = moov_box->Generate(&size); 1.367 + NS_ENSURE_SUCCESS(rv, rv); 1.368 + rv = moov_box->Write(); 1.369 + NS_ENSURE_SUCCESS(rv, rv); 1.370 + return NS_OK; 1.371 +} 1.372 + 1.373 +nsresult 1.374 +ISOControl::GenerateMoof(uint32_t aTrackType) 1.375 +{ 1.376 + mFragNum++; 1.377 + 1.378 + nsresult rv; 1.379 + uint32_t size; 1.380 + uint64_t first_sample_offset = mOutputSize; 1.381 + nsAutoPtr<MovieFragmentBox> moof_box(new MovieFragmentBox(aTrackType, this)); 1.382 + nsAutoPtr<MediaDataBox> mdat_box(new MediaDataBox(aTrackType, this)); 1.383 + 1.384 + rv = moof_box->Generate(&size); 1.385 + NS_ENSURE_SUCCESS(rv, rv); 1.386 + first_sample_offset += size; 1.387 + rv = mdat_box->Generate(&size); 1.388 + NS_ENSURE_SUCCESS(rv, rv); 1.389 + first_sample_offset += mdat_box->FirstSampleOffsetInMediaDataBox(); 1.390 + 1.391 + // correct offset info 1.392 + nsTArray<nsRefPtr<MuxerOperation>> tfhds; 1.393 + rv = moof_box->Find(NS_LITERAL_CSTRING("tfhd"), tfhds); 1.394 + NS_ENSURE_SUCCESS(rv, rv); 1.395 + uint32_t len = tfhds.Length(); 1.396 + for (uint32_t i = 0; i < len; i++) { 1.397 + TrackFragmentHeaderBox* tfhd = (TrackFragmentHeaderBox*) tfhds.ElementAt(i).get(); 1.398 + rv = tfhd->UpdateBaseDataOffset(first_sample_offset); 1.399 + NS_ENSURE_SUCCESS(rv, rv); 1.400 + } 1.401 + 1.402 + rv = moof_box->Write(); 1.403 + NS_ENSURE_SUCCESS(rv, rv); 1.404 + rv = mdat_box->Write(); 1.405 + NS_ENSURE_SUCCESS(rv, rv); 1.406 + 1.407 + return NS_OK; 1.408 +} 1.409 + 1.410 +uint32_t 1.411 +ISOControl::GetTime() 1.412 +{ 1.413 + return (uint64_t)time(nullptr) + iso_time_offset; 1.414 +} 1.415 + 1.416 +}