diff --git a/packager/media/base/buffer_reader.cc b/packager/media/base/buffer_reader.cc index 7189ea1a66..4e593550af 100644 --- a/packager/media/base/buffer_reader.cc +++ b/packager/media/base/buffer_reader.cc @@ -53,6 +53,15 @@ bool BufferReader::ReadToVector(std::vector* vec, size_t count) { return true; } +bool BufferReader::ReadToString(std::string* str, size_t size) { + DCHECK(str); + if (!HasBytes(size)) + return false; + str->assign(buf_ + pos_, buf_ + pos_ + size); + pos_ += size; + return true; +} + bool BufferReader::SkipBytes(size_t num_bytes) { if (!HasBytes(num_bytes)) return false; diff --git a/packager/media/base/buffer_reader.h b/packager/media/base/buffer_reader.h index 2d85a7764f..46b615780a 100644 --- a/packager/media/base/buffer_reader.h +++ b/packager/media/base/buffer_reader.h @@ -9,6 +9,7 @@ #include +#include #include #include "packager/base/compiler_specific.h" @@ -53,6 +54,7 @@ class BufferReader { /// @} bool ReadToVector(std::vector* t, size_t count) WARN_UNUSED_RESULT; + bool ReadToString(std::string* str, size_t size) WARN_UNUSED_RESULT; /// Advance the stream by this many bytes. /// @return false if there are not enough bytes in the buffer, true otherwise. diff --git a/packager/media/formats/mp4/box_buffer.h b/packager/media/formats/mp4/box_buffer.h index 4a20b498bb..349b1b592e 100644 --- a/packager/media/formats/mp4/box_buffer.h +++ b/packager/media/formats/mp4/box_buffer.h @@ -7,6 +7,8 @@ #ifndef MEDIA_FORMATS_MP4_BOX_BUFFER_H_ #define MEDIA_FORMATS_MP4_BOX_BUFFER_H_ +#include + #include "packager/base/compiler_specific.h" #include "packager/media/base/buffer_writer.h" #include "packager/media/formats/mp4/box.h" @@ -55,6 +57,14 @@ class BoxBuffer { return writer_->Size(); } + /// @return In read mode, return the number of bytes left in the box. + /// In write mode, return 0. + size_t BytesLeft() const { + if (reader_) + return reader_->size() - reader_->pos(); + return 0; + } + /// @name Read/write integers of various sizes and signedness. /// @{ bool ReadWriteUInt8(uint8_t* v) { @@ -120,9 +130,21 @@ class BoxBuffer { if (reader_) return reader_->ReadToVector(vector, count); DCHECK_EQ(vector->size(), count); - writer_->AppendVector(*vector); + writer_->AppendArray(vector_as_array(vector), count); return true; } + + /// Reads @a size characters from the buffer and sets it to str. + /// Writes @a str to the buffer. Write mode ignores @a size. + bool ReadWriteString(std::string* str, size_t size) { + if (reader_) + return reader_->ReadToString(str, size); + DCHECK_EQ(str->size(), size); + writer_->AppendArray(reinterpret_cast(str->data()), + str->size()); + return true; + } + bool ReadWriteFourCC(FourCC* fourcc) { if (reader_) return reader_->ReadFourCC(fourcc); diff --git a/packager/media/formats/mp4/box_definitions.cc b/packager/media/formats/mp4/box_definitions.cc index 13a18b4f2b..d13294c023 100644 --- a/packager/media/formats/mp4/box_definitions.cc +++ b/packager/media/formats/mp4/box_definitions.cc @@ -25,6 +25,7 @@ const uint8_t kUnityMatrix[] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Default entries for HandlerReference box. const char kVideoHandlerName[] = "VideoHandler"; const char kAudioHandlerName[] = "SoundHandler"; +const char kTextHandlerName[] = "TextHandler"; // Default values for VideoSampleEntry box. const uint32_t kVideoResolution = 0x00480000; // 72 dpi. @@ -36,6 +37,10 @@ const char kAvcCompressorName[] = "\012AVC Coding"; const char kHevcCompressorName[] = "\013HEVC Coding"; const char kVpcCompressorName[] = "\012VPC Coding"; +// Using negative value as "not set". It is very unlikely that 2^31 cues happen +// at once. +const int kCueSourceIdNotSet = -1; + // Utility functions to check if the 64bit integers can fit in 32bit integer. bool IsFitIn32Bits(uint64_t a) { return a <= std::numeric_limits::max(); @@ -377,10 +382,20 @@ FourCC SampleDescription::BoxType() const { return FOURCC_STSD; } bool SampleDescription::ReadWriteInternal(BoxBuffer* buffer) { uint32_t count = 0; - if (type == kVideo) - count = video_entries.size(); - else - count = audio_entries.size(); + switch (type) { + case kVideo: + count = video_entries.size(); + break; + case kAudio: + count = audio_entries.size(); + break; + case kText: + count = wvtt_entries.size(); + break; + default: + NOTIMPLEMENTED() << "SampleDecryption type " << type + << " is not handled. Skipping."; + } RCHECK(ReadWriteHeaderInternal(buffer) && buffer->ReadWriteUInt32(&count)); @@ -397,6 +412,9 @@ bool SampleDescription::ReadWriteInternal(BoxBuffer* buffer) { } else if (type == kAudio) { RCHECK(reader->ReadAllChildren(&audio_entries)); RCHECK(audio_entries.size() == count); + } else if (type == kText) { + RCHECK(reader->ReadAllChildren(&wvtt_entries)); + RCHECK(wvtt_entries.size() == count); } } else { DCHECK_LT(0u, count); @@ -406,6 +424,9 @@ bool SampleDescription::ReadWriteInternal(BoxBuffer* buffer) { } else if (type == kAudio) { for (uint32_t i = 0; i < count; ++i) RCHECK(buffer->ReadWriteChild(&audio_entries[i])); + } else if (type == kText) { + for (uint32_t i = 0; i < count; ++i) + RCHECK(buffer->ReadWriteChild(&wvtt_entries[i])); } else { NOTIMPLEMENTED(); } @@ -803,6 +824,10 @@ bool HandlerReference::ReadWriteInternal(BoxBuffer* buffer) { hdlr_type = FOURCC_SOUN; handler_name.assign(kAudioHandlerName, kAudioHandlerName + arraysize(kAudioHandlerName)); + } else if (type == kText) { + hdlr_type = FOURCC_TEXT; + handler_name.assign(kTextHandlerName, + kTextHandlerName + arraysize(kTextHandlerName)); } else { NOTIMPLEMENTED(); return false; @@ -828,9 +853,15 @@ bool HandlerReference::ReadWriteInternal(BoxBuffer* buffer) { } uint32_t HandlerReference::ComputeSizeInternal() { - return HeaderSize() + kFourCCSize + 16 + // 16 bytes Reserved - (type == kVideo ? sizeof(kVideoHandlerName) - : sizeof(kAudioHandlerName)); + uint32_t box_size = HeaderSize() + kFourCCSize + 16; // 16 bytes Reserved + if (type == kVideo) { + box_size += sizeof(kVideoHandlerName); + } else if (type == kAudio) { + box_size += sizeof(kAudioHandlerName); + } else { + box_size += sizeof(kTextHandlerName); + } + return box_size; } CodecConfigurationRecord::CodecConfigurationRecord() : box_type(FOURCC_NULL) {} @@ -844,7 +875,7 @@ FourCC CodecConfigurationRecord::BoxType() const { bool CodecConfigurationRecord::ReadWriteInternal(BoxBuffer* buffer) { RCHECK(ReadWriteHeaderInternal(buffer)); if (buffer->Reading()) { - RCHECK(buffer->ReadWriteVector(&data, buffer->Size() - buffer->Pos())); + RCHECK(buffer->ReadWriteVector(&data, buffer->BytesLeft())); } else { RCHECK(buffer->ReadWriteVector(&data, data.size())); } @@ -996,7 +1027,7 @@ bool ElementaryStreamDescriptor::ReadWriteInternal(BoxBuffer* buffer) { RCHECK(ReadWriteHeaderInternal(buffer)); if (buffer->Reading()) { std::vector data; - RCHECK(buffer->ReadWriteVector(&data, buffer->Size() - buffer->Pos())); + RCHECK(buffer->ReadWriteVector(&data, buffer->BytesLeft())); RCHECK(es_descriptor.Parse(data)); if (es_descriptor.IsAAC()) { RCHECK(aac_audio_specific_config.Parse( @@ -1104,6 +1135,68 @@ uint32_t AudioSampleEntry::ComputeSizeInternal() { 4; // 4 bytes predefined. } +WebVTTConfigurationBox::WebVTTConfigurationBox() {} +WebVTTConfigurationBox::~WebVTTConfigurationBox() {} + +FourCC WebVTTConfigurationBox::BoxType() const { + return FOURCC_vttC; +} + +bool WebVTTConfigurationBox::ReadWriteInternal(BoxBuffer* buffer) { + RCHECK(ReadWriteHeaderInternal(buffer)); + return buffer->ReadWriteString( + &config, + buffer->Reading() ? buffer->BytesLeft() : config.size()); +} + +uint32_t WebVTTConfigurationBox::ComputeSizeInternal() { + return HeaderSize() + config.size(); +} + +WebVTTSourceLabelBox::WebVTTSourceLabelBox() {} +WebVTTSourceLabelBox::~WebVTTSourceLabelBox() {} + +FourCC WebVTTSourceLabelBox::BoxType() const { + return FOURCC_vlab; +} + +bool WebVTTSourceLabelBox::ReadWriteInternal(BoxBuffer* buffer) { + RCHECK(ReadWriteHeaderInternal(buffer)); + return buffer->ReadWriteString(&source_label, buffer->Reading() + ? buffer->BytesLeft() + : source_label.size()); +} + +uint32_t WebVTTSourceLabelBox::ComputeSizeInternal() { + if (source_label.empty()) + return 0; + return HeaderSize() + source_label.size(); +} + +WVTTSampleEntry::WVTTSampleEntry() {} +WVTTSampleEntry::~WVTTSampleEntry() {} + +FourCC WVTTSampleEntry::BoxType() const { + return FOURCC_wvtt; +} + +bool WVTTSampleEntry::ReadWriteInternal(BoxBuffer* buffer) { + // TODO(rkuroiwa): Handle the optional MPEG4BitRateBox. + RCHECK(ReadWriteHeaderInternal(buffer) && + buffer->IgnoreBytes(6) && // reserved for SampleEntry. + buffer->ReadWriteUInt16(&data_reference_index) && + buffer->PrepareChildren() && + buffer->ReadWriteChild(&config) && + buffer->ReadWriteChild(&label)); + return true; +} + +uint32_t WVTTSampleEntry::ComputeSizeInternal() { + // 6 for the (anonymous) reserved bytes for SampleEntry class. + return HeaderSize() + 6 + sizeof(data_reference_index) + + config.ComputeSize() + label.ComputeSize(); +} + MediaHeader::MediaHeader() : creation_time(0), modification_time(0), timescale(0), duration(0) { language[0] = 0; @@ -1192,6 +1285,19 @@ uint32_t SoundMediaHeader::ComputeSizeInternal() { return HeaderSize() + sizeof(balance) + sizeof(uint16_t); } +SubtitleMediaHeader::SubtitleMediaHeader() {} +SubtitleMediaHeader::~SubtitleMediaHeader() {} + +FourCC SubtitleMediaHeader::BoxType() const { return FOURCC_sthd; } + +bool SubtitleMediaHeader::ReadWriteInternal(BoxBuffer* buffer) { + return ReadWriteHeaderInternal(buffer); +} + +uint32_t SubtitleMediaHeader::ComputeSizeInternal() { + return HeaderSize(); +} + DataEntryUrl::DataEntryUrl() { const uint32_t kDataEntryUrlFlags = 1; flags = kDataEntryUrlFlags; @@ -1260,12 +1366,19 @@ bool MediaInformation::ReadWriteInternal(BoxBuffer* buffer) { buffer->PrepareChildren() && buffer->ReadWriteChild(&dinf) && buffer->ReadWriteChild(&sample_table)); - if (sample_table.description.type == kVideo) - RCHECK(buffer->ReadWriteChild(&vmhd)); - else if (sample_table.description.type == kAudio) - RCHECK(buffer->ReadWriteChild(&smhd)); - else - NOTIMPLEMENTED(); + switch (sample_table.description.type) { + case kVideo: + RCHECK(buffer->ReadWriteChild(&vmhd)); + break; + case kAudio: + RCHECK(buffer->ReadWriteChild(&smhd)); + break; + case kText: + RCHECK(buffer->ReadWriteChild(&sthd)); + break; + default: + NOTIMPLEMENTED(); + } // Hint is not supported for now. return true; } @@ -1273,10 +1386,19 @@ bool MediaInformation::ReadWriteInternal(BoxBuffer* buffer) { uint32_t MediaInformation::ComputeSizeInternal() { uint32_t box_size = HeaderSize() + dinf.ComputeSize() + sample_table.ComputeSize(); - if (sample_table.description.type == kVideo) - box_size += vmhd.ComputeSize(); - else if (sample_table.description.type == kAudio) - box_size += smhd.ComputeSize(); + switch (sample_table.description.type) { + case kVideo: + box_size += vmhd.ComputeSize(); + break; + case kAudio: + box_size += smhd.ComputeSize(); + break; + case kText: + box_size += sthd.ComputeSize(); + break; + default: + NOTIMPLEMENTED(); + } return box_size; } @@ -1912,6 +2034,154 @@ uint32_t MediaData::ComputeSizeInternal() { return HeaderSize() + data_size; } +CueSourceIDBox::CueSourceIDBox() : source_id(kCueSourceIdNotSet) {} +CueSourceIDBox::~CueSourceIDBox() {} + +FourCC CueSourceIDBox::BoxType() const { return FOURCC_vsid; } + +bool CueSourceIDBox::ReadWriteInternal(BoxBuffer* buffer) { + RCHECK(ReadWriteHeaderInternal(buffer) && buffer->ReadWriteInt32(&source_id)); + return true; +} + +uint32_t CueSourceIDBox::ComputeSizeInternal() { + if (source_id == kCueSourceIdNotSet) + return 0; + return HeaderSize() + sizeof(source_id); +} + +CueTimeBox::CueTimeBox() {} +CueTimeBox::~CueTimeBox() {} + +FourCC CueTimeBox::BoxType() const { + return FOURCC_ctim; +} + +bool CueTimeBox::ReadWriteInternal(BoxBuffer* buffer) { + RCHECK(ReadWriteHeaderInternal(buffer)); + return buffer->ReadWriteString( + &cue_current_time, + buffer->Reading() ? buffer->BytesLeft() : cue_current_time.size()); +} + +uint32_t CueTimeBox::ComputeSizeInternal() { + if (cue_current_time.empty()) + return 0; + return HeaderSize() + cue_current_time.size(); +} + +CueIDBox::CueIDBox() {} +CueIDBox::~CueIDBox() {} + +FourCC CueIDBox::BoxType() const { + return FOURCC_iden; +} + +bool CueIDBox::ReadWriteInternal(BoxBuffer* buffer) { + RCHECK(ReadWriteHeaderInternal(buffer)); + return buffer->ReadWriteString( + &cue_id, buffer->Reading() ? buffer->BytesLeft() : cue_id.size()); +} + +uint32_t CueIDBox::ComputeSizeInternal() { + if (cue_id.empty()) + return 0; + return HeaderSize() + cue_id.size(); +} + +CueSettingsBox::CueSettingsBox() {} +CueSettingsBox::~CueSettingsBox() {} + +FourCC CueSettingsBox::BoxType() const { + return FOURCC_sttg; +} + +bool CueSettingsBox::ReadWriteInternal(BoxBuffer* buffer) { + RCHECK(ReadWriteHeaderInternal(buffer)); + return buffer->ReadWriteString( + &settings, buffer->Reading() ? buffer->BytesLeft() : settings.size()); +} + +uint32_t CueSettingsBox::ComputeSizeInternal() { + if (settings.empty()) + return 0; + return HeaderSize() + settings.size(); +} + +CuePayloadBox::CuePayloadBox() {} +CuePayloadBox::~CuePayloadBox() {} + +FourCC CuePayloadBox::BoxType() const { + return FOURCC_payl; +} + +bool CuePayloadBox::ReadWriteInternal(BoxBuffer* buffer) { + RCHECK(ReadWriteHeaderInternal(buffer)); + return buffer->ReadWriteString( + &cue_text, buffer->Reading() ? buffer->BytesLeft() : cue_text.size()); +} + +uint32_t CuePayloadBox::ComputeSizeInternal() { + return HeaderSize() + cue_text.size(); +} + +VTTEmptyCueBox::VTTEmptyCueBox() {} +VTTEmptyCueBox::~VTTEmptyCueBox() {} + +FourCC VTTEmptyCueBox::BoxType() const { + return FOURCC_vtte; +} + +bool VTTEmptyCueBox::ReadWriteInternal(BoxBuffer* buffer) { + return ReadWriteHeaderInternal(buffer); +} + +uint32_t VTTEmptyCueBox::ComputeSizeInternal() { + return HeaderSize(); +} + +VTTAdditionalTextBox::VTTAdditionalTextBox() {} +VTTAdditionalTextBox::~VTTAdditionalTextBox() {} + +FourCC VTTAdditionalTextBox::BoxType() const { + return FOURCC_vtta; +} + +bool VTTAdditionalTextBox::ReadWriteInternal(BoxBuffer* buffer) { + RCHECK(ReadWriteHeaderInternal(buffer)); + return buffer->ReadWriteString( + &cue_additional_text, + buffer->Reading() ? buffer->BytesLeft() : cue_additional_text.size()); +} + +uint32_t VTTAdditionalTextBox::ComputeSizeInternal() { + return HeaderSize() + cue_additional_text.size(); +} + +VTTCueBox::VTTCueBox() {} +VTTCueBox::~VTTCueBox() {} + +FourCC VTTCueBox::BoxType() const { + return FOURCC_vttc; +} + +bool VTTCueBox::ReadWriteInternal(BoxBuffer* buffer) { + RCHECK(ReadWriteHeaderInternal(buffer) && + buffer->PrepareChildren() && + buffer->ReadWriteChild(&cue_source_id) && + buffer->ReadWriteChild(&cue_id) && + buffer->ReadWriteChild(&cue_time) && + buffer->ReadWriteChild(&cue_settings) && + buffer->ReadWriteChild(&cue_payload)); + return true; +} + +uint32_t VTTCueBox::ComputeSizeInternal() { + return HeaderSize() + cue_source_id.ComputeSize() + cue_id.ComputeSize() + + cue_time.ComputeSize() + cue_settings.ComputeSize() + + cue_payload.ComputeSize(); +} + } // namespace mp4 } // namespace media } // namespace edash_packager diff --git a/packager/media/formats/mp4/box_definitions.h b/packager/media/formats/mp4/box_definitions.h index ffabc5404d..cb4f58be24 100644 --- a/packager/media/formats/mp4/box_definitions.h +++ b/packager/media/formats/mp4/box_definitions.h @@ -23,7 +23,8 @@ enum TrackType { kInvalid = 0, kVideo, kAudio, - kHint + kHint, + kText, }; class BoxBuffer; @@ -233,12 +234,33 @@ struct AudioSampleEntry : Box { DTSSpecific ddts; }; +struct WebVTTConfigurationBox : Box { + DECLARE_BOX_METHODS(WebVTTConfigurationBox); + std::string config; +}; + +struct WebVTTSourceLabelBox : Box { + DECLARE_BOX_METHODS(WebVTTSourceLabelBox); + std::string source_label; +}; + +struct WVTTSampleEntry : Box { + DECLARE_BOX_METHODS(WVTTSampleEntry); + + uint16_t data_reference_index; + + WebVTTConfigurationBox config; + WebVTTSourceLabelBox label; + // Optional MPEG4BitRateBox. +}; + struct SampleDescription : FullBox { DECLARE_BOX_METHODS(SampleDescription); TrackType type; std::vector video_entries; std::vector audio_entries; + std::vector wvtt_entries; }; struct DecodingTime { @@ -358,6 +380,10 @@ struct SoundMediaHeader : FullBox { uint16_t balance; }; +struct SubtitleMediaHeader : FullBox { + DECLARE_BOX_METHODS(SubtitleMediaHeader); +}; + struct DataEntryUrl : FullBox { DECLARE_BOX_METHODS(DataEntryUrl); @@ -385,6 +411,7 @@ struct MediaInformation : Box { // Exactly one specific meida header shall be present, vmhd, smhd, hmhd, nmhd. VideoMediaHeader vmhd; SoundMediaHeader smhd; + SubtitleMediaHeader sthd; }; struct Media : Box { @@ -591,6 +618,50 @@ struct MediaData : Box { uint32_t data_size; }; +struct CueSourceIDBox : Box { + DECLARE_BOX_METHODS(CueSourceIDBox); + int32_t source_id; +}; + +struct CueTimeBox : Box { + DECLARE_BOX_METHODS(CueTimeBox); + std::string cue_current_time; +}; + +struct CueIDBox : Box { + DECLARE_BOX_METHODS(CueIDBox); + std::string cue_id; +}; + +struct CueSettingsBox : Box { + DECLARE_BOX_METHODS(CueSettingsBox); + std::string settings; +}; + +struct CuePayloadBox : Box { + DECLARE_BOX_METHODS(CuePayloadBox); + std::string cue_text; +}; + +struct VTTEmptyCueBox : Box { + DECLARE_BOX_METHODS(VTTEmptyCueBox); +}; + +struct VTTAdditionalTextBox : Box { + DECLARE_BOX_METHODS(VTTAdditionalTextBox); + std::string cue_additional_text; +}; + +struct VTTCueBox : Box { + DECLARE_BOX_METHODS(VTTCueBox); + + CueSourceIDBox cue_source_id; + CueIDBox cue_id; + CueTimeBox cue_time; + CueSettingsBox cue_settings; + CuePayloadBox cue_payload; +}; + #undef DECLARE_BOX } // namespace mp4 diff --git a/packager/media/formats/mp4/box_definitions_comparison.h b/packager/media/formats/mp4/box_definitions_comparison.h index 6788731f05..4949b38061 100644 --- a/packager/media/formats/mp4/box_definitions_comparison.h +++ b/packager/media/formats/mp4/box_definitions_comparison.h @@ -209,6 +209,21 @@ inline bool operator==(const AudioSampleEntry& lhs, lhs.ddts == rhs.ddts; } +inline bool operator==(const WebVTTConfigurationBox& lhs, + const WebVTTConfigurationBox& rhs) { + return lhs.config == rhs.config; +} + +inline bool operator==(const WebVTTSourceLabelBox& lhs, + const WebVTTSourceLabelBox& rhs) { + return lhs.source_label == rhs.source_label; +} + +inline bool operator==(const WVTTSampleEntry& lhs, + const WVTTSampleEntry& rhs) { + return lhs.config == rhs.config && lhs.label == rhs.label; +} + inline bool operator==(const MediaHeader& lhs, const MediaHeader& rhs) { return lhs.creation_time == rhs.creation_time && lhs.modification_time == rhs.modification_time && @@ -229,6 +244,11 @@ inline bool operator==(const SoundMediaHeader& lhs, return lhs.balance == rhs.balance; } +inline bool operator==(const SubtitleMediaHeader& lhs, + const SubtitleMediaHeader& rhs) { + return true; +} + inline bool operator==(const DataEntryUrl& lhs, const DataEntryUrl& rhs) { return lhs.flags == rhs.flags && lhs.location == rhs.location; } @@ -366,6 +386,47 @@ inline bool operator==(const SegmentIndex& lhs, const SegmentIndex& rhs) { lhs.references == rhs.references; } +inline bool operator==(const CueSourceIDBox& lhs, + const CueSourceIDBox& rhs) { + return lhs.source_id == rhs.source_id; +} + +inline bool operator==(const CueTimeBox& lhs, + const CueTimeBox& rhs) { + return lhs.cue_current_time == rhs.cue_current_time; +} + +inline bool operator==(const CueIDBox& lhs, + const CueIDBox& rhs) { + return lhs.cue_id == rhs.cue_id; +} + +inline bool operator==(const CueSettingsBox& lhs, + const CueSettingsBox& rhs) { + return lhs.settings == rhs.settings; +} + +inline bool operator==(const CuePayloadBox& lhs, + const CuePayloadBox& rhs) { + return lhs.cue_text == rhs.cue_text; +} + +inline bool operator==(const VTTEmptyCueBox& lhs, const VTTEmptyCueBox& rhs) { + return true; +} + +inline bool operator==(const VTTAdditionalTextBox& lhs, + const VTTAdditionalTextBox& rhs) { + return lhs.cue_additional_text == rhs.cue_additional_text; +} + +inline bool operator==(const VTTCueBox& lhs, + const VTTCueBox& rhs) { + return lhs.cue_source_id == rhs.cue_source_id && lhs.cue_id == rhs.cue_id && + lhs.cue_time == rhs.cue_time && lhs.cue_settings == rhs.cue_settings && + lhs.cue_payload == rhs.cue_payload; +} + } // namespace mp4 } // namespace media } // namespace edash_packager diff --git a/packager/media/formats/mp4/box_definitions_unittest.cc b/packager/media/formats/mp4/box_definitions_unittest.cc index 0f6239d048..dc0fdc036a 100644 --- a/packager/media/formats/mp4/box_definitions_unittest.cc +++ b/packager/media/formats/mp4/box_definitions_unittest.cc @@ -335,6 +335,33 @@ class BoxDefinitionsTestGeneral : public testing::Test { void Modify(AudioSampleEntry* enca) { enca->channelcount = 2; } + void Fill(WebVTTConfigurationBox* vttc) { + vttc->config = "WEBVTT"; + } + + void Modify(WebVTTConfigurationBox* vttc) { + vttc->config = "WEBVTT\n" + "Region: id=someting width=40\% lines=3"; + } + + void Fill(WebVTTSourceLabelBox* vlab) { + vlab->source_label = "some_label"; + } + + void Modify(WebVTTSourceLabelBox* vlab) { + vlab->source_label = "another_label"; + } + + void Fill(WVTTSampleEntry* wvtt) { + Fill(&wvtt->config); + Fill(&wvtt->label); + } + + void Modify(WVTTSampleEntry* wvtt) { + Modify(&wvtt->config); + Modify(&wvtt->label); + } + void Fill(SampleDescription* stsd) { stsd->type = kSampleDescriptionTrackType; stsd->video_entries.resize(1); @@ -475,6 +502,9 @@ class BoxDefinitionsTestGeneral : public testing::Test { void Modify(SoundMediaHeader* smhd) { smhd->balance /= 2; } + void Fill(SubtitleMediaHeader* sthd) {} + void Modify(SubtitleMediaHeader* sthd) {} + void Fill(DataEntryUrl* url) { url->flags = 2; url->location.assign(kData8, kData8 + arraysize(kData8)); @@ -718,6 +748,73 @@ class BoxDefinitionsTestGeneral : public testing::Test { sidx->version = 1; } + void Fill(CueSourceIDBox* vsid) { + vsid->source_id = 5; + } + + void Modify(CueSourceIDBox* vsid) { + vsid->source_id = 100; + } + + void Fill(CueTimeBox* ctim) { + ctim->cue_current_time = "00:19:00.000"; + } + + void Modify(CueTimeBox* ctim) { + ctim->cue_current_time = "00:20:01.291"; + } + + void Fill(CueIDBox* iden) { + iden->cue_id = "some_id"; + } + + void Modify(CueIDBox* iden) { + iden->cue_id = "another_id"; + } + + void Fill(CueSettingsBox* sttg) { + sttg->settings = "align:left"; + } + + void Modify(CueSettingsBox* sttg) { + sttg->settings = "align:right"; + } + + void Fill(CuePayloadBox* payl) { + payl->cue_text = "hello"; + } + + void Modify(CuePayloadBox* payl) { + payl->cue_text = "hi"; + } + + void Fill(VTTEmptyCueBox* vtte) {} + void Modify(VTTEmptyCueBox* vtte) {} + + void Fill(VTTAdditionalTextBox* vtta) { + vtta->cue_additional_text = "NOTE some comment"; + } + + void Modify(VTTAdditionalTextBox* vtta) { + vtta->cue_additional_text = "NOTE another comment"; + } + + void Fill(VTTCueBox* vttc) { + Fill(&vttc->cue_source_id); + Fill(&vttc->cue_id); + Fill(&vttc->cue_time); + Fill(&vttc->cue_settings); + Fill(&vttc->cue_payload); + } + + void Modify(VTTCueBox* vttc) { + Modify(&vttc->cue_source_id); + Modify(&vttc->cue_id); + Modify(&vttc->cue_time); + Modify(&vttc->cue_settings); + Modify(&vttc->cue_payload); + } + bool IsOptional(const SampleAuxiliaryInformationOffset* box) { return true; } bool IsOptional(const SampleAuxiliaryInformationSize* box) { return true; } bool IsOptional(const ProtectionSchemeInfo* box) { return true; } @@ -726,12 +823,18 @@ class BoxDefinitionsTestGeneral : public testing::Test { bool IsOptional(const CodecConfigurationRecord* box) { return true; } bool IsOptional(const PixelAspectRatio* box) { return true; } bool IsOptional(const ElementaryStreamDescriptor* box) { return true; } + // Recommended, but optional. + bool IsOptional(const WebVTTSourceLabelBox* box) { return true; } bool IsOptional(const CompositionTimeToSample* box) { return true; } bool IsOptional(const SyncSample* box) { return true; } bool IsOptional(const MovieExtendsHeader* box) { return true; } bool IsOptional(const MovieExtends* box) { return true; } bool IsOptional(const SampleToGroup* box) { return true; } bool IsOptional(const SampleGroupDescription* box) { return true; } + bool IsOptional(const CueSourceIDBox* box) { return true; } + bool IsOptional(const CueIDBox* box) { return true; } + bool IsOptional(const CueTimeBox* box) { return true; } + bool IsOptional(const CueSettingsBox* box) { return true; } protected: scoped_ptr buffer_; @@ -758,6 +861,9 @@ typedef testing::Types< VideoSampleEntry, ElementaryStreamDescriptor, AudioSampleEntry, + WebVTTConfigurationBox, + WebVTTSourceLabelBox, + WVTTSampleEntry, SampleDescription, DecodingTimeToSample, CompositionTimeToSample, @@ -771,6 +877,7 @@ typedef testing::Types< MediaHeader, VideoMediaHeader, SoundMediaHeader, + SubtitleMediaHeader, DataEntryUrl, DataReference, DataInformation, @@ -783,17 +890,25 @@ typedef testing::Types< Movie, TrackFragmentDecodeTime, MovieFragmentHeader, - TrackFragmentHeader, - TrackFragmentRun, - TrackFragment, - MovieFragment, - SegmentIndex> Boxes; + TrackFragmentHeader> Boxes; // GTEST support a maximum of 50 types in the template list, so we have to // break it into two groups. typedef testing::Types< + TrackFragmentRun, + TrackFragment, + MovieFragment, + SegmentIndex, SampleToGroup, - SampleGroupDescription> Boxes2; + SampleGroupDescription, + CueSourceIDBox, + CueTimeBox, + CueIDBox, + CueSettingsBox, + CuePayloadBox, + VTTEmptyCueBox, + VTTAdditionalTextBox, + VTTCueBox> Boxes2; TYPED_TEST_CASE_P(BoxDefinitionsTestGeneral); diff --git a/packager/media/formats/mp4/fourccs.h b/packager/media/formats/mp4/fourccs.h index 8f399281a0..8467003419 100644 --- a/packager/media/formats/mp4/fourccs.h +++ b/packager/media/formats/mp4/fourccs.h @@ -11,6 +11,7 @@ namespace edash_packager { namespace media { namespace mp4 { +// TODO(rkuroiwa): Make these case sensitive. e.g. FOURCC_avc1. enum FourCC { FOURCC_NULL = 0, FOURCC_AVC1 = 0x61766331, @@ -88,7 +89,9 @@ enum FourCC { FOURCC_STTS = 0x73747473, FOURCC_STYP = 0x73747970, FOURCC_STZ2 = 0x73747a32, + FOURCC_SUBT = 0x73756274, FOURCC_TENC = 0x74656e63, + FOURCC_TEXT = 0x74657874, FOURCC_TFDT = 0x74666474, FOURCC_TFHD = 0x74666864, FOURCC_TKHD = 0x746b6864, @@ -97,8 +100,8 @@ enum FourCC { FOURCC_TREX = 0x74726578, FOURCC_TRUN = 0x7472756e, FOURCC_UDTA = 0x75647461, - FOURCC_URL = 0x75726c20, - FOURCC_URN = 0x75726e20, + FOURCC_URL = 0x75726c20, + FOURCC_URN = 0x75726e20, FOURCC_UUID = 0x75756964, FOURCC_VIDE = 0x76696465, FOURCC_VMHD = 0x766d6864, @@ -107,6 +110,18 @@ enum FourCC { FOURCC_VP10 = 0x76703130, FOURCC_VPCC = 0x76706343, FOURCC_WIDE = 0x77696465, + FOURCC_ctim = 0x6374696d, + FOURCC_iden = 0x6964656e, + FOURCC_payl = 0x7061796c, + FOURCC_sthd = 0x73746864, + FOURCC_sttg = 0x73747467, + FOURCC_vlab = 0x766c6162, + FOURCC_vsid = 0x76736964, + FOURCC_vttC = 0x76747443, + FOURCC_vtta = 0x76747461, + FOURCC_vttc = 0x76747463, + FOURCC_vtte = 0x76747465, + FOURCC_wvtt = 0x77767474, }; const inline std::string FourCCToString(FourCC fourcc) {