content/media/encoder/fmp4_muxer/ISOControl.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial