7 #include <packager/media/formats/webm/two_pass_single_segment_segmenter.h>
11 #include <absl/log/check.h>
12 #include <mkvmuxer/mkvmuxer.h>
13 #include <mkvmuxer/mkvmuxerutil.h>
15 #include <packager/file/file_util.h>
16 #include <packager/media/base/media_sample.h>
17 #include <packager/media/base/muxer_options.h>
18 #include <packager/media/base/stream_info.h>
28 uint64_t UpdateCues(mkvmuxer::Cues* cues) {
29 uint64_t cues_size = cues->Size();
30 uint64_t adjustment = cues_size;
31 while (adjustment != 0) {
32 for (
int i = 0; i < cues->cue_entries_size(); ++i) {
33 mkvmuxer::CuePoint* cue = cues->GetCueByIndex(i);
34 cue->set_cluster_pos(cue->cluster_pos() + adjustment);
36 uint64_t new_cues_size = cues->Size();
37 DCHECK_LE(cues_size, new_cues_size);
38 adjustment = new_cues_size - cues_size;
39 cues_size = new_cues_size;
46 bool ReadSkip(File* file, int64_t byte_count) {
47 const int64_t kBufferSize = 0x40000;
48 std::unique_ptr<char[]> buffer(
new char[kBufferSize]);
49 int64_t bytes_read = 0;
50 while (bytes_read < byte_count) {
51 int64_t size = std::min(kBufferSize, byte_count - bytes_read);
52 int64_t result = file->Read(buffer.get(), size);
61 DCHECK_EQ(bytes_read, byte_count);
66 TwoPassSingleSegmentSegmenter::TwoPassSingleSegmentSegmenter(
67 const MuxerOptions& options)
68 : SingleSegmentSegmenter(options) {}
70 TwoPassSingleSegmentSegmenter::~TwoPassSingleSegmentSegmenter() {}
72 Status TwoPassSingleSegmentSegmenter::DoInitialize() {
75 set_progress_target(duration() * 2);
78 return Status(error::FILE_FAILURE,
"Unable to create temporary file.");
79 std::unique_ptr<MkvWriter> temp(
new MkvWriter);
80 Status status = temp->Open(temp_file_name_);
83 set_writer(std::move(temp));
85 return SingleSegmentSegmenter::DoInitialize();
88 Status TwoPassSingleSegmentSegmenter::DoFinalize() {
89 const uint64_t header_size = init_end() + 1;
90 const uint64_t cues_pos = header_size - segment_payload_pos();
91 const uint64_t cues_size = UpdateCues(cues());
92 seek_head()->set_cues_pos(cues_pos);
93 seek_head()->set_cluster_pos(cues_pos + cues_size);
96 std::unique_ptr<MkvWriter> real_writer(
new MkvWriter);
97 Status status = real_writer->Open(options().output_file_name);
101 const uint64_t file_size = writer()->Position() + cues_size;
102 Status temp = WriteSegmentHeader(file_size, real_writer.get());
105 DCHECK_EQ(real_writer->Position(),
static_cast<int64_t
>(header_size));
108 set_index_start(real_writer->Position());
109 if (!cues()->Write(real_writer.get()))
110 return Status(error::FILE_FAILURE,
"Error writing Cues data.");
111 set_index_end(real_writer->Position() - 1);
112 DCHECK_EQ(real_writer->Position(),
113 static_cast<int64_t
>(segment_payload_pos() + cues_pos + cues_size));
116 set_writer(std::unique_ptr<MkvWriter>());
117 std::unique_ptr<File, FileCloser> temp_reader(
118 File::Open(temp_file_name_.c_str(),
"r"));
120 return Status(error::FILE_FAILURE,
"Error opening temp file.");
123 if (!ReadSkip(temp_reader.get(), header_size))
124 return Status(error::FILE_FAILURE,
"Error reading temp file.");
127 if (!CopyFileWithClusterRewrite(temp_reader.get(), real_writer.get(),
128 cluster()->Size())) {
129 return Status(error::FILE_FAILURE,
"Error copying temp file.");
134 if (!File::Delete(temp_file_name_.c_str())) {
135 LOG(WARNING) <<
"Unable to delete temporary file " << temp_file_name_;
138 return real_writer->Close();
141 bool TwoPassSingleSegmentSegmenter::CopyFileWithClusterRewrite(
144 uint64_t last_size) {
145 const int cluster_id_size = mkvmuxer::GetUIntSize(libwebm::kMkvCluster);
146 const int cluster_size_size = 8;
147 const int cluster_header_size = cluster_id_size + cluster_size_size;
150 if (dest->WriteFromFile(source, cluster_id_size) != cluster_id_size)
153 for (
int i = 0; i < cues()->cue_entries_size() - 1; ++i) {
155 const mkvmuxer::CuePoint* cue = cues()->GetCueByIndex(i);
156 const mkvmuxer::CuePoint* next_cue = cues()->GetCueByIndex(i + 1);
157 const int64_t cluster_payload_size =
158 next_cue->cluster_pos() - cue->cluster_pos() - cluster_header_size;
159 if (mkvmuxer::WriteUIntSize(dest, cluster_payload_size, cluster_size_size))
161 if (!ReadSkip(source, cluster_size_size))
165 int64_t to_copy = cluster_payload_size + cluster_id_size;
166 if (dest->WriteFromFile(source, to_copy) != to_copy)
170 const int64_t webm_delta_time = next_cue->time() - cue->time();
171 const int64_t delta_time = FromWebMTimecode(webm_delta_time);
172 UpdateProgress(delta_time);
176 const uint64_t last_cluster_payload_size = last_size - cluster_header_size;
177 if (mkvmuxer::WriteUIntSize(dest, last_cluster_payload_size,
180 if (!ReadSkip(source, cluster_size_size))
184 return dest->WriteFromFile(source) ==
185 static_cast<int64_t
>(last_cluster_payload_size);
All the methods that are virtual are virtual for mocking.
bool TempFilePath(const std::string &temp_dir, std::string *temp_file_path)