int main()

in vireo/tools/unchunk/main.cpp [43:162]


int main(int argc, const char* argv[]) {
  // Usage
  auto print_usage = [](const string name) {
    const int opt_len = 30;
    cout << "Usage: " << name << " [options] dts_offsets chunks original" << endl;
    cout << std::left << std::setw(opt_len) << "dts_offsets" << "dts offsets in sec for audio/video tracks in each chunk (format: a0:v0;a1:v1...) (e.g. 0.0:0.0;1.0:1.01...) (-1.0 means unavailable)" << endl;
    cout << std::left << std::setw(opt_len) << "chunks"      << "list of chunks" << endl;
    cout << std::left << std::setw(opt_len) << "original"    << "unchunked original file to be created" << endl;
    cout << endl << "Options:" << endl;
    cout << std::left << std::setw(opt_len) << "--help"       << "show usage" << endl;
  };

  struct dts_offset_in_sec {
    double audio;
    double video;
  };
  auto parse_dts_offsets = [](string dts_offsets_str) -> vector<dts_offset_in_sec> {
    vector<dts_offset_in_sec> dts_offsets;
    stringstream ss_semicolon(dts_offsets_str);
    std::string chunk_dts_offsets;
    __try {
      while(getline(ss_semicolon, chunk_dts_offsets, ';')) {
        stringstream ss_colon(chunk_dts_offsets);
        string audio;
        getline(ss_colon, audio, ':');
        string video;
        getline(ss_colon, video, ':');
        dts_offsets.push_back({ stod(audio), stod(video) });
      }
    } __catch (std::exception& e) {
      cerr << "error parsing dts offsets string \"" << dts_offsets_str << "\" (expected format: a0:v0;a1:v1...) (e.g. 0.0:0.0;1.0:1.01...) (-1.0 means unavailable)" << endl;
    }
    return dts_offsets;
  };

  const string name = common::Path::Filename(argv[0]);
  if (argc < 2) {
    print_usage(name);
    return 1;
  }

  // Parse arguments
  int last_arg = 1;
  for (int i = 1; i < argc; ++i) {
    if (strcmp(argv[i], "--help") == 0) {
      print_usage(name);
      return 0;
    }
  }
  uint32_t dts_offsets_arg = last_arg;
  uint32_t first_chunk_arg = dts_offsets_arg + 1;
  int32_t num_chunks = argc - first_chunk_arg - 1;
  if (num_chunks < 0) {  // one more arg for outfile
    cerr << "Need to specify dts_offsets, infiles and an outfile" << endl;
    return 1;
  }
  vector<dts_offset_in_sec> dts_offsets = parse_dts_offsets(argv[last_arg]);
  if (dts_offsets.size() == 0 || dts_offsets.size() != num_chunks) {
    cerr << "dts offsets must be defined for all chunks, # dts offsets = " << dts_offsets.size() << ", # chunks = " << num_chunks << endl;
    return 1;
  }

  __try {
    vector<common::EditBox> edit_boxes;
    auto audio_settings = settings::Audio::None;
    auto video_settings = settings::Video::None;
    bool audio_settings_initialized = false;
    bool video_settings_initialized = false;
    vector<decode::Sample> audio_samples;
    vector<decode::Sample> video_samples;
    uint64_t audio_prev_dts = 0;
    uint64_t video_prev_dts = 0;

    // Unchunk
    for (uint32_t i = 0; i < dts_offsets.size(); ++i) {
      const auto& movie = demux::Movie(string(argv[first_chunk_arg + i]));
      const auto& dts_offset = dts_offsets[i];
      if (movie.audio_track.count()) {
        if (!audio_settings_initialized) {
          audio_settings = movie.audio_track.settings();
          edit_boxes.insert(edit_boxes.begin(), movie.audio_track.edit_boxes().begin(), movie.audio_track.edit_boxes().end());
          audio_settings_initialized = true;
        }
        for (const auto& sample: movie.audio_track) {
          uint64_t dts = (dts_offset.audio < 0.0) ? sample.pts : sample.dts + (uint64_t)round(dts_offset.audio * movie.audio_track.settings().timescale);
          CHECK(audio_samples.size() == 0 || dts > audio_prev_dts);
          audio_samples.push_back(decode::Sample(sample.pts, dts, sample.keyframe, sample.type, sample.nal));
          audio_prev_dts = dts;
        }
      }
      if (movie.video_track.count()) {
        if (!video_settings_initialized) {
          video_settings = movie.video_track.settings();
          edit_boxes.insert(edit_boxes.begin(), movie.video_track.edit_boxes().begin(), movie.video_track.edit_boxes().end());
          video_settings_initialized = true;
        }
        for (const auto& sample: movie.video_track) {
          CHECK(dts_offset.video >= 0);
          uint64_t dts = sample.dts + (uint64_t)round(dts_offset.video * movie.video_track.settings().timescale);
          CHECK(video_samples.size() == 0 || dts > video_prev_dts);
          video_samples.push_back(decode::Sample(sample.pts, dts, sample.keyframe, sample.type, sample.nal));
          video_prev_dts = dts;
        }
      }
    }

    // Mux
    auto audio_track = functional::Audio<encode::Sample>(functional::Audio<decode::Sample>(audio_samples, audio_settings), encode::Sample::Convert);
    auto video_track = functional::Video<encode::Sample>(functional::Video<decode::Sample>(video_samples, video_settings), encode::Sample::Convert);
    mux::MP4 mp4_encoder(audio_track, video_track, edit_boxes);
    const string abs_dst = vireo::common::Path::MakeAbsolute(argv[first_chunk_arg + num_chunks]);
    util::save(abs_dst, mp4_encoder());
  } __catch (std::exception& e) {
    cerr << "Error unchunking: " << e.what() << endl;
    return 1;
  }

  cout << "success" << endl;
  return 0;
}