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