// Copyright 2014 Google LLC. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd #include #include #include #include #include "absl/flags/parse.h" #include "absl/flags/usage.h" #include "absl/flags/usage_config.h" #include "packager/app/mpd_generator_flags.h" #include "packager/mpd/util/mpd_writer.h" #include "packager/tools/license_notice.h" #include "packager/version/version.h" #include "vlog_flags.h" #if defined(OS_WIN) #include #include #endif // defined(OS_WIN) ABSL_FLAG(bool, licenses, false, "Dump licenses."); ABSL_FLAG(std::string, test_packager_version, "", "Packager version for testing. Should be used for testing only."); namespace shaka { namespace { const char kUsage[] = "MPD generation driver program.\n" "This program accepts MediaInfo files in human readable text " "format and outputs an MPD.\n" "The main use case for this is to output MPD for VOD.\n" "Limitations:\n" " Each MediaInfo can only have one of VideoInfo, AudioInfo, or TextInfo.\n" " There will be at most 3 AdaptationSets in the MPD, i.e. 1 video, 1 " "audio, and 1 text.\n" "Sample Usage:\n" "%s --input=\"video1.media_info,video2.media_info,audio1.media_info\" " "--output=\"video_audio.mpd\""; enum ExitStatus { kSuccess = 0, kEmptyInputError, kEmptyOutputError, kFailedToWriteMpdToFileError }; ExitStatus CheckRequiredFlags() { if (absl::GetFlag(FLAGS_input).empty()) { LOG(ERROR) << "--input is required."; return kEmptyInputError; } if (absl::GetFlag(FLAGS_output).empty()) { LOG(ERROR) << "--output is required."; return kEmptyOutputError; } return kSuccess; } ExitStatus RunMpdGenerator() { DCHECK_EQ(CheckRequiredFlags(), kSuccess); std::vector base_urls; typedef std::vector::const_iterator Iterator; std::vector input_files = absl::StrSplit(absl::GetFlag(FLAGS_input), ",", absl::AllowEmpty()); if (!absl::GetFlag(FLAGS_base_urls).empty()) { base_urls = absl::StrSplit(absl::GetFlag(FLAGS_base_urls), ",", absl::AllowEmpty()); } MpdWriter mpd_writer; for (Iterator it = base_urls.begin(); it != base_urls.end(); ++it) mpd_writer.AddBaseUrl(*it); for (const std::string& file : input_files) { if (!mpd_writer.AddFile(file)) { LOG(WARNING) << "MpdWriter failed to read " << file << ", skipping."; } } if (!mpd_writer.WriteMpdToFile(absl::GetFlag(FLAGS_output).c_str())) { LOG(ERROR) << "Failed to write MPD to " << absl::GetFlag(FLAGS_output); return kFailedToWriteMpdToFileError; } return kSuccess; } int MpdMain(int argc, char** argv) { absl::FlagsUsageConfig flag_config; flag_config.version_string = []() -> std::string { return "mpd_generator version " + GetPackagerVersion() + "\n"; }; flag_config.contains_help_flags = [](absl::string_view flag_file_name) -> bool { return true; }; absl::SetFlagsUsageConfig(flag_config); auto usage = absl::StrFormat(kUsage, argv[0]); absl::SetProgramUsageMessage(usage); absl::ParseCommandLine(argc, argv); if (absl::GetFlag(FLAGS_licenses)) { for (const char* line : kLicenseNotice) std::cout << line << std::endl; return kSuccess; } ExitStatus status = CheckRequiredFlags(); if (status != kSuccess) { std::cerr << "Usage " << absl::ProgramUsageMessage(); return status; } register_flags_with_glog(); if (!absl::GetFlag(FLAGS_test_packager_version).empty()) SetPackagerVersionForTesting(absl::GetFlag(FLAGS_test_packager_version)); return RunMpdGenerator(); } } // namespace } // namespace shaka #if defined(OS_WIN) // Windows wmain, which converts wide character arguments to UTF-8. int wmain(int argc, wchar_t* argv[], wchar_t* envp[]) { std::unique_ptr> utf8_argv( new char*[argc], [argc](char** utf8_args) { // TODO(tinskip): This leaks, but if this code is enabled, it crashes. // Figure out why. I suspect gflags does something funny with the // argument array. // for (int idx = 0; idx < argc; ++idx) // delete[] utf8_args[idx]; delete[] utf8_args; }); std::wstring_convert> converter; for (int idx = 0; idx < argc; ++idx) { std::string utf8_arg(converter.to_bytes(argv[idx])); utf8_arg += '\0'; utf8_argv[idx] = new char[utf8_arg.size()]; memcpy(utf8_argv[idx], &utf8_arg[0], utf8_arg.size()); } // Because we just converted wide character args into UTF8, and because // std::filesystem::u8path is used to interpret all std::string paths as // UTF8, we should set the locale to UTF8 as well, for the transition point // to C library functions like fopen to work correctly with non-ASCII paths. std::setlocale(LC_ALL, ".UTF8"); return shaka::MpdMain(argc, utf8_argv.get()); } #else int main(int argc, char** argv) { return shaka::MpdMain(argc, argv); } #endif // !defined(OS_WIN)