Shaka Packager SDK
Loading...
Searching...
No Matches
mpd_generator.cc
1// Copyright 2014 Google LLC. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file or at
5// https://developers.google.com/open-source/licenses/bsd
6
7#include <iostream>
8
9#if defined(OS_WIN)
10#include <codecvt>
11#include <functional>
12#endif // defined(OS_WIN)
13
14#include <absl/flags/parse.h>
15#include <absl/flags/usage.h>
16#include <absl/flags/usage_config.h>
17#include <absl/log/check.h>
18#include <absl/log/initialize.h>
19#include <absl/log/log.h>
20#include <absl/strings/str_format.h>
21#include <absl/strings/str_split.h>
22
23#include <packager/app/mpd_generator_flags.h>
24#include <packager/mpd/util/mpd_writer.h>
25#include <packager/tools/license_notice.h>
26#include <packager/version/version.h>
27
28ABSL_FLAG(bool, licenses, false, "Dump licenses.");
29ABSL_FLAG(std::string,
30 test_packager_version,
31 "",
32 "Packager version for testing. Should be used for testing only.");
33
34// From absl/log:
35ABSL_DECLARE_FLAG(int, stderrthreshold);
36
37namespace shaka {
38namespace {
39const char kUsage[] =
40 "MPD generation driver program.\n"
41 "This program accepts MediaInfo files in human readable text "
42 "format and outputs an MPD.\n"
43 "The main use case for this is to output MPD for VOD.\n"
44 "Limitations:\n"
45 " Each MediaInfo can only have one of VideoInfo, AudioInfo, or TextInfo.\n"
46 " There will be at most 3 AdaptationSets in the MPD, i.e. 1 video, 1 "
47 "audio, and 1 text.\n"
48 "Sample Usage:\n"
49 "%s --input=\"video1.media_info,video2.media_info,audio1.media_info\" "
50 "--output=\"video_audio.mpd\"";
51
52enum ExitStatus {
53 kSuccess = 0,
54 kEmptyInputError,
55 kEmptyOutputError,
56 kFailedToWriteMpdToFileError
57};
58
59ExitStatus CheckRequiredFlags() {
60 if (absl::GetFlag(FLAGS_input).empty()) {
61 LOG(ERROR) << "--input is required.";
62 return kEmptyInputError;
63 }
64
65 if (absl::GetFlag(FLAGS_output).empty()) {
66 LOG(ERROR) << "--output is required.";
67 return kEmptyOutputError;
68 }
69
70 return kSuccess;
71}
72
73ExitStatus RunMpdGenerator() {
74 DCHECK_EQ(CheckRequiredFlags(), kSuccess);
75 std::vector<std::string> base_urls;
76 typedef std::vector<std::string>::const_iterator Iterator;
77
78 std::vector<std::string> input_files =
79 absl::StrSplit(absl::GetFlag(FLAGS_input), ",", absl::AllowEmpty());
80
81 if (!absl::GetFlag(FLAGS_base_urls).empty()) {
82 base_urls =
83 absl::StrSplit(absl::GetFlag(FLAGS_base_urls), ",", absl::AllowEmpty());
84 }
85
86 MpdWriter mpd_writer;
87 for (Iterator it = base_urls.begin(); it != base_urls.end(); ++it)
88 mpd_writer.AddBaseUrl(*it);
89
90 for (const std::string& file : input_files) {
91 if (!mpd_writer.AddFile(file)) {
92 LOG(WARNING) << "MpdWriter failed to read " << file << ", skipping.";
93 }
94 }
95
96 if (!mpd_writer.WriteMpdToFile(absl::GetFlag(FLAGS_output).c_str())) {
97 LOG(ERROR) << "Failed to write MPD to " << absl::GetFlag(FLAGS_output);
98 return kFailedToWriteMpdToFileError;
99 }
100
101 return kSuccess;
102}
103
104int MpdMain(int argc, char** argv) {
105 absl::FlagsUsageConfig flag_config;
106 flag_config.version_string = []() -> std::string {
107 return "mpd_generator version " + GetPackagerVersion() + "\n";
108 };
109 flag_config.contains_help_flags =
110 [](absl::string_view flag_file_name) -> bool { return true; };
111 absl::SetFlagsUsageConfig(flag_config);
112
113 auto usage = absl::StrFormat(kUsage, argv[0]);
114 absl::SetProgramUsageMessage(usage);
115
116 // Before parsing the command line, change the default value of some flags
117 // provided by libraries.
118
119 // Always log to stderr. Log levels are still controlled by --minloglevel.
120 absl::SetFlag(&FLAGS_stderrthreshold, 0);
121
122 absl::ParseCommandLine(argc, argv);
123
124 if (absl::GetFlag(FLAGS_licenses)) {
125 for (const char* line : kLicenseNotice)
126 std::cout << line << std::endl;
127 return kSuccess;
128 }
129
130 ExitStatus status = CheckRequiredFlags();
131 if (status != kSuccess) {
132 std::cerr << "Usage " << absl::ProgramUsageMessage();
133 return status;
134 }
135
136 absl::InitializeLog();
137
138 if (!absl::GetFlag(FLAGS_test_packager_version).empty())
139 SetPackagerVersionForTesting(absl::GetFlag(FLAGS_test_packager_version));
140
141 return RunMpdGenerator();
142}
143
144} // namespace
145} // namespace shaka
146
147#if defined(OS_WIN)
148// Windows wmain, which converts wide character arguments to UTF-8.
149int wmain(int argc, wchar_t* argv[], wchar_t* envp[]) {
150 std::unique_ptr<char* [], std::function<void(char**)>> utf8_argv(
151 new char*[argc], [argc](char** utf8_args) {
152 // TODO(tinskip): This leaks, but if this code is enabled, it crashes.
153 // Figure out why. I suspect gflags does something funny with the
154 // argument array.
155 // for (int idx = 0; idx < argc; ++idx)
156 // delete[] utf8_args[idx];
157 delete[] utf8_args;
158 });
159 std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
160
161 for (int idx = 0; idx < argc; ++idx) {
162 std::string utf8_arg(converter.to_bytes(argv[idx]));
163 utf8_arg += '\0';
164 utf8_argv[idx] = new char[utf8_arg.size()];
165 memcpy(utf8_argv[idx], &utf8_arg[0], utf8_arg.size());
166 }
167
168 // Because we just converted wide character args into UTF8, and because
169 // std::filesystem::u8path is used to interpret all std::string paths as
170 // UTF8, we should set the locale to UTF8 as well, for the transition point
171 // to C library functions like fopen to work correctly with non-ASCII paths.
172 std::setlocale(LC_ALL, ".UTF8");
173
174 return shaka::MpdMain(argc, utf8_argv.get());
175}
176#else
177int main(int argc, char** argv) {
178 return shaka::MpdMain(argc, argv);
179}
180#endif // !defined(OS_WIN)
All the methods that are virtual are virtual for mocking.