diff options
Diffstat (limited to 'dom/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp')
-rw-r--r-- | dom/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp | 1550 |
1 files changed, 1550 insertions, 0 deletions
diff --git a/dom/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp b/dom/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp new file mode 100644 index 000000000..32a0c577b --- /dev/null +++ b/dom/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp @@ -0,0 +1,1550 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <climits> +#include "TrackMetadataBase.h" +#include "ISOMediaBoxes.h" +#include "ISOControl.h" +#include "ISOMediaWriter.h" +#include "EncodedFrameContainer.h" +#include "ISOTrackMetadata.h" +#include "MP4ESDS.h" +#include "AMRBox.h" +#include "AVCBox.h" +#include "EVRCBox.h" +#include "VideoUtils.h" + +namespace mozilla { + +// 14496-12 6.2.2 'Data Types and fields' +const uint32_t iso_matrix[] = { 0x00010000, 0, 0, + 0, 0x00010000, 0, + 0, 0, 0x40000000 }; + +uint32_t +set_sample_flags(bool aSync) +{ + std::bitset<32> flags; + flags.set(16, !aSync); + return flags.to_ulong(); +} + +Box::BoxSizeChecker::BoxSizeChecker(ISOControl* aControl, uint32_t aSize) +{ + mControl = aControl; + ori_size = mControl->GetBufPos(); + box_size = aSize; + MOZ_COUNT_CTOR(BoxSizeChecker); +} + +Box::BoxSizeChecker::~BoxSizeChecker() +{ + uint32_t cur_size = mControl->GetBufPos(); + if ((cur_size - ori_size) != box_size) { + MOZ_ASSERT(false); + } + + MOZ_COUNT_DTOR(BoxSizeChecker); +} + +nsresult +MediaDataBox::Generate(uint32_t* aBoxSize) +{ + mFirstSampleOffset = size; + mAllSampleSize = 0; + + if (mTrackType & Audio_Track) { + FragmentBuffer* frag = mControl->GetFragment(Audio_Track); + mAllSampleSize += frag->GetFirstFragmentSampleSize(); + } + if (mTrackType & Video_Track) { + FragmentBuffer* frag = mControl->GetFragment(Video_Track); + mAllSampleSize += frag->GetFirstFragmentSampleSize(); + } + + size += mAllSampleSize; + *aBoxSize = size; + return NS_OK; +} + +nsresult +MediaDataBox::Write() +{ + nsresult rv; + BoxSizeChecker checker(mControl, size); + Box::Write(); + nsTArray<uint32_t> types; + types.AppendElement(Audio_Track); + types.AppendElement(Video_Track); + + for (uint32_t l = 0; l < types.Length(); l++) { + if (mTrackType & types[l]) { + FragmentBuffer* frag = mControl->GetFragment(types[l]); + nsTArray<RefPtr<EncodedFrame>> frames; + + // Here is the last time we get fragment frames, flush it! + rv = frag->GetFirstFragment(frames, true); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t len = frames.Length(); + for (uint32_t i = 0; i < len; i++) { + nsTArray<uint8_t> frame_buffer; + frames.ElementAt(i)->SwapOutFrameData(frame_buffer); + mControl->WriteAVData(frame_buffer); + } + } + } + + return NS_OK; +} + +MediaDataBox::MediaDataBox(uint32_t aTrackType, ISOControl* aControl) + : Box(NS_LITERAL_CSTRING("mdat"), aControl) + , mAllSampleSize(0) + , mFirstSampleOffset(0) + , mTrackType(aTrackType) +{ + MOZ_COUNT_CTOR(MediaDataBox); +} + +MediaDataBox::~MediaDataBox() +{ + MOZ_COUNT_DTOR(MediaDataBox); +} + +uint32_t +TrackRunBox::fillSampleTable() +{ + uint32_t table_size = 0; + nsresult rv; + nsTArray<RefPtr<EncodedFrame>> frames; + FragmentBuffer* frag = mControl->GetFragment(mTrackType); + + rv = frag->GetFirstFragment(frames); + if (NS_FAILED(rv)) { + return 0; + } + uint32_t len = frames.Length(); + sample_info_table = MakeUnique<tbl[]>(len); + // Create sample table according to 14496-12 8.8.8.2. + for (uint32_t i = 0; i < len; i++) { + // Sample size. + sample_info_table[i].sample_size = 0; + if (flags.to_ulong() & flags_sample_size_present) { + sample_info_table[i].sample_size = frames.ElementAt(i)->GetFrameData().Length(); + mAllSampleSize += sample_info_table[i].sample_size; + table_size += sizeof(uint32_t); + } + + // Sample flags. + sample_info_table[i].sample_flags = 0; + if (flags.to_ulong() & flags_sample_flags_present) { + sample_info_table[i].sample_flags = + set_sample_flags( + (frames.ElementAt(i)->GetFrameType() == EncodedFrame::AVC_I_FRAME)); + table_size += sizeof(uint32_t); + } + + // Sample duration. + sample_info_table[i].sample_duration = 0; + if (flags.to_ulong() & flags_sample_duration_present) { + // Calculate each frame's duration, it is decided by "current frame + // timestamp - last frame timestamp". + uint64_t frame_time = 0; + if (i == 0) { + frame_time = frames.ElementAt(i)->GetTimeStamp() - + frag->GetLastFragmentLastFrameTime(); + } else { + frame_time = frames.ElementAt(i)->GetTimeStamp() - + frames.ElementAt(i - 1)->GetTimeStamp(); + // Keep the last frame time of current fagment, it will be used to calculate + // the first frame duration of next fragment. + if ((len - 1) == i) { + frag->SetLastFragmentLastFrameTime(frames.ElementAt(i)->GetTimeStamp()); + } + } + + // In TrackRunBox, there should be exactly one type, either audio or video. + MOZ_ASSERT((mTrackType & Video_Track) ^ (mTrackType & Audio_Track)); + sample_info_table[i].sample_duration = (mTrackType & Video_Track ? + frame_time * mVideoMeta->GetVideoClockRate() / USECS_PER_S : + frame_time * mAudioMeta->GetAudioSampleRate() / USECS_PER_S); + + table_size += sizeof(uint32_t); + } + + sample_info_table[i].sample_composition_time_offset = 0; + } + return table_size; +} + +nsresult +TrackRunBox::Generate(uint32_t* aBoxSize) +{ + FragmentBuffer* frag = mControl->GetFragment(mTrackType); + sample_count = frag->GetFirstFragmentSampleNumber(); + size += sizeof(sample_count); + + // data_offset needs to be updated if there is other + // TrackRunBox before this one. + if (flags.to_ulong() & flags_data_offset_present) { + data_offset = 0; + size += sizeof(data_offset); + } + size += fillSampleTable(); + + *aBoxSize = size; + + return NS_OK; +} + +nsresult +TrackRunBox::SetDataOffset(uint32_t aOffset) +{ + data_offset = aOffset; + return NS_OK; +} + +nsresult +TrackRunBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(sample_count); + if (flags.to_ulong() & flags_data_offset_present) { + mControl->Write(data_offset); + } + for (uint32_t i = 0; i < sample_count; i++) { + if (flags.to_ulong() & flags_sample_duration_present) { + mControl->Write(sample_info_table[i].sample_duration); + } + if (flags.to_ulong() & flags_sample_size_present) { + mControl->Write(sample_info_table[i].sample_size); + } + if (flags.to_ulong() & flags_sample_flags_present) { + mControl->Write(sample_info_table[i].sample_flags); + } + } + + return NS_OK; +} + +TrackRunBox::TrackRunBox(uint32_t aType, uint32_t aFlags, ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("trun"), 0, aFlags, aControl) + , sample_count(0) + , data_offset(0) + , first_sample_flags(0) + , mAllSampleSize(0) + , mTrackType(aType) +{ + MOZ_COUNT_CTOR(TrackRunBox); +} + +TrackRunBox::~TrackRunBox() +{ + MOZ_COUNT_DTOR(TrackRunBox); +} + +nsresult +TrackFragmentHeaderBox::UpdateBaseDataOffset(uint64_t aOffset) +{ + base_data_offset = aOffset; + return NS_OK; +} + +nsresult +TrackFragmentHeaderBox::Generate(uint32_t* aBoxSize) +{ + track_ID = (mTrackType == Audio_Track ? + mControl->GetTrackID(mAudioMeta->GetKind()) : + mControl->GetTrackID(mVideoMeta->GetKind())); + size += sizeof(track_ID); + + if (flags.to_ulong() & base_data_offset_present) { + // base_data_offset needs to add size of 'trun', 'tfhd' and + // header of 'mdat' later. + base_data_offset = 0; + size += sizeof(base_data_offset); + } + if (flags.to_ulong() & default_sample_duration_present) { + if (mTrackType == Video_Track) { + if (!mVideoMeta->GetVideoFrameRate()) { + // 0 means frame rate is variant, so it is wrong to write + // default_sample_duration. + MOZ_ASSERT(0); + default_sample_duration = 0; + } else { + default_sample_duration = mVideoMeta->GetVideoClockRate() / mVideoMeta->GetVideoFrameRate(); + } + } else if (mTrackType == Audio_Track) { + default_sample_duration = mAudioMeta->GetAudioFrameDuration(); + } else { + MOZ_ASSERT(0); + return NS_ERROR_FAILURE; + } + size += sizeof(default_sample_duration); + } + *aBoxSize = size; + return NS_OK; +} + +nsresult +TrackFragmentHeaderBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(track_ID); + if (flags.to_ulong() & base_data_offset_present) { + mControl->Write(base_data_offset); + } + if (flags.to_ulong() & default_sample_duration_present) { + mControl->Write(default_sample_duration); + } + return NS_OK; +} + +TrackFragmentHeaderBox::TrackFragmentHeaderBox(uint32_t aType, + uint32_t aFlags, + ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("tfhd"), 0, aFlags, aControl) + , track_ID(0) + , base_data_offset(0) + , default_sample_duration(0) +{ + mTrackType = aType; + MOZ_COUNT_CTOR(TrackFragmentHeaderBox); +} + +TrackFragmentHeaderBox::~TrackFragmentHeaderBox() +{ + MOZ_COUNT_DTOR(TrackFragmentHeaderBox); +} + +TrackFragmentBox::TrackFragmentBox(uint32_t aType, ISOControl* aControl) + : DefaultContainerImpl(NS_LITERAL_CSTRING("traf"), aControl) + , mTrackType(aType) +{ + // Flags in TrackFragmentHeaderBox. + uint32_t tf_flags = base_data_offset_present; + + // Ideally, audio encoder generates audio frame in const rate. However, some + // audio encoders don't do it so the audio frame duration needs to be checked + // here. + if ((mTrackType & Audio_Track) && mAudioMeta->GetAudioFrameDuration()) { + tf_flags |= default_sample_duration_present; + } + + boxes.AppendElement(new TrackFragmentHeaderBox(aType, tf_flags, aControl)); + + // Always adds flags_data_offset_present in each TrackRunBox, Android + // parser requires this flag to calculate the correct bitstream offset. + uint32_t tr_flags = flags_sample_size_present | flags_data_offset_present; + + // Flags in TrackRunBox. + // If there is no default sample duration exists, each frame duration needs to + // be recored in the TrackRunBox. + tr_flags |= (tf_flags & default_sample_duration_present ? 0 : flags_sample_duration_present); + + // For video, add sample_flags to record I frame. + tr_flags |= (mTrackType & Video_Track ? flags_sample_flags_present : 0); + + boxes.AppendElement(new TrackRunBox(mTrackType, tr_flags, aControl)); + MOZ_COUNT_CTOR(TrackFragmentBox); +} + +TrackFragmentBox::~TrackFragmentBox() +{ + MOZ_COUNT_DTOR(TrackFragmentBox); +} + +nsresult +MovieFragmentHeaderBox::Generate(uint32_t* aBoxSize) +{ + sequence_number = mControl->GetCurFragmentNumber(); + size += sizeof(sequence_number); + *aBoxSize = size; + return NS_OK; +} + +nsresult +MovieFragmentHeaderBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(sequence_number); + return NS_OK; +} + +MovieFragmentHeaderBox::MovieFragmentHeaderBox(uint32_t aTrackType, + ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("mfhd"), 0, 0, aControl) + , sequence_number(0) + , mTrackType(aTrackType) +{ + MOZ_COUNT_CTOR(MovieFragmentHeaderBox); +} + +MovieFragmentHeaderBox::~MovieFragmentHeaderBox() +{ + MOZ_COUNT_DTOR(MovieFragmentHeaderBox); +} + +MovieFragmentBox::MovieFragmentBox(uint32_t aType, ISOControl* aControl) + : DefaultContainerImpl(NS_LITERAL_CSTRING("moof"), aControl) + , mTrackType(aType) +{ + boxes.AppendElement(new MovieFragmentHeaderBox(mTrackType, aControl)); + + if (mTrackType & Audio_Track) { + boxes.AppendElement( + new TrackFragmentBox(Audio_Track, aControl)); + } + if (mTrackType & Video_Track) { + boxes.AppendElement( + new TrackFragmentBox(Video_Track, aControl)); + } + MOZ_COUNT_CTOR(MovieFragmentBox); +} + +MovieFragmentBox::~MovieFragmentBox() +{ + MOZ_COUNT_DTOR(MovieFragmentBox); +} + +nsresult +MovieFragmentBox::Generate(uint32_t* aBoxSize) +{ + nsresult rv = DefaultContainerImpl::Generate(aBoxSize); + NS_ENSURE_SUCCESS(rv, rv); + + // Correct data_offset if there are both audio and video track in + // this fragment. This offset means the offset in the MediaDataBox. + if (mTrackType & (Audio_Track | Video_Track)) { + nsTArray<RefPtr<MuxerOperation>> truns; + rv = Find(NS_LITERAL_CSTRING("trun"), truns); + NS_ENSURE_SUCCESS(rv, rv); + uint32_t len = truns.Length(); + uint32_t data_offset = 0; + for (uint32_t i = 0; i < len; i++) { + TrackRunBox* trun = (TrackRunBox*) truns.ElementAt(i).get(); + rv = trun->SetDataOffset(data_offset); + NS_ENSURE_SUCCESS(rv, rv); + data_offset += trun->GetAllSampleSize(); + } + } + + return NS_OK; +} + +nsresult +TrackExtendsBox::Generate(uint32_t* aBoxSize) +{ + track_ID = (mTrackType == Audio_Track ? + mControl->GetTrackID(mAudioMeta->GetKind()) : + mControl->GetTrackID(mVideoMeta->GetKind())); + + if (mTrackType == Audio_Track) { + default_sample_description_index = 1; + default_sample_duration = mAudioMeta->GetAudioFrameDuration(); + default_sample_size = mAudioMeta->GetAudioFrameSize(); + default_sample_flags = set_sample_flags(1); + } else if (mTrackType == Video_Track) { + default_sample_description_index = 1; + // Video meta data has assigned framerate, it implies that this video's + // frame rate should be fixed. + if (mVideoMeta->GetVideoFrameRate()) { + default_sample_duration = + mVideoMeta->GetVideoClockRate() / mVideoMeta->GetVideoFrameRate(); + } + default_sample_size = 0; + default_sample_flags = set_sample_flags(0); + } else { + MOZ_ASSERT(0); + return NS_ERROR_FAILURE; + } + + size += sizeof(track_ID) + + sizeof(default_sample_description_index) + + sizeof(default_sample_duration) + + sizeof(default_sample_size) + + sizeof(default_sample_flags); + + *aBoxSize = size; + + return NS_OK; +} + +nsresult +TrackExtendsBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(track_ID); + mControl->Write(default_sample_description_index); + mControl->Write(default_sample_duration); + mControl->Write(default_sample_size); + mControl->Write(default_sample_flags); + + return NS_OK; +} + +TrackExtendsBox::TrackExtendsBox(uint32_t aType, ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("trex"), 0, 0, aControl) + , track_ID(0) + , default_sample_description_index(0) + , default_sample_duration(0) + , default_sample_size(0) + , default_sample_flags(0) + , mTrackType(aType) +{ + MOZ_COUNT_CTOR(TrackExtendsBox); +} + +TrackExtendsBox::~TrackExtendsBox() +{ + MOZ_COUNT_DTOR(TrackExtendsBox); +} + +MovieExtendsBox::MovieExtendsBox(ISOControl* aControl) + : DefaultContainerImpl(NS_LITERAL_CSTRING("mvex"), aControl) +{ + if (mAudioMeta) { + boxes.AppendElement(new TrackExtendsBox(Audio_Track, aControl)); + } + if (mVideoMeta) { + boxes.AppendElement(new TrackExtendsBox(Video_Track, aControl)); + } + MOZ_COUNT_CTOR(MovieExtendsBox); +} + +MovieExtendsBox::~MovieExtendsBox() +{ + MOZ_COUNT_DTOR(MovieExtendsBox); +} + +nsresult +ChunkOffsetBox::Generate(uint32_t* aBoxSize) +{ + // We don't need time to sample table in fragmented mp4. + entry_count = 0; + size += sizeof(entry_count); + *aBoxSize = size; + return NS_OK; +} + +nsresult +ChunkOffsetBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(entry_count); + return NS_OK; +} + +ChunkOffsetBox::ChunkOffsetBox(uint32_t aType, ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("stco"), 0, 0, aControl) + , entry_count(0) +{ + MOZ_COUNT_CTOR(ChunkOffsetBox); +} + +ChunkOffsetBox::~ChunkOffsetBox() +{ + MOZ_COUNT_DTOR(ChunkOffsetBox); +} + +nsresult +SampleToChunkBox::Generate(uint32_t* aBoxSize) +{ + // We don't need time to sample table in fragmented mp4 + entry_count = 0; + size += sizeof(entry_count); + *aBoxSize = size; + return NS_OK; +} + +nsresult +SampleToChunkBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(entry_count); + return NS_OK; +} + +SampleToChunkBox::SampleToChunkBox(uint32_t aType, ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("stsc"), 0, 0, aControl) + , entry_count(0) +{ + MOZ_COUNT_CTOR(SampleToChunkBox); +} + +SampleToChunkBox::~SampleToChunkBox() +{ + MOZ_COUNT_DTOR(SampleToChunkBox); +} + +nsresult +TimeToSampleBox::Generate(uint32_t* aBoxSize) +{ + // We don't need time to sample table in fragmented mp4. + entry_count = 0; + size += sizeof(entry_count); + *aBoxSize = size; + return NS_OK; +} + +nsresult +TimeToSampleBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(entry_count); + return NS_OK; +} + +TimeToSampleBox::TimeToSampleBox(uint32_t aType, ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("stts"), 0, 0, aControl) + , entry_count(0) +{ + MOZ_COUNT_CTOR(TimeToSampleBox); +} + +TimeToSampleBox::~TimeToSampleBox() +{ + MOZ_COUNT_DTOR(TimeToSampleBox); +} + +nsresult +SampleDescriptionBox::Generate(uint32_t* aBoxSize) +{ + entry_count = 1; + size += sizeof(entry_count); + + nsresult rv; + uint32_t box_size; + rv = sample_entry_box->Generate(&box_size); + NS_ENSURE_SUCCESS(rv, rv); + size += box_size; + *aBoxSize = size; + + return NS_OK; +} + +nsresult +SampleDescriptionBox::Write() +{ + WRITE_FULLBOX(mControl, size) + nsresult rv; + mControl->Write(entry_count); + rv = sample_entry_box->Write(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +SampleDescriptionBox::SampleDescriptionBox(uint32_t aType, ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("stsd"), 0, 0, aControl) + , entry_count(0) +{ + mTrackType = aType; + + switch (mTrackType) { + case Audio_Track: + { + CreateAudioSampleEntry(sample_entry_box); + } + break; + case Video_Track: + { + CreateVideoSampleEntry(sample_entry_box); + } + break; + } + MOZ_ASSERT(sample_entry_box); + MOZ_COUNT_CTOR(SampleDescriptionBox); +} + +nsresult +SampleDescriptionBox::CreateAudioSampleEntry(RefPtr<SampleEntryBox>& aSampleEntry) +{ + if (mAudioMeta->GetKind() == TrackMetadataBase::METADATA_AMR) { + aSampleEntry = new AMRSampleEntry(mControl); + } else if (mAudioMeta->GetKind() == TrackMetadataBase::METADATA_AAC) { + aSampleEntry = new MP4AudioSampleEntry(mControl); + } else if (mAudioMeta->GetKind() == TrackMetadataBase::METADATA_EVRC) { + aSampleEntry = new EVRCSampleEntry(mControl); + } else { + MOZ_ASSERT(0); + } + return NS_OK; +} + +nsresult +SampleDescriptionBox::CreateVideoSampleEntry(RefPtr<SampleEntryBox>& aSampleEntry) +{ + if (mVideoMeta->GetKind() == TrackMetadataBase::METADATA_AVC) { + aSampleEntry = new AVCSampleEntry(mControl); + } else { + MOZ_ASSERT(0); + } + return NS_OK; +} + +SampleDescriptionBox::~SampleDescriptionBox() +{ + MOZ_COUNT_DTOR(SampleDescriptionBox); +} + +nsresult +SampleSizeBox::Generate(uint32_t* aBoxSize) +{ + size += sizeof(sample_size) + + sizeof(sample_count); + *aBoxSize = size; + return NS_OK; +} + +nsresult +SampleSizeBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(sample_size); + mControl->Write(sample_count); + return NS_OK; +} + +SampleSizeBox::SampleSizeBox(ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("stsz"), 0, 0, aControl) + , sample_size(0) + , sample_count(0) +{ + MOZ_COUNT_CTOR(SampleSizeBox); +} + +SampleSizeBox::~SampleSizeBox() +{ + MOZ_COUNT_DTOR(SampleSizeBox); +} + +SampleTableBox::SampleTableBox(uint32_t aType, ISOControl* aControl) + : DefaultContainerImpl(NS_LITERAL_CSTRING("stbl"), aControl) +{ + boxes.AppendElement(new SampleDescriptionBox(aType, aControl)); + boxes.AppendElement(new TimeToSampleBox(aType, aControl)); + boxes.AppendElement(new SampleToChunkBox(aType, aControl)); + boxes.AppendElement(new SampleSizeBox(aControl)); + boxes.AppendElement(new ChunkOffsetBox(aType, aControl)); + MOZ_COUNT_CTOR(SampleTableBox); +} + +SampleTableBox::~SampleTableBox() +{ + MOZ_COUNT_DTOR(SampleTableBox); +} + +nsresult +DataEntryUrlBox::Generate(uint32_t* aBoxSize) +{ + // location is null here, do nothing + size += location.Length(); + *aBoxSize = size; + + return NS_OK; +} + +nsresult +DataEntryUrlBox::Write() +{ + WRITE_FULLBOX(mControl, size) + return NS_OK; +} + +DataEntryUrlBox::DataEntryUrlBox() + : FullBox(NS_LITERAL_CSTRING("url "), 0, 0, (ISOControl*) nullptr) +{ + MOZ_COUNT_CTOR(DataEntryUrlBox); +} + +DataEntryUrlBox::DataEntryUrlBox(ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("url "), 0, flags_media_at_the_same_file, aControl) +{ + MOZ_COUNT_CTOR(DataEntryUrlBox); +} + +DataEntryUrlBox::DataEntryUrlBox(const DataEntryUrlBox& aBox) + : FullBox(aBox.boxType, aBox.version, aBox.flags.to_ulong(), aBox.mControl) +{ + location = aBox.location; + MOZ_COUNT_CTOR(DataEntryUrlBox); +} + +DataEntryUrlBox::~DataEntryUrlBox() +{ + MOZ_COUNT_DTOR(DataEntryUrlBox); +} + +nsresult DataReferenceBox::Generate(uint32_t* aBoxSize) +{ + entry_count = 1; // only allow on entry here + size += sizeof(uint32_t); + + for (uint32_t i = 0; i < entry_count; i++) { + uint32_t box_size = 0; + DataEntryUrlBox* url = new DataEntryUrlBox(mControl); + url->Generate(&box_size); + size += box_size; + urls.AppendElement(url); + } + + *aBoxSize = size; + + return NS_OK; +} + +nsresult DataReferenceBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(entry_count); + + for (uint32_t i = 0; i < entry_count; i++) { + urls[i]->Write(); + } + + return NS_OK; +} + +DataReferenceBox::DataReferenceBox(ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("dref"), 0, 0, aControl) + , entry_count(0) +{ + MOZ_COUNT_CTOR(DataReferenceBox); +} + +DataReferenceBox::~DataReferenceBox() +{ + MOZ_COUNT_DTOR(DataReferenceBox); +} + +DataInformationBox::DataInformationBox(ISOControl* aControl) + : DefaultContainerImpl(NS_LITERAL_CSTRING("dinf"), aControl) +{ + boxes.AppendElement(new DataReferenceBox(aControl)); + MOZ_COUNT_CTOR(DataInformationBox); +} + +DataInformationBox::~DataInformationBox() +{ + MOZ_COUNT_DTOR(DataInformationBox); +} + +nsresult +VideoMediaHeaderBox::Generate(uint32_t* aBoxSize) +{ + size += sizeof(graphicsmode) + + sizeof(opcolor); + + *aBoxSize = size; + + return NS_OK; +} + +nsresult +VideoMediaHeaderBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(graphicsmode); + mControl->WriteArray(opcolor, 3); + return NS_OK; +} + +VideoMediaHeaderBox::VideoMediaHeaderBox(ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("vmhd"), 0, 1, aControl) + , graphicsmode(0) +{ + memset(opcolor, 0 , sizeof(opcolor)); + MOZ_COUNT_CTOR(VideoMediaHeaderBox); +} + +VideoMediaHeaderBox::~VideoMediaHeaderBox() +{ + MOZ_COUNT_DTOR(VideoMediaHeaderBox); +} + +nsresult +SoundMediaHeaderBox::Generate(uint32_t* aBoxSize) +{ + balance = 0; + reserved = 0; + size += sizeof(balance) + + sizeof(reserved); + + *aBoxSize = size; + + return NS_OK; +} + +nsresult +SoundMediaHeaderBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(balance); + mControl->Write(reserved); + + return NS_OK; +} + +SoundMediaHeaderBox::SoundMediaHeaderBox(ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("smhd"), 0, 0, aControl) +{ + MOZ_COUNT_CTOR(SoundMediaHeaderBox); +} + +SoundMediaHeaderBox::~SoundMediaHeaderBox() +{ + MOZ_COUNT_DTOR(SoundMediaHeaderBox); +} + +MediaInformationBox::MediaInformationBox(uint32_t aType, ISOControl* aControl) + : DefaultContainerImpl(NS_LITERAL_CSTRING("minf"), aControl) +{ + mTrackType = aType; + + if (mTrackType == Audio_Track) { + boxes.AppendElement(new SoundMediaHeaderBox(aControl)); + } else if (mTrackType == Video_Track) { + boxes.AppendElement(new VideoMediaHeaderBox(aControl)); + } else { + MOZ_ASSERT(0); + } + + boxes.AppendElement(new DataInformationBox(aControl)); + boxes.AppendElement(new SampleTableBox(aType, aControl)); + MOZ_COUNT_CTOR(MediaInformationBox); +} + +MediaInformationBox::~MediaInformationBox() +{ + MOZ_COUNT_DTOR(MediaInformationBox); +} + +nsresult +HandlerBox::Generate(uint32_t* aBoxSize) +{ + pre_defined = 0; + if (mTrackType == Audio_Track) { + handler_type = FOURCC('s', 'o', 'u', 'n'); + } else if (mTrackType == Video_Track) { + handler_type = FOURCC('v', 'i', 'd', 'e'); + } + + size += sizeof(pre_defined) + + sizeof(handler_type) + + sizeof(reserved); + + *aBoxSize = size; + + return NS_OK; +} + +nsresult +HandlerBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(pre_defined); + mControl->Write(handler_type); + mControl->WriteArray(reserved, 3); + + return NS_OK; +} + +HandlerBox::HandlerBox(uint32_t aType, ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("hdlr"), 0, 0, aControl) + , pre_defined(0) + , handler_type(0) +{ + mTrackType = aType; + memset(reserved, 0 , sizeof(reserved)); + MOZ_COUNT_CTOR(HandlerBox); +} + +HandlerBox::~HandlerBox() +{ + MOZ_COUNT_DTOR(HandlerBox); +} + +MediaHeaderBox::MediaHeaderBox(uint32_t aType, ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("mdhd"), 0, 0, aControl) + , creation_time(0) + , modification_time(0) + , timescale(0) + , duration(0) + , pad(0) + , lang1(0) + , lang2(0) + , lang3(0) + , pre_defined(0) +{ + mTrackType = aType; + MOZ_COUNT_CTOR(MediaHeaderBox); +} + +MediaHeaderBox::~MediaHeaderBox() +{ + MOZ_COUNT_DTOR(MediaHeaderBox); +} + +uint32_t +MediaHeaderBox::GetTimeScale() +{ + if (mTrackType == Audio_Track) { + return mAudioMeta->GetAudioSampleRate(); + } + + return mVideoMeta->GetVideoClockRate(); +} + +nsresult +MediaHeaderBox::Generate(uint32_t* aBoxSize) +{ + creation_time = mControl->GetTime(); + modification_time = mControl->GetTime(); + timescale = GetTimeScale(); + duration = 0; // fragmented mp4 + + pad = 0; + lang1 = 'u' - 0x60; // "und" underdetermined language + lang2 = 'n' - 0x60; + lang3 = 'd' - 0x60; + size += (pad.size() + lang1.size() + lang2.size() + lang3.size()) / CHAR_BIT; + + pre_defined = 0; + size += sizeof(creation_time) + + sizeof(modification_time) + + sizeof(timescale) + + sizeof(duration) + + sizeof(pre_defined); + + *aBoxSize = size; + + return NS_OK; +} + +nsresult +MediaHeaderBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(creation_time); + mControl->Write(modification_time); + mControl->Write(timescale); + mControl->Write(duration); + mControl->WriteBits(pad.to_ulong(), pad.size()); + mControl->WriteBits(lang1.to_ulong(), lang1.size()); + mControl->WriteBits(lang2.to_ulong(), lang2.size()); + mControl->WriteBits(lang3.to_ulong(), lang3.size()); + mControl->Write(pre_defined); + + return NS_OK; +} + +MovieBox::MovieBox(ISOControl* aControl) + : DefaultContainerImpl(NS_LITERAL_CSTRING("moov"), aControl) +{ + boxes.AppendElement(new MovieHeaderBox(aControl)); + if (aControl->HasAudioTrack()) { + boxes.AppendElement(new TrackBox(Audio_Track, aControl)); + } + if (aControl->HasVideoTrack()) { + boxes.AppendElement(new TrackBox(Video_Track, aControl)); + } + boxes.AppendElement(new MovieExtendsBox(aControl)); + MOZ_COUNT_CTOR(MovieBox); +} + +MovieBox::~MovieBox() +{ + MOZ_COUNT_DTOR(MovieBox); +} + +nsresult +MovieHeaderBox::Generate(uint32_t* aBoxSize) +{ + creation_time = mControl->GetTime(); + modification_time = mControl->GetTime(); + timescale = GetTimeScale(); + duration = 0; // The duration is always 0 in fragmented mp4. + next_track_ID = mControl->GetNextTrackID(); + + size += sizeof(next_track_ID) + + sizeof(creation_time) + + sizeof(modification_time) + + sizeof(timescale) + + sizeof(duration) + + sizeof(rate) + + sizeof(volume) + + sizeof(reserved16) + + sizeof(reserved32) + + sizeof(matrix) + + sizeof(pre_defined); + + *aBoxSize = size; + + return NS_OK; +} + +nsresult +MovieHeaderBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(creation_time); + mControl->Write(modification_time); + mControl->Write(timescale); + mControl->Write(duration); + mControl->Write(rate); + mControl->Write(volume); + mControl->Write(reserved16); + mControl->WriteArray(reserved32, 2); + mControl->WriteArray(matrix, 9); + mControl->WriteArray(pre_defined, 6); + mControl->Write(next_track_ID); + + return NS_OK; +} + +uint32_t +MovieHeaderBox::GetTimeScale() +{ + // Only audio track in container. + if (mAudioMeta && !mVideoMeta) { + return mAudioMeta->GetAudioSampleRate(); + } + + // return video rate + return mVideoMeta->GetVideoClockRate(); +} + +MovieHeaderBox::~MovieHeaderBox() +{ + MOZ_COUNT_DTOR(MovieHeaderBox); +} + +MovieHeaderBox::MovieHeaderBox(ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("mvhd"), 0, 0, aControl) + , creation_time(0) + , modification_time(0) + , timescale(90000) + , duration(0) + , rate(0x00010000) + , volume(0x0100) + , reserved16(0) + , next_track_ID(1) +{ + memcpy(matrix, iso_matrix, sizeof(matrix)); + memset(reserved32, 0, sizeof(reserved32)); + memset(pre_defined, 0, sizeof(pre_defined)); + MOZ_COUNT_CTOR(MovieHeaderBox); +} + +TrackHeaderBox::TrackHeaderBox(uint32_t aType, ISOControl* aControl) + : FullBox(NS_LITERAL_CSTRING("tkhd"), 0, + flags_track_enabled | flags_track_in_movie | flags_track_in_preview, + aControl) + , creation_time(0) + , modification_time(0) + , track_ID(0) + , reserved(0) + , duration(0) + , layer(0) + , alternate_group(0) + , volume(0) + , reserved3(0) + , width(0) + , height(0) +{ + mTrackType = aType; + memcpy(matrix, iso_matrix, sizeof(matrix)); + memset(reserved2, 0, sizeof(reserved2)); + MOZ_COUNT_CTOR(TrackHeaderBox); +} + +TrackHeaderBox::~TrackHeaderBox() +{ + MOZ_COUNT_DTOR(TrackHeaderBox); +} + +nsresult +TrackHeaderBox::Generate(uint32_t* aBoxSize) +{ + creation_time = mControl->GetTime(); + modification_time = mControl->GetTime(); + track_ID = (mTrackType == Audio_Track ? + mControl->GetTrackID(mAudioMeta->GetKind()) : + mControl->GetTrackID(mVideoMeta->GetKind())); + // fragmented mp4 + duration = 0; + + // volume, audiotrack is always 0x0100 in 14496-12 8.3.2.2 + volume = (mTrackType == Audio_Track ? 0x0100 : 0); + + if (mTrackType == Video_Track) { + width = mVideoMeta->GetVideoDisplayWidth() << 16; + height = mVideoMeta->GetVideoDisplayHeight() << 16; + // Check display size, using the pixel size if any of them is invalid. + if (!width || !height) { + width = mVideoMeta->GetVideoWidth() << 16; + height = mVideoMeta->GetVideoHeight() << 16; + } + } + + size += sizeof(creation_time) + + sizeof(modification_time) + + sizeof(track_ID) + + sizeof(reserved) + + sizeof(duration) + + sizeof(reserved2) + + sizeof(layer) + + sizeof(alternate_group) + + sizeof(volume) + + sizeof(reserved3) + + sizeof(matrix) + + sizeof(width) + + sizeof(height); + + *aBoxSize = size; + + return NS_OK; +} + +nsresult +TrackHeaderBox::Write() +{ + WRITE_FULLBOX(mControl, size) + mControl->Write(creation_time); + mControl->Write(modification_time); + mControl->Write(track_ID); + mControl->Write(reserved); + mControl->Write(duration); + mControl->WriteArray(reserved2, 2); + mControl->Write(layer); + mControl->Write(alternate_group); + mControl->Write(volume); + mControl->Write(reserved3); + mControl->WriteArray(matrix, 9); + mControl->Write(width); + mControl->Write(height); + + return NS_OK; +} + +nsresult +FileTypeBox::Generate(uint32_t* aBoxSize) +{ + minor_version = 0; + + if (mControl->GetMuxingType() == ISOMediaWriter::TYPE_FRAG_MP4) { + if (!mControl->HasVideoTrack() && mControl->HasAudioTrack()) { + major_brand = "M4A "; + } else { + major_brand = "MP42"; + } + compatible_brands.AppendElement("mp42"); + compatible_brands.AppendElement("isom"); + } else if (mControl->GetMuxingType() == ISOMediaWriter::TYPE_FRAG_3GP) { + major_brand = "3gp9"; + // According to 3GPP TS 26.244 V12.2.0, section 5.3.4, it's recommended to + // list all compatible brands here. 3GP spec supports fragment from '3gp6'. + compatible_brands.AppendElement("3gp9"); + compatible_brands.AppendElement("3gp8"); + compatible_brands.AppendElement("3gp7"); + compatible_brands.AppendElement("3gp6"); + compatible_brands.AppendElement("isom"); + } else if (mControl->GetMuxingType() == ISOMediaWriter::TYPE_FRAG_3G2) { + major_brand = "3g2a"; + // 3GPP2 Release 0 and A and 3GPP Release 6 allow movie fragmentation + compatible_brands.AppendElement("3gp9"); + compatible_brands.AppendElement("3gp8"); + compatible_brands.AppendElement("3gp7"); + compatible_brands.AppendElement("3gp6"); + compatible_brands.AppendElement("isom"); + compatible_brands.AppendElement("3g2c"); + compatible_brands.AppendElement("3g2b"); + compatible_brands.AppendElement("3g2a"); + } else { + MOZ_ASSERT(0); + } + + size += major_brand.Length() + + sizeof(minor_version) + + compatible_brands.Length() * 4; + + *aBoxSize = size; + + return NS_OK; +} + +nsresult +FileTypeBox::Write() +{ + BoxSizeChecker checker(mControl, size); + Box::Write(); + mControl->WriteFourCC(major_brand.get()); + mControl->Write(minor_version); + uint32_t len = compatible_brands.Length(); + for (uint32_t i = 0; i < len; i++) { + mControl->WriteFourCC(compatible_brands[i].get()); + } + + return NS_OK; +} + +FileTypeBox::FileTypeBox(ISOControl* aControl) + : Box(NS_LITERAL_CSTRING("ftyp"), aControl) + , minor_version(0) +{ + MOZ_COUNT_CTOR(FileTypeBox); +} + +FileTypeBox::~FileTypeBox() +{ + MOZ_COUNT_DTOR(FileTypeBox); +} + +MediaBox::MediaBox(uint32_t aType, ISOControl* aControl) + : DefaultContainerImpl(NS_LITERAL_CSTRING("mdia"), aControl) +{ + mTrackType = aType; + boxes.AppendElement(new MediaHeaderBox(aType, aControl)); + boxes.AppendElement(new HandlerBox(aType, aControl)); + boxes.AppendElement(new MediaInformationBox(aType, aControl)); + MOZ_COUNT_CTOR(MediaBox); +} + +MediaBox::~MediaBox() +{ + MOZ_COUNT_DTOR(MediaBox); +} + +nsresult +DefaultContainerImpl::Generate(uint32_t* aBoxSize) +{ + nsresult rv; + uint32_t box_size; + uint32_t len = boxes.Length(); + for (uint32_t i = 0; i < len; i++) { + rv = boxes.ElementAt(i)->Generate(&box_size); + NS_ENSURE_SUCCESS(rv, rv); + size += box_size; + } + *aBoxSize = size; + return NS_OK; +} + +nsresult +DefaultContainerImpl::Find(const nsACString& aType, + nsTArray<RefPtr<MuxerOperation>>& aOperations) +{ + nsresult rv = Box::Find(aType, aOperations); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t len = boxes.Length(); + for (uint32_t i = 0; i < len; i++) { + rv = boxes.ElementAt(i)->Find(aType, aOperations); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; +} + +nsresult +DefaultContainerImpl::Write() +{ + BoxSizeChecker checker(mControl, size); + Box::Write(); + + nsresult rv; + uint32_t len = boxes.Length(); + for (uint32_t i = 0; i < len; i++) { + rv = boxes.ElementAt(i)->Write(); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +DefaultContainerImpl::DefaultContainerImpl(const nsACString& aType, + ISOControl* aControl) + : Box(aType, aControl) +{ +} + +nsresult +Box::Write() +{ + mControl->Write(size); + mControl->WriteFourCC(boxType.get()); + return NS_OK; +} + +nsresult +Box::Find(const nsACString& aType, nsTArray<RefPtr<MuxerOperation>>& aOperations) +{ + if (boxType == aType) { + aOperations.AppendElement(this); + } + return NS_OK; +} + +Box::Box(const nsACString& aType, ISOControl* aControl) + : size(8), mControl(aControl) +{ + MOZ_ASSERT(aType.Length() == 4); + boxType = aType; + aControl->GetAudioMetadata(mAudioMeta); + aControl->GetVideoMetadata(mVideoMeta); +} + +FullBox::FullBox(const nsACString& aType, uint8_t aVersion, uint32_t aFlags, + ISOControl* aControl) + : Box(aType, aControl) +{ + std::bitset<24> tmp_flags(aFlags); + version = aVersion; + flags = tmp_flags; + size += sizeof(version) + flags.size() / CHAR_BIT; +} + +nsresult +FullBox::Write() +{ + Box::Write(); + mControl->Write(version); + mControl->WriteBits(flags.to_ulong(), flags.size()); + return NS_OK; +} + +TrackBox::TrackBox(uint32_t aTrackType, ISOControl* aControl) + : DefaultContainerImpl(NS_LITERAL_CSTRING("trak"), aControl) +{ + boxes.AppendElement(new TrackHeaderBox(aTrackType, aControl)); + boxes.AppendElement(new MediaBox(aTrackType, aControl)); + MOZ_COUNT_CTOR(TrackBox); +} + +TrackBox::~TrackBox() +{ + MOZ_COUNT_DTOR(TrackBox); +} + +SampleEntryBox::SampleEntryBox(const nsACString& aFormat, ISOControl* aControl) + : Box(aFormat, aControl) + , data_reference_index(0) +{ + data_reference_index = 1; // There is only one data reference in each track. + size += sizeof(reserved) + + sizeof(data_reference_index); + memset(reserved, 0, sizeof(reserved)); +} + +nsresult +SampleEntryBox::Write() +{ + Box::Write(); + mControl->Write(reserved, sizeof(reserved)); + mControl->Write(data_reference_index); + return NS_OK; +} + +nsresult +AudioSampleEntry::Write() +{ + SampleEntryBox::Write(); + mControl->Write(sound_version); + mControl->Write(reserved2, sizeof(reserved2)); + mControl->Write(channels); + mControl->Write(sample_size); + mControl->Write(compressionId); + mControl->Write(packet_size); + mControl->Write(timeScale); + return NS_OK; +} + +AudioSampleEntry::AudioSampleEntry(const nsACString& aFormat, ISOControl* aControl) + : SampleEntryBox(aFormat, aControl) + , sound_version(0) + , channels(2) + , sample_size(16) + , compressionId(0) + , packet_size(0) + , timeScale(0) +{ + memset(reserved2, 0 , sizeof(reserved2)); + channels = mAudioMeta->GetAudioChannels(); + timeScale = mAudioMeta->GetAudioSampleRate() << 16; + + size += sizeof(sound_version) + + sizeof(reserved2) + + sizeof(sample_size) + + sizeof(channels) + + sizeof(packet_size) + + sizeof(compressionId) + + sizeof(timeScale); + + MOZ_COUNT_CTOR(AudioSampleEntry); +} + +AudioSampleEntry::~AudioSampleEntry() +{ + MOZ_COUNT_DTOR(AudioSampleEntry); +} + +nsresult +VisualSampleEntry::Write() +{ + SampleEntryBox::Write(); + + mControl->Write(reserved, sizeof(reserved)); + mControl->Write(width); + mControl->Write(height); + mControl->Write(horizresolution); + mControl->Write(vertresolution); + mControl->Write(reserved2); + mControl->Write(frame_count); + mControl->Write(compressorName, sizeof(compressorName)); + mControl->Write(depth); + mControl->Write(pre_defined); + + return NS_OK; +} + +VisualSampleEntry::VisualSampleEntry(const nsACString& aFormat, ISOControl* aControl) + : SampleEntryBox(aFormat, aControl) + , width(0) + , height(0) + , horizresolution(resolution_72_dpi) + , vertresolution(resolution_72_dpi) + , reserved2(0) + , frame_count(1) + , depth(video_depth) + , pre_defined(-1) +{ + memset(reserved, 0 , sizeof(reserved)); + memset(compressorName, 0 , sizeof(compressorName)); + + // both fields occupy 16 bits defined in 14496-2 6.2.3. + width = mVideoMeta->GetVideoWidth(); + height = mVideoMeta->GetVideoHeight(); + + size += sizeof(reserved) + + sizeof(width) + + sizeof(height) + + sizeof(horizresolution) + + sizeof(vertresolution) + + sizeof(reserved2) + + sizeof(frame_count) + + sizeof(compressorName) + + sizeof(depth) + + sizeof(pre_defined); + + MOZ_COUNT_CTOR(VisualSampleEntry); +} + +VisualSampleEntry::~VisualSampleEntry() +{ + MOZ_COUNT_DTOR(VisualSampleEntry); +} + +} |