sample_muxer: added WebVTT support
Change-Id: If72d31ca4828adf39e4637003979a314e5dda98e
diff --git a/sample_muxer_metadata.cc b/sample_muxer_metadata.cc
new file mode 100644
index 0000000..0288b27
--- /dev/null
+++ b/sample_muxer_metadata.cc
@@ -0,0 +1,236 @@
+#include "sample_muxer_metadata.h"
+#include <string>
+#include "vttreader.h"
+
+using std::string;
+
+SampleMuxerMetadata::SampleMuxerMetadata() : segment_(NULL) {
+}
+
+void SampleMuxerMetadata::Init(mkvmuxer::Segment* s) {
+ segment_ = s;
+}
+
+bool SampleMuxerMetadata::Load(const char* file, Kind kind) {
+ mkvmuxer::uint64 track_num;
+
+ if (!AddTrack(kind, &track_num)) {
+ printf("Unable to add track for WebVTT file \"%s\"\n", file);
+ return false;
+ }
+
+ return Parse(file, kind, track_num);
+}
+
+bool SampleMuxerMetadata::Write(mkvmuxer::int64 time_ns) {
+ typedef cues_set_t::iterator iter_t;
+
+ iter_t i = cues_set_.begin();
+ const iter_t j = cues_set_.end();
+
+ while (i != j) {
+ const cues_set_t::value_type& v = *i;
+
+ if (time_ns >= 0 && v > time_ns)
+ return true; // nothing else to do just yet
+
+ if (!v.Write(segment_)) {
+ printf("\nCould not add metadata.\n");
+ return false; // error
+ }
+
+ cues_set_.erase(i++);
+ }
+
+ return true;
+}
+
+bool SampleMuxerMetadata::AddTrack(
+ Kind kind,
+ mkvmuxer::uint64* track_num) {
+ *track_num = 0;
+
+ // Track number value 0 means "let muxer choose track number"
+ mkvmuxer::Track* const track = segment_->AddTrack(0);
+
+ if (track == NULL) // error
+ return false;
+
+ // Return the track number value chosen by the muxer
+ *track_num = track->number();
+
+ int type;
+ const char* codec_id;
+
+ switch (kind) {
+ case kSubtitles:
+ type = 0x11;
+ codec_id = "D_WEBVTT/SUBTITLES";
+ break;
+
+ case kCaptions:
+ type = 0x11;
+ codec_id = "D_WEBVTT/CAPTIONS";
+ break;
+
+ case kDescriptions:
+ type = 0x21;
+ codec_id = "D_WEBVTT/DESCRIPTIONS";
+ break;
+
+ case kMetadata:
+ type = 0x21;
+ codec_id = "D_WEBVTT/METADATA";
+ break;
+
+ default:
+ return false;
+ }
+
+ track->set_type(type);
+ track->set_codec_id(codec_id);
+
+ // TODO(matthewjheaney): set name and language
+
+ return true;
+}
+
+bool SampleMuxerMetadata::Parse(
+ const char* file,
+ Kind /* kind */,
+ mkvmuxer::uint64 track_num) {
+ libwebvtt::VttReader r;
+ int e = r.Open(file);
+
+ if (e) {
+ printf("Unable to open WebVTT file: \"%s\"\n", file);
+ return false;
+ }
+
+ libwebvtt::Parser p(&r);
+
+ e = p.Init();
+
+ if (e < 0) { // error
+ printf("Error parsing WebVTT file: \"%s\"\n", file);
+ return false;
+ }
+
+ SortableCue cue;
+ cue.track_num = track_num;
+
+ libwebvtt::Time t;
+ t.hours = -1;
+
+ for (;;) {
+ cue_t& c = cue.cue;
+ e = p.Parse(&c);
+
+ if (e < 0) { // error
+ printf("Error parsing WebVTT file: \"%s\"\n", file);
+ return false;
+ }
+
+ if (e > 0) // EOF
+ return true;
+
+ if (c.start_time >= t) {
+ t = c.start_time;
+ } else {
+ printf("bad WebVTT cue timestamp (out-of-order)\n");
+ return false;
+ }
+
+ if (c.stop_time < c.start_time) {
+ printf("bad WebVTT cue timestamp (stop < start)\n");
+ return false;
+ }
+
+ cues_set_.insert(cue);
+ }
+}
+
+void SampleMuxerMetadata::MakeFrame(const cue_t& c, string* pf) {
+ pf->clear();
+ WriteCueIdentifier(c.identifier, pf);
+ WriteCueSettings(c.settings, pf);
+ WriteCuePayload(c.payload, pf);
+}
+
+void SampleMuxerMetadata::WriteCueIdentifier(
+ const string& identifier,
+ string* pf) {
+ pf->append(identifier);
+ pf->push_back('\x0A'); // LF
+}
+
+void SampleMuxerMetadata::WriteCueSettings(
+ const cue_t::settings_t& settings,
+ string* pf) {
+ if (settings.empty()) {
+ pf->push_back('\x0A'); // LF
+ return;
+ }
+
+ typedef cue_t::settings_t::const_iterator iter_t;
+
+ iter_t i = settings.begin();
+ const iter_t j = settings.end();
+
+ for (;;) {
+ const libwebvtt::Setting& setting = *i++;
+
+ pf->append(setting.name);
+ pf->push_back(':');
+ pf->append(setting.value);
+
+ if (i == j)
+ break;
+
+ pf->push_back(' '); // separate settings with whitespace
+ }
+
+ pf->push_back('\x0A'); // LF
+}
+
+void SampleMuxerMetadata::WriteCuePayload(
+ const cue_t::payload_t& payload,
+ string* pf) {
+ typedef cue_t::payload_t::const_iterator iter_t;
+
+ iter_t i = payload.begin();
+ const iter_t j = payload.end();
+
+ while (i != j) {
+ const string& line = *i++;
+ pf->append(line);
+ pf->push_back('\x0A'); // LF
+ }
+}
+
+bool SampleMuxerMetadata::SortableCue::Write(
+ mkvmuxer::Segment* segment) const {
+ // Cue start time expressed in milliseconds
+ const mkvmuxer::int64 start_ms = cue.start_time.presentation();
+
+ // Cue start time expressed in nanoseconds (MKV time)
+ const mkvmuxer::int64 start_ns = start_ms * 1000000;
+
+ // Cue stop time expressed in milliseconds
+ const mkvmuxer::int64 stop_ms = cue.stop_time.presentation();
+
+ // Cue stop time expressed in nanonseconds
+ const mkvmuxer::int64 stop_ns = stop_ms * 1000000;
+
+ // Metadata blocks always specify the block duration.
+ const mkvmuxer::int64 duration_ns = stop_ns - start_ns;
+
+ string frame;
+ MakeFrame(cue, &frame);
+
+ typedef const mkvmuxer::uint8* data_t;
+ const data_t buf = reinterpret_cast<data_t>(frame.data());
+ const mkvmuxer::uint64 len = frame.length();
+
+ return segment->AddMetadata(buf, len, track_num, start_ns, duration_ns);
+}