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 "iamf/cli/obu_sequencer_iamf.h"
13
14 #include <cstdint>
15 #include <filesystem>
16 #include <fstream>
17 #include <ios>
18 #include <optional>
19 #include <string>
20 #include <system_error>
21
22 #include "absl/log/check.h"
23 #include "absl/log/log.h"
24 #include "absl/status/status.h"
25 #include "absl/types/span.h"
26 #include "iamf/cli/obu_sequencer_base.h"
27 #include "iamf/common/leb_generator.h"
28 #include "iamf/common/utils/macros.h"
29 #include "iamf/common/write_bit_buffer.h"
30
31 namespace iamf_tools {
32
33 namespace {
34
35 constexpr int64_t kBufferStartSize = 65536;
36
37 // This sequencer does not care about the delay or timing information. It would
38 // be pointless to delay the descriptor OBUs.
39 constexpr bool kDoNotDelayDescriptorsUntilFirstUntrimmedSample = false;
40
MaybeRemoveFile(const std::string & filename,std::optional<std::fstream> & file_to_remove)41 void MaybeRemoveFile(const std::string& filename,
42 std::optional<std::fstream>& file_to_remove) {
43 if (filename.empty() || !file_to_remove.has_value()) {
44 return;
45 }
46
47 // Close and delete the file.
48 file_to_remove->close();
49 file_to_remove = std::nullopt;
50 std::error_code error_code;
51 std::filesystem::remove(filename, error_code);
52 if (!error_code) {
53 // File clean up failed somehow. Just log the error and move on.
54 LOG(ERROR).WithPerror() << "Failed to remove " << filename;
55 }
56 }
57
58 } // namespace
59
ObuSequencerIamf(const std::string & iamf_filename,bool include_temporal_delimiters,const LebGenerator & leb_generator)60 ObuSequencerIamf::ObuSequencerIamf(const std::string& iamf_filename,
61 bool include_temporal_delimiters,
62 const LebGenerator& leb_generator)
63 : ObuSequencerBase(leb_generator, include_temporal_delimiters,
64 kDoNotDelayDescriptorsUntilFirstUntrimmedSample),
65 iamf_filename_(iamf_filename),
66 wb_(kBufferStartSize, leb_generator) {}
67
PushSerializedDescriptorObus(uint32_t,uint32_t,uint8_t,std::optional<int64_t>,int,absl::Span<const uint8_t> descriptor_obus)68 absl::Status ObuSequencerIamf::PushSerializedDescriptorObus(
69 uint32_t /*common_samples_per_frame*/, uint32_t /*common_sample_rate*/,
70 uint8_t /*common_bit_depth*/,
71 std::optional<int64_t> /*first_untrimmed_timestamp*/, int /*num_channels*/,
72 absl::Span<const uint8_t> descriptor_obus) {
73 if (!iamf_filename_.empty()) {
74 LOG(INFO) << "Writing descriptor OBUs to " << iamf_filename_;
75
76 output_iamf_.emplace(iamf_filename_, std::fstream::out | std::ios::binary);
77 }
78
79 RETURN_IF_NOT_OK(wb_.WriteUint8Span(descriptor_obus));
80 return wb_.FlushAndWriteToFile(output_iamf_);
81 }
82
PushSerializedTemporalUnit(int64_t,int,absl::Span<const uint8_t> temporal_unit)83 absl::Status ObuSequencerIamf::PushSerializedTemporalUnit(
84 int64_t /*timestamp*/, int /*num_samples*/,
85 absl::Span<const uint8_t> temporal_unit) {
86 RETURN_IF_NOT_OK(wb_.WriteUint8Span(temporal_unit));
87 return wb_.FlushAndWriteToFile(output_iamf_);
88 }
89
PushFinalizedDescriptorObus(absl::Span<const uint8_t> descriptor_obus)90 absl::Status ObuSequencerIamf::PushFinalizedDescriptorObus(
91 absl::Span<const uint8_t> descriptor_obus) {
92 if (output_iamf_.has_value()) {
93 // For good practice, restore the previous position in the file after we
94 // rewrite. But in reality this function usually will be called right before
95 // closing the file.
96 const auto previous_position = output_iamf_->tellg();
97 output_iamf_->seekg(0, std::ios::beg);
98 RETURN_IF_NOT_OK(wb_.WriteUint8Span(descriptor_obus));
99 RETURN_IF_NOT_OK(wb_.FlushAndWriteToFile(output_iamf_));
100
101 output_iamf_->seekg(previous_position);
102 }
103
104 return absl::OkStatus();
105 }
106
CloseDerived()107 void ObuSequencerIamf::CloseDerived() {
108 if (output_iamf_.has_value() && output_iamf_->is_open()) {
109 output_iamf_->close();
110 output_iamf_ = std::nullopt;
111 }
112 }
113
AbortDerived()114 void ObuSequencerIamf::AbortDerived() {
115 LOG(INFO) << "Aborting ObuSequencerIamf.";
116 MaybeRemoveFile(iamf_filename_, output_iamf_);
117 }
118
119 } // namespace iamf_tools
120