Shaka Packager SDK
mpd_generator.cc
1 // Copyright 2014 Google Inc. 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 #include "packager/app/mpd_generator_flags.h"
10 #include "packager/app/vlog_flags.h"
11 #include "packager/base/at_exit.h"
12 #include "packager/base/command_line.h"
13 #include "packager/base/logging.h"
14 #include "packager/base/strings/string_split.h"
15 #include "packager/base/strings/stringprintf.h"
16 #include "packager/mpd/util/mpd_writer.h"
17 #include "packager/tools/license_notice.h"
18 #include "packager/version/version.h"
19 
20 #if defined(OS_WIN)
21 #include <codecvt>
22 #include <functional>
23 #include <locale>
24 #endif // defined(OS_WIN)
25 
26 DEFINE_bool(licenses, false, "Dump licenses.");
27 DEFINE_string(test_packager_version,
28  "",
29  "Packager version for testing. Should be used for testing only.");
30 
31 namespace shaka {
32 namespace {
33 const char kUsage[] =
34  "MPD generation driver program.\n"
35  "This program accepts MediaInfo files in human readable text "
36  "format and outputs an MPD.\n"
37  "The main use case for this is to output MPD for VOD.\n"
38  "Limitations:\n"
39  " Each MediaInfo can only have one of VideoInfo, AudioInfo, or TextInfo.\n"
40  " There will be at most 3 AdaptationSets in the MPD, i.e. 1 video, 1 "
41  "audio, and 1 text.\n"
42  "Sample Usage:\n"
43  "%s --input=\"video1.media_info,video2.media_info,audio1.media_info\" "
44  "--output=\"video_audio.mpd\"";
45 
46 enum ExitStatus {
47  kSuccess = 0,
48  kEmptyInputError,
49  kEmptyOutputError,
50  kFailedToWriteMpdToFileError
51 };
52 
53 ExitStatus CheckRequiredFlags() {
54  if (FLAGS_input.empty()) {
55  LOG(ERROR) << "--input is required.";
56  return kEmptyInputError;
57  }
58 
59  if (FLAGS_output.empty()) {
60  LOG(ERROR) << "--output is required.";
61  return kEmptyOutputError;
62  }
63 
64  return kSuccess;
65 }
66 
67 ExitStatus RunMpdGenerator() {
68  DCHECK_EQ(CheckRequiredFlags(), kSuccess);
69  std::vector<std::string> base_urls;
70  typedef std::vector<std::string>::const_iterator Iterator;
71 
72  std::vector<std::string> input_files = base::SplitString(
73  FLAGS_input, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
74 
75  if (!FLAGS_base_urls.empty()) {
76  base_urls = base::SplitString(FLAGS_base_urls, ",", base::KEEP_WHITESPACE,
77  base::SPLIT_WANT_ALL);
78  }
79 
80  MpdWriter mpd_writer;
81  for (Iterator it = base_urls.begin(); it != base_urls.end(); ++it)
82  mpd_writer.AddBaseUrl(*it);
83 
84  for (const std::string& file : input_files) {
85  if (!mpd_writer.AddFile(file)) {
86  LOG(WARNING) << "MpdWriter failed to read " << file << ", skipping.";
87  }
88  }
89 
90  if (!mpd_writer.WriteMpdToFile(FLAGS_output.c_str())) {
91  LOG(ERROR) << "Failed to write MPD to " << FLAGS_output;
92  return kFailedToWriteMpdToFileError;
93  }
94 
95  return kSuccess;
96 }
97 
98 int MpdMain(int argc, char** argv) {
99  base::AtExitManager exit;
100  // Needed to enable VLOG/DVLOG through --vmodule or --v.
101  base::CommandLine::Init(argc, argv);
102 
103  // Set up logging.
104  logging::LoggingSettings log_settings;
105  log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
106  CHECK(logging::InitLogging(log_settings));
107 
108  google::SetVersionString(GetPackagerVersion());
109  google::SetUsageMessage(base::StringPrintf(kUsage, argv[0]));
110  google::ParseCommandLineFlags(&argc, &argv, true);
111  if (FLAGS_licenses) {
112  for (const char* line : kLicenseNotice)
113  std::cout << line << std::endl;
114  return kSuccess;
115  }
116 
117  ExitStatus status = CheckRequiredFlags();
118  if (status != kSuccess) {
119  google::ShowUsageWithFlags("Usage");
120  return status;
121  }
122 
123  if (!FLAGS_test_packager_version.empty())
124  SetPackagerVersionForTesting(FLAGS_test_packager_version);
125 
126  return RunMpdGenerator();
127 }
128 
129 } // namespace
130 } // namespace shaka
131 
132 #if defined(OS_WIN)
133 // Windows wmain, which converts wide character arguments to UTF-8.
134 int wmain(int argc, wchar_t* argv[], wchar_t* envp[]) {
135  std::unique_ptr<char* [], std::function<void(char**)>> utf8_argv(
136  new char*[argc], [argc](char** utf8_args) {
137  // TODO(tinskip): This leaks, but if this code is enabled, it crashes.
138  // Figure out why. I suspect gflags does something funny with the
139  // argument array.
140  // for (int idx = 0; idx < argc; ++idx)
141  // delete[] utf8_args[idx];
142  delete[] utf8_args;
143  });
144  std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
145  for (int idx = 0; idx < argc; ++idx) {
146  std::string utf8_arg(converter.to_bytes(argv[idx]));
147  utf8_arg += '\0';
148  utf8_argv[idx] = new char[utf8_arg.size()];
149  memcpy(utf8_argv[idx], &utf8_arg[0], utf8_arg.size());
150  }
151  return shaka::MpdMain(argc, utf8_argv.get());
152 }
153 #else
154 int main(int argc, char** argv) {
155  return shaka::MpdMain(argc, argv);
156 }
157 #endif // !defined(OS_WIN)
All the methods that are virtual are virtual for mocking.