Adds support for simple tags

Change-Id: I285e4b73df0a7112bbde7ef13eddf8fdccf59178
This commit is contained in:
Francisco Facioni 2015-07-14 14:18:09 -03:00
parent 75a6d2da8b
commit b6de61a5c0
7 changed files with 761 additions and 1 deletions

View File

@ -1514,6 +1514,237 @@ uint64 Chapters::WriteEdition(IMkvWriter* writer) const {
return edition_size;
}
// Tag Class
bool Tag::add_simple_tag(const char* tag_name, const char* tag_string) {
if (!ExpandSimpleTagsArray())
return false;
SimpleTag& st = simple_tags_[simple_tags_count_++];
st.Init();
if (!st.set_tag_name(tag_name))
return false;
if (!st.set_tag_string(tag_string))
return false;
return true;
}
Tag::Tag() {
simple_tags_ = NULL;
simple_tags_size_ = 0;
simple_tags_count_ = 0;
}
Tag::~Tag() {}
void Tag::ShallowCopy(Tag* dst) const {
dst->simple_tags_ = simple_tags_;
dst->simple_tags_size_ = simple_tags_size_;
dst->simple_tags_count_ = simple_tags_count_;
}
void Tag::Clear() {
while (simple_tags_count_ > 0) {
SimpleTag& st = simple_tags_[--simple_tags_count_];
st.Clear();
}
delete[] simple_tags_;
simple_tags_ = NULL;
simple_tags_size_ = 0;
}
bool Tag::ExpandSimpleTagsArray() {
if (simple_tags_size_ > simple_tags_count_)
return true; // nothing to do yet
const int size = (simple_tags_size_ == 0) ? 1 : 2 * simple_tags_size_;
SimpleTag* const simple_tags = new (std::nothrow) SimpleTag[size]; // NOLINT
if (simple_tags == NULL)
return false;
for (int idx = 0; idx < simple_tags_count_; ++idx) {
simple_tags[idx] = simple_tags_[idx]; // shallow copy
}
delete[] simple_tags_;
simple_tags_ = simple_tags;
simple_tags_size_ = size;
return true;
}
uint64 Tag::Write(IMkvWriter* writer) const {
uint64 payload_size = 0;
for (int idx = 0; idx < simple_tags_count_; ++idx) {
const SimpleTag& st = simple_tags_[idx];
payload_size += st.Write(NULL);
}
const uint64 tag_size =
EbmlMasterElementSize(kMkvTag, payload_size) + payload_size;
if (writer == NULL)
return tag_size;
const int64 start = writer->Position();
if (!WriteEbmlMasterElement(writer, kMkvTag, payload_size))
return 0;
for (int idx = 0; idx < simple_tags_count_; ++idx) {
const SimpleTag& st = simple_tags_[idx];
if (!st.Write(writer))
return 0;
}
const int64 stop = writer->Position();
if (stop >= start && uint64(stop - start) != tag_size)
return 0;
return tag_size;
}
// Tag::SimpleTag
void Tag::SimpleTag::Init() {
tag_name_ = NULL;
tag_string_ = NULL;
}
void Tag::SimpleTag::Clear() {
StrCpy(NULL, &tag_name_);
StrCpy(NULL, &tag_string_);
}
bool Tag::SimpleTag::set_tag_name(const char* tag_name) {
return StrCpy(tag_name, &tag_name_);
}
bool Tag::SimpleTag::set_tag_string(const char* tag_string) {
return StrCpy(tag_string, &tag_string_);
}
uint64 Tag::SimpleTag::Write(IMkvWriter* writer) const {
uint64 payload_size = EbmlElementSize(kMkvTagName, tag_name_);
payload_size += EbmlElementSize(kMkvTagString, tag_string_);
const uint64 simple_tag_size =
EbmlMasterElementSize(kMkvSimpleTag, payload_size) + payload_size;
if (writer == NULL)
return simple_tag_size;
const int64 start = writer->Position();
if (!WriteEbmlMasterElement(writer, kMkvSimpleTag, payload_size))
return 0;
if (!WriteEbmlElement(writer, kMkvTagName, tag_name_))
return 0;
if (!WriteEbmlElement(writer, kMkvTagString, tag_string_))
return 0;
const int64 stop = writer->Position();
if (stop >= start && uint64(stop - start) != simple_tag_size)
return 0;
return simple_tag_size;
}
// Tags Class
Tags::Tags() : tags_size_(0), tags_count_(0), tags_(NULL) {}
Tags::~Tags() {
while (tags_count_ > 0) {
Tag& tag = tags_[--tags_count_];
tag.Clear();
}
delete[] tags_;
tags_ = NULL;
}
int Tags::Count() const { return tags_count_; }
Tag* Tags::AddTag() {
if (!ExpandTagsArray())
return NULL;
Tag& tag = tags_[tags_count_++];
return &tag;
}
bool Tags::Write(IMkvWriter* writer) const {
if (writer == NULL)
return false;
uint64 payload_size = 0;
for (int idx = 0; idx < tags_count_; ++idx) {
const Tag& tag = tags_[idx];
payload_size += tag.Write(NULL);
}
if (!WriteEbmlMasterElement(writer, kMkvTags, payload_size))
return false;
const int64 start = writer->Position();
for (int idx = 0; idx < tags_count_; ++idx) {
const Tag& tag = tags_[idx];
const uint64 tag_size = tag.Write(writer);
if (tag_size == 0) // error
return 0;
}
const int64 stop = writer->Position();
if (stop >= start && uint64(stop - start) != payload_size)
return false;
return true;
}
bool Tags::ExpandTagsArray() {
if (tags_size_ > tags_count_)
return true; // nothing to do yet
const int size = (tags_size_ == 0) ? 1 : 2 * tags_size_;
Tag* const tags = new (std::nothrow) Tag[size]; // NOLINT
if (tags == NULL)
return false;
for (int idx = 0; idx < tags_count_; ++idx) {
const Tag& src = tags_[idx];
Tag* const dst = tags + idx;
src.ShallowCopy(dst);
}
delete[] tags_;
tags_ = tags;
tags_size_ = size;
return true;
}
///////////////////////////////////////////////////////////////
//
// Cluster class
@ -2274,6 +2505,8 @@ Track* Segment::AddTrack(int32 number) {
Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); }
Tag* Segment::AddTag() { return tags_.AddTag(); }
uint64 Segment::AddVideoTrack(int32 width, int32 height, int32 number) {
VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT
if (!track)
@ -2614,6 +2847,13 @@ bool Segment::WriteSegmentHeader() {
return false;
}
if (tags_.Count() > 0) {
if (!seek_head_.AddSeekEntry(kMkvTags, MaxOffset()))
return false;
if (!tags_.Write(writer_header_))
return false;
}
if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) {
if (!chunk_writer_header_)
return false;

View File

@ -736,6 +736,112 @@ class Chapters {
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Chapters);
};
///////////////////////////////////////////////////////////////
// Tag element
//
class Tag {
public:
bool add_simple_tag(const char* tag_name, const char* tag_string);
private:
// Tags calls Clear and the destructor of Tag
friend class Tags;
// For storage of simple tags
class SimpleTag {
public:
// Establish representation invariant for new SimpleTag object.
void Init();
// Reclaim resources, in anticipation of destruction.
void Clear();
// Copies the title to the |tag_name_| member. Returns false on
// error.
bool set_tag_name(const char* tag_name);
// Copies the language to the |tag_string_| member. Returns false
// on error.
bool set_tag_string(const char* tag_string);
// If |writer| is non-NULL, serialize the SimpleTag sub-element of
// the Atom into the stream. Returns the SimpleTag element size on
// success, 0 if error.
uint64 Write(IMkvWriter* writer) const;
private:
char* tag_name_;
char* tag_string_;
};
Tag();
~Tag();
// Copies this Tag object to a different one. This is used when
// expanding a plain array of Tag objects (see Tags).
void ShallowCopy(Tag* dst) const;
// Reclaim resources used by this Tag object, pending its
// destruction.
void Clear();
// If there is no storage remaining on the |simple_tags_| array for a
// new display object, creates a new, longer array and copies the
// existing SimpleTag objects to the new array. Returns false if the
// array cannot be expanded.
bool ExpandSimpleTagsArray();
// If |writer| is non-NULL, serialize the Tag sub-element into the
// stream. Returns the total size of the element on success, 0 if
// error.
uint64 Write(IMkvWriter* writer) const;
// The Atom element can contain multiple SimpleTag sub-elements
SimpleTag* simple_tags_;
// The physical length (total size) of the |simple_tags_| array.
int simple_tags_size_;
// The logical length (number of active elements) on the |simple_tags_|
// array.
int simple_tags_count_;
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tag);
};
///////////////////////////////////////////////////////////////
// Tags element
//
class Tags {
public:
Tags();
~Tags();
Tag* AddTag();
// Returns the number of tags that have been added.
int Count() const;
// Output the Tags element to the writer. Returns true on success.
bool Write(IMkvWriter* writer) const;
private:
// Expands the tags_ array if there is not enough space to contain
// another tag object. Returns true on success.
bool ExpandTagsArray();
// Total length of the tags_ array.
int tags_size_;
// Number of active tags on the tags_ array.
int tags_count_;
// Array for storage of tag objects.
Tag* tags_;
LIBWEBM_DISALLOW_COPY_AND_ASSIGN(Tags);
};
///////////////////////////////////////////////////////////////
// Cluster element
//
@ -1029,6 +1135,11 @@ class Segment {
// populate its fields via the Chapter member functions.
Chapter* AddChapter();
// Adds an empty tag to the tags of this segment. Returns
// non-NULL on success. After adding the tag, the caller should
// populate its fields via the Tag member functions.
Tag* AddTag();
// Adds a cue point to the Cues element. |timestamp| is the time in
// nanoseconds of the cue's time. |track| is the Track of the Cue. This
// function must be called after AddFrame to calculate the correct
@ -1254,6 +1365,7 @@ class Segment {
SegmentInfo segment_info_;
Tracks tracks_;
Chapters chapters_;
Tags tags_;
// Number of chunks written.
int chunk_count_;

View File

@ -607,6 +607,7 @@ Segment::Segment(IMkvReader* pReader, long long elem_start,
m_pTracks(NULL),
m_pCues(NULL),
m_pChapters(NULL),
m_pTags(NULL),
m_clusters(NULL),
m_clusterCount(0),
m_clusterPreloadCount(0),
@ -631,6 +632,7 @@ Segment::~Segment() {
delete m_pInfo;
delete m_pCues;
delete m_pChapters;
delete m_pTags;
delete m_pSeekHead;
}
@ -906,6 +908,19 @@ long long Segment::ParseHeaders() {
const long status = m_pChapters->Parse();
if (status)
return status;
}
} else if (id == 0x0254C367) { // Tags ID
if (m_pTags == NULL) {
m_pTags = new (std::nothrow)
Tags(this, pos, size, element_start, element_size);
if (m_pTags == NULL)
return -1;
const long status = m_pTags->Parse();
if (status)
return status;
}
@ -3071,6 +3086,7 @@ const Tracks* Segment::GetTracks() const { return m_pTracks; }
const SegmentInfo* Segment::GetInfo() const { return m_pInfo; }
const Cues* Segment::GetCues() const { return m_pCues; }
const Chapters* Segment::GetChapters() const { return m_pChapters; }
const Tags* Segment::GetTags() const { return m_pTags; }
const SeekHead* Segment::GetSeekHead() const { return m_pSeekHead; }
long long Segment::GetDuration() const {
@ -3536,6 +3552,277 @@ long Chapters::Display::Parse(IMkvReader* pReader, long long pos,
return 0;
}
Tags::Tags(Segment* pSegment, long long payload_start, long long payload_size,
long long element_start, long long element_size)
: m_pSegment(pSegment),
m_start(payload_start),
m_size(payload_size),
m_element_start(element_start),
m_element_size(element_size),
m_tags(NULL),
m_tags_size(0),
m_tags_count(0) {}
Tags::~Tags() {
while (m_tags_count > 0) {
Tag& t = m_tags[--m_tags_count];
t.Clear();
}
delete[] m_tags;
}
long Tags::Parse() {
IMkvReader* const pReader = m_pSegment->m_pReader;
long long pos = m_start; // payload start
const long long stop = pos + m_size; // payload stop
while (pos < stop) {
long long id, size;
long status = ParseElementHeader(pReader, pos, stop, id, size);
if (status < 0)
return status;
if (size == 0) // 0 length tag, read another
continue;
if (id == 0x3373) { // Tag ID
status = ParseTag(pos, size);
if (status < 0)
return status;
}
pos += size;
assert(pos <= stop);
if (pos > stop)
return -1;
}
assert(pos == stop);
if (pos != stop)
return -1;
return 0;
}
int Tags::GetTagCount() const { return m_tags_count; }
const Tags::Tag* Tags::GetTag(int idx) const {
if (idx < 0)
return NULL;
if (idx >= m_tags_count)
return NULL;
return m_tags + idx;
}
bool Tags::ExpandTagsArray() {
if (m_tags_size > m_tags_count)
return true; // nothing else to do
const int size = (m_tags_size == 0) ? 1 : 2 * m_tags_size;
Tag* const tags = new (std::nothrow) Tag[size];
if (tags == NULL)
return false;
for (int idx = 0; idx < m_tags_count; ++idx) {
m_tags[idx].ShallowCopy(tags[idx]);
}
delete[] m_tags;
m_tags = tags;
m_tags_size = size;
return true;
}
long Tags::ParseTag(long long pos, long long size) {
if (!ExpandTagsArray())
return -1;
Tag& t = m_tags[m_tags_count++];
t.Init();
return t.Parse(m_pSegment->m_pReader, pos, size);
}
Tags::Tag::Tag() {}
Tags::Tag::~Tag() {}
int Tags::Tag::GetSimpleTagCount() const { return m_simple_tags_count; }
const Tags::SimpleTag* Tags::Tag::GetSimpleTag(int index) const {
if (index < 0)
return NULL;
if (index >= m_simple_tags_count)
return NULL;
return m_simple_tags + index;
}
void Tags::Tag::Init() {
m_simple_tags = NULL;
m_simple_tags_size = 0;
m_simple_tags_count = 0;
}
void Tags::Tag::ShallowCopy(Tag& rhs) const {
rhs.m_simple_tags = m_simple_tags;
rhs.m_simple_tags_size = m_simple_tags_size;
rhs.m_simple_tags_count = m_simple_tags_count;
}
void Tags::Tag::Clear() {
while (m_simple_tags_count > 0) {
SimpleTag& d = m_simple_tags[--m_simple_tags_count];
d.Clear();
}
delete[] m_simple_tags;
m_simple_tags = NULL;
m_simple_tags_size = 0;
}
long Tags::Tag::Parse(IMkvReader* pReader, long long pos, long long size) {
const long long stop = pos + size;
while (pos < stop) {
long long id, size;
long status = ParseElementHeader(pReader, pos, stop, id, size);
if (status < 0)
return status;
if (size == 0) // 0 length tag, read another
continue;
if (id == 0x27C8) { // SimpleTag ID
status = ParseSimpleTag(pReader, pos, size);
if (status < 0)
return status;
}
pos += size;
assert(pos <= stop);
if (pos > stop)
return -1;
}
assert(pos == stop);
if (pos != stop)
return -1;
return 0;
}
long Tags::Tag::ParseSimpleTag(IMkvReader* pReader, long long pos,
long long size) {
if (!ExpandSimpleTagsArray())
return -1;
SimpleTag& st = m_simple_tags[m_simple_tags_count++];
st.Init();
return st.Parse(pReader, pos, size);
}
bool Tags::Tag::ExpandSimpleTagsArray() {
if (m_simple_tags_size > m_simple_tags_count)
return true; // nothing else to do
const int size = (m_simple_tags_size == 0) ? 1 : 2 * m_simple_tags_size;
SimpleTag* const displays = new (std::nothrow) SimpleTag[size];
if (displays == NULL)
return false;
for (int idx = 0; idx < m_simple_tags_count; ++idx) {
m_simple_tags[idx].ShallowCopy(displays[idx]);
}
delete[] m_simple_tags;
m_simple_tags = displays;
m_simple_tags_size = size;
return true;
}
Tags::SimpleTag::SimpleTag() {}
Tags::SimpleTag::~SimpleTag() {}
const char* Tags::SimpleTag::GetTagName() const { return m_tag_name; }
const char* Tags::SimpleTag::GetTagString() const { return m_tag_string; }
void Tags::SimpleTag::Init() {
m_tag_name = NULL;
m_tag_string = NULL;
}
void Tags::SimpleTag::ShallowCopy(SimpleTag& rhs) const {
rhs.m_tag_name = m_tag_name;
rhs.m_tag_string = m_tag_string;
}
void Tags::SimpleTag::Clear() {
delete[] m_tag_name;
m_tag_name = NULL;
delete[] m_tag_string;
m_tag_string = NULL;
}
long Tags::SimpleTag::Parse(IMkvReader* pReader, long long pos,
long long size) {
const long long stop = pos + size;
while (pos < stop) {
long long id, size;
long status = ParseElementHeader(pReader, pos, stop, id, size);
if (status < 0) // error
return status;
if (size == 0) // weird
continue;
if (id == 0x5A3) { // TagName ID
status = UnserializeString(pReader, pos, size, m_tag_name);
if (status)
return status;
} else if (id == 0x487) { // TagString ID
status = UnserializeString(pReader, pos, size, m_tag_string);
if (status)
return status;
}
pos += size;
assert(pos <= stop);
if (pos > stop)
return -1;
}
assert(pos == stop);
if (pos != stop)
return -1;
return 0;
}
SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_,
long long element_start, long long element_size)
: m_pSegment(pSegment),

View File

@ -591,6 +591,85 @@ class Chapters {
int m_editions_count;
};
class Tags {
Tags(const Tags&);
Tags& operator=(const Tags&);
public:
Segment* const m_pSegment;
const long long m_start;
const long long m_size;
const long long m_element_start;
const long long m_element_size;
Tags(Segment*, long long payload_start, long long payload_size,
long long element_start, long long element_size);
~Tags();
long Parse();
class Tag;
class SimpleTag;
class SimpleTag {
friend class Tag;
SimpleTag();
SimpleTag(const SimpleTag&);
~SimpleTag();
SimpleTag& operator=(const SimpleTag&);
public:
const char* GetTagName() const;
const char* GetTagString() const;
private:
void Init();
void ShallowCopy(SimpleTag&) const;
void Clear();
long Parse(IMkvReader*, long long pos, long long size);
char* m_tag_name;
char* m_tag_string;
};
class Tag {
friend class Tags;
Tag();
Tag(const Tag&);
~Tag();
Tag& operator=(const Tag&);
public:
int GetSimpleTagCount() const;
const SimpleTag* GetSimpleTag(int index) const;
private:
void Init();
void ShallowCopy(Tag&) const;
void Clear();
long Parse(IMkvReader*, long long pos, long long size);
long ParseSimpleTag(IMkvReader*, long long pos, long long size);
bool ExpandSimpleTagsArray();
SimpleTag* m_simple_tags;
int m_simple_tags_size;
int m_simple_tags_count;
};
int GetTagCount() const;
const Tag* GetTag(int index) const;
private:
long ParseTag(long long pos, long long size);
bool ExpandTagsArray();
Tag* m_tags;
int m_tags_size;
int m_tags_count;
};
class SegmentInfo {
SegmentInfo(const SegmentInfo&);
SegmentInfo& operator=(const SegmentInfo&);
@ -883,6 +962,7 @@ class Segment {
const SegmentInfo* GetInfo() const;
const Cues* GetCues() const;
const Chapters* GetChapters() const;
const Tags* GetTags() const;
long long GetDuration() const;
@ -908,6 +988,7 @@ class Segment {
Tracks* m_pTracks;
Cues* m_pCues;
Chapters* m_pChapters;
Tags* m_pTags;
Cluster** m_clusters;
long m_clusterCount; // number of entries for which m_index >= 0
long m_clusterPreloadCount; // number of entries for which m_index < 0

View File

@ -336,6 +336,21 @@ int main(int argc, char* argv[]) {
} while (cue != NULL);
}
const mkvparser::Tags* const tags = pSegment->GetTags();
if (tags && tags->GetTagCount() > 0) {
printf("\t\tTags\n");
for (int i = 0; i < tags->GetTagCount(); ++i) {
const mkvparser::Tags::Tag* const tag = tags->GetTag(i);
printf("\t\t\tTag\n");
for (int j = 0; j < tag->GetSimpleTagCount(); j++) {
const mkvparser::Tags::SimpleTag* const simple_tag =
tag->GetSimpleTag(j);
printf("\t\t\t\tSimple Tag \"%s\" Value \"%s\"\n",
simple_tag->GetTagName(), simple_tag->GetTagString());
}
}
}
fflush(stdout);
return 0;
}

View File

@ -51,6 +51,7 @@ void Usage() {
printf(" -audio_track_number <int> >0 Changes the audio track number\n");
printf(" -video_track_number <int> >0 Changes the video track number\n");
printf(" -chunking <string> Chunk output\n");
printf(" -copy_tags <int> >0 Copies the tags\n");
printf("\n");
printf("Video options:\n");
printf(" -display_width <int> Display width in pixels\n");
@ -159,6 +160,7 @@ int main(int argc, char* argv[]) {
int audio_track_number = 0; // 0 tells muxer to decide.
int video_track_number = 0; // 0 tells muxer to decide.
bool chunking = false;
bool copy_tags = false;
const char* chunk_name = NULL;
bool output_cues_block_number = true;
@ -212,6 +214,8 @@ int main(int argc, char* argv[]) {
} else if (!strcmp("-chunking", argv[i]) && i < argc_check) {
chunking = true;
chunk_name = argv[++i];
} else if (!strcmp("-copy_tags", argv[i]) && i < argc_check) {
copy_tags = strtol(argv[++i], &end, 10) == 0 ? false : true;
} else if (!strcmp("-display_width", argv[i]) && i < argc_check) {
display_width = strtol(argv[++i], &end, 10);
} else if (!strcmp("-display_height", argv[i]) && i < argc_check) {
@ -298,6 +302,21 @@ int main(int argc, char* argv[]) {
info->set_timecode_scale(timeCodeScale);
info->set_writing_app("sample_muxer");
const mkvparser::Tags* const tags = parser_segment->GetTags();
if (copy_tags && tags) {
for (int i = 0; i < tags->GetTagCount(); i++) {
const mkvparser::Tags::Tag* const tag = tags->GetTag(i);
mkvmuxer::Tag* muxer_tag = muxer_segment.AddTag();
for (int j = 0; j < tag->GetSimpleTagCount(); j++) {
const mkvparser::Tags::SimpleTag* const simple_tag =
tag->GetSimpleTag(j);
muxer_tag->add_simple_tag(simple_tag->GetTagName(),
simple_tag->GetTagString());
}
}
}
// Set Tracks element attributes
const mkvparser::Tracks* const parser_tracks = parser_segment->GetTracks();
unsigned long i = 0;

View File

@ -133,7 +133,13 @@ enum MkvId {
kMkvChapterDisplay = 0x80,
kMkvChapString = 0x85,
kMkvChapLanguage = 0x437C,
kMkvChapCountry = 0x437E
kMkvChapCountry = 0x437E,
// Tags
kMkvTags = 0x1254C367,
kMkvTag = 0x7373,
kMkvSimpleTag = 0x67C8,
kMkvTagName = 0x45A3,
kMkvTagString = 0x4487
};
} // end namespace mkvmuxer