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