in vireo/internal/demux/webm.cpp [107:237]
bool finish_initialization() {
long long pos = 0;
mkvparser::EBMLHeader ebml_header;
ebml_header.Parse(&reader, pos);
unique_ptr<mkvparser::Segment, function<void(mkvparser::Segment*)>> segment = { nullptr, [](mkvparser::Segment* ptr){ delete ptr; } };
mkvparser::Segment* segment_ptr;
THROW_IF(mkvparser::Segment::CreateInstance(&reader, pos, segment_ptr) != 0, Invalid);
segment.reset(segment_ptr);
CHECK(segment);
THROW_IF(segment->Load() < 0, Invalid);
const mkvparser::SegmentInfo* const segment_info = segment->GetInfo();
CHECK(segment_info);
THROW_IF(segment_info->GetTimeCodeScale() != kMicroSecondScale, Unsupported);
duration_in_ns = segment_info->GetDuration(); // in nanoseconds
THROW_IF(!duration_in_ns, Unsupported);
const mkvparser::Tracks* const parser_tracks = segment->GetTracks();
CHECK(parser_tracks);
// Parse track info
const uint64_t num_tracks = parser_tracks->GetTracksCount();
for (uint32_t track_number = 0; track_number < num_tracks; ++track_number) {
const mkvparser::Track* const track = parser_tracks->GetTrackByIndex(track_number);
if (!track) {
continue;
}
const uint64_t track_type = track->GetType();
SampleType type = SampleType::Unknown;
if (track_type == mkvparser::Track::kVideo) {
type = SampleType::Video;
} else if (track_type == mkvparser::Track::kAudio){
type = SampleType::Audio;
} else {
continue;
}
if (tracks(type).track_ID) { // Already found a track of the same type
continue;
}
tracks(type).track_ID = track->GetNumber();
CHECK(tracks(type).track_ID);
if (type == SampleType::Video) {
const mkvparser::VideoTrack* const video_track = static_cast<const mkvparser::VideoTrack*>(track);
CHECK(video_track);
video.width = video_track->GetWidth();
video.height = video_track->GetHeight();
tracks(type).timescale = kTimescale;
if (string(track->GetCodecId()) == "V_VP8") {
video.codec = settings::Video::Codec::VP8;
}
THROW_IF(!security::valid_dimensions(video.width, video.height), Unsafe);
THROW_IF(video.codec != settings::Video::Codec::VP8, Unsupported);
} else {
const mkvparser::AudioTrack* const audio_track = static_cast<const mkvparser::AudioTrack*>(track);
audio.channels = audio_track->GetChannels();
tracks(type).timescale = audio_track->GetSamplingRate();
THROW_IF(find(kSampleRate.begin(), kSampleRate.end(), tracks(type).timescale) == kSampleRate.end(), Unsupported);
if (string(track->GetCodecId()) == "A_VORBIS") {
audio.codec = settings::Audio::Codec::Vorbis;
}
THROW_IF(!audio.channels, Invalid);
THROW_IF(audio.channels > 2, Unsupported);
THROW_IF(audio.codec != settings::Audio::Codec::Vorbis, Unsupported);
}
tracks(type).duration = common::round_divide(duration_in_ns, (uint64_t)tracks(type).timescale, kNanoSecondScale);
}
// Parse samples for existing tracks
const mkvparser::Cluster* cluster = segment->GetFirst();
while (cluster && !cluster->EOS()) {
const mkvparser::BlockEntry* block_entry = nullptr;
THROW_IF(cluster->GetFirst(block_entry) != 0, Invalid);
while (block_entry && !block_entry->EOS()) {
const mkvparser::Block* const block = block_entry->GetBlock();
CHECK(block);
THROW_IF(block->IsInvisible(), Unsupported);
THROW_IF(block->GetFrameCount() != 1, Unsupported); // > 1 frame per block is allowed but we don't know how to deal with them!
const uint64_t trackNum = block->GetTrackNumber();
const mkvparser::Track* const parser_track = parser_tracks->GetTrackByNumber(static_cast<unsigned long>(trackNum));
CHECK(parser_track);
const uint64_t track_type = parser_track->GetType();
SampleType type = SampleType::Unknown;
if (track_type == mkvparser::Track::kVideo) {
type = SampleType::Video;
} else if (track_type == mkvparser::Track::kAudio) {
type = SampleType::Audio;
}
THROW_IF(!tracks(type).track_ID, Invalid);
// Parse sample data and add to track
if (type == SampleType::Video || type == SampleType::Audio) {
// pts / dts
const uint64_t time_ns = block->GetTime(cluster); // in nanoseconds
uint64_t ts = common::round_divide(time_ns, (uint64_t)tracks(type).timescale, kNanoSecondScale);
THROW_IF(ts > numeric_limits<uint32_t>::max(), Overflow);
// keyframe
const bool keyframe = block->IsKey();
// nal, pos, size
const mkvparser::Block::Frame& frame = block->GetFrame(0);
THROW_IF(frame.pos > numeric_limits<uint32_t>::max(), Overflow);
THROW_IF(frame.len > numeric_limits<uint32_t>::max(), Overflow);
auto nal = [reader = &reader, pos = frame.pos, len = frame.len]() -> common::Data32 {
common::Data32 data(new uint8_t[len], (uint32_t)len, [](uint8_t* ptr){ delete[] ptr; });
THROW_IF(reader->Read(pos, len, (uint8_t*)data.data()) != 0, Invalid);
return move(data);
};
if (type == SampleType::Audio) {
audio.bitrate += frame.len;
}
// add sample to track
Sample sample((uint32_t)ts, (uint32_t)ts, keyframe, type, nal, (uint32_t)frame.pos, (uint32_t)frame.len);
tracks(type).samples.push_back(sample);
THROW_IF(cluster->GetNext(block_entry, block_entry) != 0, Invalid);
}
}
cluster = segment->GetNext(cluster);
}
if (tracks(SampleType::Audio).duration) {
audio.bitrate = (uint32_t)common::round_divide((uint64_t)audio.bitrate,
(uint64_t)tracks(SampleType::Audio).timescale * CHAR_BIT,
(uint64_t)tracks(SampleType::Audio).duration);
}
initialized = true;
return true;
}