• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023, Alliance for Open Media. All rights reserved
3  *
4  * This source code is subject to the terms of the BSD 3-Clause Clear License
5  * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6  * License was not distributed with this source code in the LICENSE file, you
7  * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
8  * Alliance for Open Media Patent License 1.0 was not distributed with this
9  * source code in the PATENTS file, you can obtain it at
10  * www.aomedia.org/license/patent.
11  */
12 #include <cstdint>
13 #include <filesystem>
14 #include <fstream>
15 #include <ios>
16 #include <sstream>
17 #include <string>
18 
19 #include "absl/flags/flag.h"
20 #include "absl/flags/parse.h"
21 #include "absl/flags/usage.h"
22 #include "absl/log/log.h"
23 #include "absl/status/status.h"
24 #include "absl/strings/str_cat.h"
25 #include "absl/strings/string_view.h"
26 #include "iamf/cli/adm_to_user_metadata/app/adm_to_user_metadata_main_lib.h"
27 #include "iamf/cli/encoder_main_lib.h"
28 #include "iamf/cli/proto/test_vector_metadata.pb.h"
29 #include "iamf/cli/proto/user_metadata.pb.h"
30 #include "iamf/obu/ia_sequence_header.h"
31 #include "src/google/protobuf/text_format.h"
32 
33 // Flags to parse input user metadata.
34 ABSL_FLAG(
35     std::string, user_metadata_filename, "",
36     "Filename of the proto containing user metadata. It will be read as "
37     "a textproto if the file extension is `.txtpb or `.textproto`. It will "
38     "be read as a binary proto if the file extension is `.binpb`.  Exactly one "
39     "of --adm_filename or --user_metadata_filename must be provided.");
40 ABSL_FLAG(std::string, input_wav_directory, "",
41           "Directory containing the input wav files. Used only if "
42           "--user_metadata_filename is provided.");
43 
44 // Flags to parse input ADM file.
45 ABSL_FLAG(std::string, adm_filename, "",
46           "Filename of the ADM BW64 file to use. Exactly one of --adm_filename "
47           "or --user_metadata_filename must be provided.");
48 ABSL_FLAG(std::string, adm_profile_version, "base",
49           "IAMF version to be used: (base/enhanced). Used only if "
50           "--adm_filename is provided.");
51 ABSL_FLAG(int32_t, adm_importance_threshold, 0,
52           "Importance value used to skip an audioObject. Clamped to [0, 10]. "
53           "Used only if --adm_filename is provided.");
54 ABSL_FLAG(int32_t, adm_frame_duration_ms, 10,
55           "Target frame duration in milliseconds. The actual frame duration "
56           "may vary slightly. Used only if --adm_filename is provided.");
57 
58 // Flags to control output directory for either type of input.
59 ABSL_FLAG(std::string, output_iamf_directory, "",
60           "Output directory for iamf files");
61 // TODO(b/349504599): Add support to write output WAV files.
62 
63 namespace {
64 
65 // Reads in a user metadata proto from a binary or textproto file.
ReadUserMetadataFromFile(const std::filesystem::path & user_metadata_filename)66 absl::StatusOr<iamf_tools_cli_proto::UserMetadata> ReadUserMetadataFromFile(
67     const std::filesystem::path& user_metadata_filename) {
68   std::ifstream user_metadata_file(user_metadata_filename.string(),
69                                    std::ios::binary | std::ios::in);
70   if (!user_metadata_file) {
71     return absl::FailedPreconditionError(
72         absl::StrCat("Error loading user_metadata_filename= ",
73                      user_metadata_filename.string()));
74   }
75 
76   std::ostringstream user_metadata_stream;
77   user_metadata_stream << user_metadata_file.rdbuf();
78 
79   bool is_parse_successful = false;
80   iamf_tools_cli_proto::UserMetadata user_metadata;
81   if (user_metadata_filename.extension() == ".binpb") {
82     is_parse_successful =
83         user_metadata.ParseFromString(user_metadata_stream.str());
84   } else if (user_metadata_filename.extension() == ".textproto" ||
85              user_metadata_filename.extension() == ".txtpb") {
86     is_parse_successful = google::protobuf::TextFormat::ParseFromString(
87         user_metadata_stream.str(), &user_metadata);
88   }
89 
90   if (!is_parse_successful) {
91     return absl::InvalidArgumentError(
92         absl::StrCat("Error parsing proto with user_metadata_filename= ",
93                      user_metadata_filename.string()));
94   }
95 
96   return user_metadata;
97 }
98 
99 // Gets a user metadata proto and directory which the encoder will read wav
100 // files from. The proto may be read directly from a file or be generated based
101 // on an input ADM file.
102 absl::StatusOr<iamf_tools_cli_proto::UserMetadata>
GetUserMetadataAndInputWavDirectory(const std::filesystem::path & input_user_metadata_filename,const std::filesystem::path & adm_filename,std::filesystem::path & encoder_input_wav_directory,const iamf_tools::ProfileVersion profile_version)103 GetUserMetadataAndInputWavDirectory(
104     const std::filesystem::path& input_user_metadata_filename,
105     const std::filesystem::path& adm_filename,
106     std::filesystem::path& encoder_input_wav_directory,
107     const iamf_tools::ProfileVersion profile_version) {
108   if (input_user_metadata_filename.empty() == adm_filename.empty()) {
109     return absl::InvalidArgumentError(
110         "Please provide exactly one of --user_metadata_filename or "
111         "--adm_filename.");
112   } else if (!input_user_metadata_filename.empty()) {
113     // The user directly provided a proto. Load it from the input file.
114     encoder_input_wav_directory =
115         absl::GetFlag(FLAGS_input_wav_directory).empty()
116             ? std::filesystem::path("iamf/cli/testdata/")
117             : std::filesystem::path(absl::GetFlag(FLAGS_input_wav_directory));
118     return ReadUserMetadataFromFile(input_user_metadata_filename);
119   } else {
120     // Generate user metadata and wav files based on the input ADM file.
121     std::ifstream adm_file(adm_filename, std::ios::binary | std::ios::in);
122 
123     // Wav files associated with each audio object will be written to a
124     // temporary directory. The encoder will read back in the wav files from
125     // this temporary directory.
126     const auto temp_wav_file_directory = std::filesystem::temp_directory_path();
127     encoder_input_wav_directory = temp_wav_file_directory;
128     return iamf_tools::adm_to_user_metadata::
129         GenerateUserMetadataAndSpliceWavFiles(
130             std::filesystem::path(adm_filename).stem().string(),
131             absl::GetFlag(FLAGS_adm_frame_duration_ms),
132             absl::GetFlag(FLAGS_adm_importance_threshold),
133             temp_wav_file_directory, adm_file, profile_version);
134   }
135 }
136 
137 }  // namespace
138 
main(int argc,char ** argv)139 int main(int argc, char** argv) {
140   absl::SetProgramUsageMessage(argv[0]);
141   absl::ParseCommandLine(argc, argv);
142 
143   // Log the profile version flag.
144   using enum iamf_tools::ProfileVersion;
145   std::string iamf_profile = absl::GetFlag(FLAGS_adm_profile_version);
146   iamf_tools::ProfileVersion profile_version;
147   if (iamf_profile == "base") {
148     profile_version = kIamfBaseProfile;
149   } else if (iamf_profile == "enhanced") {
150     profile_version = kIamfBaseEnhancedProfile;
151   } else {
152     LOG(ERROR) << "Invalid profile version: " << iamf_profile;
153     return static_cast<int>(absl::StatusCode::kInvalidArgument);
154   }
155   LOG(INFO) << "Using IAMF" << iamf_profile << "profile version.";
156 
157   // Prepare `user_metadata` and `input_wav_directory` depending on the
158   // input source.
159   std::filesystem::path input_wav_directory;
160   const auto& user_metadata = GetUserMetadataAndInputWavDirectory(
161       absl::GetFlag(FLAGS_user_metadata_filename),
162       absl::GetFlag(FLAGS_adm_filename), input_wav_directory, profile_version);
163   if (!user_metadata.ok()) {
164     LOG(ERROR) << user_metadata.status();
165     return static_cast<int>(user_metadata.status().code());
166   }
167 
168   LOG(INFO) << user_metadata;
169 
170   // Get the directory for the output .iamf files.
171   const auto& output_iamf_directory =
172       absl::GetFlag(FLAGS_output_iamf_directory).empty()
173           ? std::filesystem::temp_directory_path()
174           : std::filesystem::path(absl::GetFlag(FLAGS_output_iamf_directory));
175 
176   absl::Status status =
177       iamf_tools::TestMain(*user_metadata, input_wav_directory.string(),
178                            output_iamf_directory.string());
179 
180   // Log success or failure. Success is defined as a valid test vector returning
181   // `absl::OkStatus()` or an invalid test vector returning a different status.
182   const bool test_vector_is_valid =
183       user_metadata->test_vector_metadata().is_valid();
184   std::stringstream ss;
185   ss << "Test case expected to " << (test_vector_is_valid ? "pass" : "fail")
186      << ".\nstatus= " << status;
187   if (test_vector_is_valid == status.ok()) {
188     LOG(INFO) << "Success. " << ss.str();
189   } else {
190     LOG(ERROR) << "Failure. " << ss.str();
191   }
192 
193   return static_cast<int>(status.code());
194 }
195