mkvmuxer: write correct DocTypeVersion

Change-Id: I9a463394dec5e69ae8a7b5a1378f19d390e441e4
2: default
4: if CodecDelay/SeekPreRoll/DiscardPadding are present
This commit is contained in:
James Zern 2014-06-20 16:57:14 -07:00
parent 574045edd4
commit a321704b4c
2 changed files with 58 additions and 6 deletions

View File

@ -65,14 +65,14 @@ IMkvWriter::IMkvWriter() {}
IMkvWriter::~IMkvWriter() {}
bool WriteEbmlHeader(IMkvWriter* writer) {
bool WriteEbmlHeader(IMkvWriter* writer, uint64 doc_type_version) {
// Level 0
uint64 size = EbmlElementSize(kMkvEBMLVersion, 1ULL);
size += EbmlElementSize(kMkvEBMLReadVersion, 1ULL);
size += EbmlElementSize(kMkvEBMLMaxIDLength, 4ULL);
size += EbmlElementSize(kMkvEBMLMaxSizeLength, 8ULL);
size += EbmlElementSize(kMkvDocType, "webm");
size += EbmlElementSize(kMkvDocTypeVersion, 2ULL);
size += EbmlElementSize(kMkvDocTypeVersion, doc_type_version);
size += EbmlElementSize(kMkvDocTypeReadVersion, 2ULL);
if (!WriteEbmlMasterElement(writer, kMkvEBML, size))
@ -87,7 +87,7 @@ bool WriteEbmlHeader(IMkvWriter* writer) {
return false;
if (!WriteEbmlElement(writer, kMkvDocType, "webm"))
return false;
if (!WriteEbmlElement(writer, kMkvDocTypeVersion, 2ULL))
if (!WriteEbmlElement(writer, kMkvDocTypeVersion, doc_type_version))
return false;
if (!WriteEbmlElement(writer, kMkvDocTypeReadVersion, 2ULL))
return false;
@ -2003,6 +2003,8 @@ Segment::Segment()
output_cues_(true),
payload_pos_(0),
size_position_(0),
doc_type_version_(kDefaultDocTypeVersion),
doc_type_version_written_(0),
writer_cluster_(NULL),
writer_cues_(NULL),
writer_header_(NULL) {
@ -2201,12 +2203,24 @@ bool Segment::Finalize() {
if (size_position_ == -1)
return false;
const int64 pos = writer_header_->Position();
const int64 segment_size = MaxOffset();
if (segment_size < 1)
return false;
const int64 pos = writer_header_->Position();
UpdateDocTypeVersion();
if (doc_type_version_ != doc_type_version_written_) {
if (writer_header_->Position(0))
return false;
if (!WriteEbmlHeader(writer_header_, doc_type_version_))
return false;
if (writer_header_->Position() != ebml_header_size_)
return false;
doc_type_version_written_ = doc_type_version_;
}
if (writer_header_->Position(size_position_))
return false;
@ -2444,6 +2458,9 @@ bool Segment::AddFrameWithDiscardPadding(const uint8* frame, uint64 length,
if (!tracks_.GetTrackByNumber(track_number))
return false;
if (discard_padding != 0)
doc_type_version_ = 4;
// If the segment has a video track hold onto audio frames to make sure the
// audio that is associated with the start time of a video key-frame is
// muxed into the same cluster.
@ -2654,9 +2671,13 @@ Track* Segment::GetTrackByNumber(uint64 track_number) const {
}
bool Segment::WriteSegmentHeader() {
UpdateDocTypeVersion();
// TODO(fgalligan): Support more than one segment.
if (!WriteEbmlHeader(writer_header_))
if (!WriteEbmlHeader(writer_header_, doc_type_version_))
return false;
doc_type_version_written_ = doc_type_version_;
ebml_header_size_ = static_cast<int32>(writer_header_->Position());
// Write "unknown" (-1) as segment size value. If mode is kFile, Segment
// will write over duration when the file is finalized.
@ -2929,6 +2950,18 @@ bool Segment::CheckHeaderInfo() {
return true;
}
void Segment::UpdateDocTypeVersion() {
for (uint32 index = 0; index < tracks_.track_entries_size(); ++index) {
const Track* track = tracks_.GetTrackByIndex(index);
if (track == NULL) break;
if ((track->codec_delay() || track->seek_pre_roll()) &&
doc_type_version_ < 4) {
doc_type_version_ = 4;
break;
}
}
}
bool Segment::UpdateChunkName(const char* ext, char** name) const {
if (!name || !ext)
return false;
@ -3026,6 +3059,9 @@ int Segment::WriteFramesAll() {
const uint64 frame_timecode = frame_timestamp / timecode_scale;
if (frame->discard_padding() != 0) {
// TODO(jzern): using the Segment:: variants here would limit the places
// where doc_type_version_ needs to be updated.
doc_type_version_ = 4;
if (!cluster->AddFrameWithDiscardPadding(
frame->frame(), frame->length(), frame->discard_padding(),
frame->track_number(), frame_timecode, frame->is_key())) {
@ -3086,6 +3122,7 @@ bool Segment::WriteFramesLessThan(uint64 timestamp) {
const int64 discard_padding = frame_prev->discard_padding();
if (discard_padding != 0) {
doc_type_version_ = 4;
if (!cluster->AddFrameWithDiscardPadding(
frame_prev->frame(), frame_prev->length(), discard_padding,
frame_prev->track_number(), frame_timecode,

View File

@ -1010,6 +1010,7 @@ class Segment {
kBeforeClusters = 0x1 // Position Cues before Clusters
};
const static uint32 kDefaultDocTypeVersion = 2;
const static uint64 kDefaultMaxClusterDuration = 30000000000ULL;
Segment();
@ -1191,6 +1192,9 @@ class Segment {
// Cues elements.
bool CheckHeaderInfo();
// Sets |doc_type_version_| based on the current element requirements.
void UpdateDocTypeVersion();
// Sets |name| according to how many chunks have been written. |ext| is the
// file extension. |name| must be deleted by the calling app. Returns true
// on success.
@ -1351,12 +1355,23 @@ class Segment {
// Flag whether or not the muxer should output a Cues element.
bool output_cues_;
// The size of the EBML header, used to validate the header if
// WriteEbmlHeader() is called more than once.
int32 ebml_header_size_;
// The file position of the segment's payload.
int64 payload_pos_;
// The file position of the element's size.
int64 size_position_;
// Current DocTypeVersion (|doc_type_version_|) and that written in
// WriteSegmentHeader().
// WriteEbmlHeader() will be called from Finalize() if |doc_type_version_|
// differs from |doc_type_version_written_|.
uint32 doc_type_version_;
uint32 doc_type_version_written_;
// Pointer to the writer objects. Not owned by this class.
IMkvWriter* writer_cluster_;
IMkvWriter* writer_cues_;