7ef225de9f
Change-Id: If72d31ca4828adf39e4637003979a314e5dda98e
237 lines
5.0 KiB
C++
237 lines
5.0 KiB
C++
#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);
|
|
}
|