bool finish_initialization()

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;
  }