/* * Copyright (c) 2023, Alliance for Open Media. All rights reserved * * This source code is subject to the terms of the BSD 3-Clause Clear License * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear * License was not distributed with this source code in the LICENSE file, you * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the * Alliance for Open Media Patent License 1.0 was not distributed with this * source code in the PATENTS file, you can obtain it at * www.aomedia.org/license/patent. */ #include #include #include #include #include #include #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/flags/usage.h" #include "absl/log/log.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "iamf/cli/adm_to_user_metadata/app/adm_to_user_metadata_main_lib.h" #include "iamf/cli/encoder_main_lib.h" #include "iamf/cli/proto/test_vector_metadata.pb.h" #include "iamf/cli/proto/user_metadata.pb.h" #include "iamf/obu/ia_sequence_header.h" #include "src/google/protobuf/text_format.h" // Flags to parse input user metadata. ABSL_FLAG( std::string, user_metadata_filename, "", "Filename of the proto containing user metadata. It will be read as " "a textproto if the file extension is `.txtpb or `.textproto`. It will " "be read as a binary proto if the file extension is `.binpb`. Exactly one " "of --adm_filename or --user_metadata_filename must be provided."); ABSL_FLAG(std::string, input_wav_directory, "", "Directory containing the input wav files. Used only if " "--user_metadata_filename is provided."); // Flags to parse input ADM file. ABSL_FLAG(std::string, adm_filename, "", "Filename of the ADM BW64 file to use. Exactly one of --adm_filename " "or --user_metadata_filename must be provided."); ABSL_FLAG(std::string, adm_profile_version, "base", "IAMF version to be used: (base/enhanced). Used only if " "--adm_filename is provided."); ABSL_FLAG(int32_t, adm_importance_threshold, 0, "Importance value used to skip an audioObject. Clamped to [0, 10]. " "Used only if --adm_filename is provided."); ABSL_FLAG(int32_t, adm_frame_duration_ms, 10, "Target frame duration in milliseconds. The actual frame duration " "may vary slightly. Used only if --adm_filename is provided."); // Flags to control output directory for either type of input. ABSL_FLAG(std::string, output_iamf_directory, "", "Output directory for iamf files"); // TODO(b/349504599): Add support to write output WAV files. namespace { // Reads in a user metadata proto from a binary or textproto file. absl::StatusOr ReadUserMetadataFromFile( const std::filesystem::path& user_metadata_filename) { std::ifstream user_metadata_file(user_metadata_filename.string(), std::ios::binary | std::ios::in); if (!user_metadata_file) { return absl::FailedPreconditionError( absl::StrCat("Error loading user_metadata_filename= ", user_metadata_filename.string())); } std::ostringstream user_metadata_stream; user_metadata_stream << user_metadata_file.rdbuf(); bool is_parse_successful = false; iamf_tools_cli_proto::UserMetadata user_metadata; if (user_metadata_filename.extension() == ".binpb") { is_parse_successful = user_metadata.ParseFromString(user_metadata_stream.str()); } else if (user_metadata_filename.extension() == ".textproto" || user_metadata_filename.extension() == ".txtpb") { is_parse_successful = google::protobuf::TextFormat::ParseFromString( user_metadata_stream.str(), &user_metadata); } if (!is_parse_successful) { return absl::InvalidArgumentError( absl::StrCat("Error parsing proto with user_metadata_filename= ", user_metadata_filename.string())); } return user_metadata; } // Gets a user metadata proto and directory which the encoder will read wav // files from. The proto may be read directly from a file or be generated based // on an input ADM file. absl::StatusOr 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) { if (input_user_metadata_filename.empty() == adm_filename.empty()) { return absl::InvalidArgumentError( "Please provide exactly one of --user_metadata_filename or " "--adm_filename."); } else if (!input_user_metadata_filename.empty()) { // The user directly provided a proto. Load it from the input file. encoder_input_wav_directory = absl::GetFlag(FLAGS_input_wav_directory).empty() ? std::filesystem::path("iamf/cli/testdata/") : std::filesystem::path(absl::GetFlag(FLAGS_input_wav_directory)); return ReadUserMetadataFromFile(input_user_metadata_filename); } else { // Generate user metadata and wav files based on the input ADM file. std::ifstream adm_file(adm_filename, std::ios::binary | std::ios::in); // Wav files associated with each audio object will be written to a // temporary directory. The encoder will read back in the wav files from // this temporary directory. const auto temp_wav_file_directory = std::filesystem::temp_directory_path(); encoder_input_wav_directory = temp_wav_file_directory; return iamf_tools::adm_to_user_metadata:: GenerateUserMetadataAndSpliceWavFiles( std::filesystem::path(adm_filename).stem().string(), absl::GetFlag(FLAGS_adm_frame_duration_ms), absl::GetFlag(FLAGS_adm_importance_threshold), temp_wav_file_directory, adm_file, profile_version); } } } // namespace int main(int argc, char** argv) { absl::SetProgramUsageMessage(argv[0]); absl::ParseCommandLine(argc, argv); // Log the profile version flag. using enum iamf_tools::ProfileVersion; std::string iamf_profile = absl::GetFlag(FLAGS_adm_profile_version); iamf_tools::ProfileVersion profile_version; if (iamf_profile == "base") { profile_version = kIamfBaseProfile; } else if (iamf_profile == "enhanced") { profile_version = kIamfBaseEnhancedProfile; } else { LOG(ERROR) << "Invalid profile version: " << iamf_profile; return static_cast(absl::StatusCode::kInvalidArgument); } LOG(INFO) << "Using IAMF" << iamf_profile << "profile version."; // Prepare `user_metadata` and `input_wav_directory` depending on the // input source. std::filesystem::path input_wav_directory; const auto& user_metadata = GetUserMetadataAndInputWavDirectory( absl::GetFlag(FLAGS_user_metadata_filename), absl::GetFlag(FLAGS_adm_filename), input_wav_directory, profile_version); if (!user_metadata.ok()) { LOG(ERROR) << user_metadata.status(); return static_cast(user_metadata.status().code()); } LOG(INFO) << user_metadata; // Get the directory for the output .iamf files. const auto& output_iamf_directory = absl::GetFlag(FLAGS_output_iamf_directory).empty() ? std::filesystem::temp_directory_path() : std::filesystem::path(absl::GetFlag(FLAGS_output_iamf_directory)); absl::Status status = iamf_tools::TestMain(*user_metadata, input_wav_directory.string(), output_iamf_directory.string()); // Log success or failure. Success is defined as a valid test vector returning // `absl::OkStatus()` or an invalid test vector returning a different status. const bool test_vector_is_valid = user_metadata->test_vector_metadata().is_valid(); std::stringstream ss; ss << "Test case expected to " << (test_vector_is_valid ? "pass" : "fail") << ".\nstatus= " << status; if (test_vector_is_valid == status.ok()) { LOG(INFO) << "Success. " << ss.str(); } else { LOG(ERROR) << "Failure. " << ss.str(); } return static_cast(status.code()); }