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