7#include <packager/media/formats/webm/two_pass_single_segment_segmenter.h>
14#include <absl/log/check.h>
15#include <absl/log/log.h>
16#include <common/webmids.h>
17#include <mkvmuxer/mkvmuxer.h>
18#include <mkvmuxer/mkvmuxerutil.h>
20#include <packager/file.h>
21#include <packager/file/file_closer.h>
22#include <packager/file/file_util.h>
23#include <packager/media/base/muxer_options.h>
24#include <packager/media/formats/webm/mkv_writer.h>
25#include <packager/media/formats/webm/single_segment_segmenter.h>
26#include <packager/status.h>
36uint64_t UpdateCues(mkvmuxer::Cues* cues) {
37 uint64_t cues_size = cues->Size();
38 uint64_t adjustment = cues_size;
39 while (adjustment != 0) {
40 for (
int i = 0; i < cues->cue_entries_size(); ++i) {
41 mkvmuxer::CuePoint* cue = cues->GetCueByIndex(i);
42 cue->set_cluster_pos(cue->cluster_pos() + adjustment);
44 uint64_t new_cues_size = cues->Size();
45 DCHECK_LE(cues_size, new_cues_size);
46 adjustment = new_cues_size - cues_size;
47 cues_size = new_cues_size;
54bool ReadSkip(File* file, int64_t byte_count) {
55 const int64_t kBufferSize = 0x40000;
56 std::unique_ptr<char[]> buffer(
new char[kBufferSize]);
57 int64_t bytes_read = 0;
58 while (bytes_read < byte_count) {
59 int64_t size = std::min(kBufferSize, byte_count - bytes_read);
60 int64_t result = file->Read(buffer.get(), size);
69 DCHECK_EQ(bytes_read, byte_count);
74TwoPassSingleSegmentSegmenter::TwoPassSingleSegmentSegmenter(
75 const MuxerOptions& options)
76 : SingleSegmentSegmenter(options) {}
78TwoPassSingleSegmentSegmenter::~TwoPassSingleSegmentSegmenter() {}
80Status TwoPassSingleSegmentSegmenter::DoInitialize() {
83 set_progress_target(duration() * 2);
86 return Status(error::FILE_FAILURE,
"Unable to create temporary file.");
87 std::unique_ptr<MkvWriter> temp(
new MkvWriter);
88 Status status = temp->Open(temp_file_name_);
91 set_writer(std::move(temp));
93 return SingleSegmentSegmenter::DoInitialize();
96Status TwoPassSingleSegmentSegmenter::DoFinalize() {
97 const uint64_t header_size = init_end() + 1;
98 const uint64_t cues_pos = header_size - segment_payload_pos();
99 const uint64_t cues_size = UpdateCues(cues());
100 seek_head()->set_cues_pos(cues_pos);
101 seek_head()->set_cluster_pos(cues_pos + cues_size);
104 std::unique_ptr<MkvWriter> real_writer(
new MkvWriter);
105 Status status = real_writer->Open(options().output_file_name);
109 const uint64_t file_size = writer()->Position() + cues_size;
110 Status temp = WriteSegmentHeader(file_size, real_writer.get());
113 DCHECK_EQ(real_writer->Position(),
static_cast<int64_t
>(header_size));
116 set_index_start(real_writer->Position());
117 if (!cues()->Write(real_writer.get()))
118 return Status(error::FILE_FAILURE,
"Error writing Cues data.");
119 set_index_end(real_writer->Position() - 1);
120 DCHECK_EQ(real_writer->Position(),
121 static_cast<int64_t
>(segment_payload_pos() + cues_pos + cues_size));
124 set_writer(std::unique_ptr<MkvWriter>());
125 std::unique_ptr<File, FileCloser> temp_reader(
126 File::Open(temp_file_name_.c_str(),
"r"));
128 return Status(error::FILE_FAILURE,
"Error opening temp file.");
131 if (!ReadSkip(temp_reader.get(), header_size))
132 return Status(error::FILE_FAILURE,
"Error reading temp file.");
135 if (!CopyFileWithClusterRewrite(temp_reader.get(), real_writer.get(),
136 cluster()->Size())) {
137 return Status(error::FILE_FAILURE,
"Error copying temp file.");
142 if (!File::Delete(temp_file_name_.c_str())) {
143 LOG(WARNING) <<
"Unable to delete temporary file " << temp_file_name_;
146 return real_writer->Close();
149bool TwoPassSingleSegmentSegmenter::CopyFileWithClusterRewrite(
152 uint64_t last_size) {
153 const int cluster_id_size = mkvmuxer::GetUIntSize(libwebm::kMkvCluster);
154 const int cluster_size_size = 8;
155 const int cluster_header_size = cluster_id_size + cluster_size_size;
158 if (dest->WriteFromFile(source, cluster_id_size) != cluster_id_size)
161 for (
int i = 0; i < cues()->cue_entries_size() - 1; ++i) {
163 const mkvmuxer::CuePoint* cue = cues()->GetCueByIndex(i);
164 const mkvmuxer::CuePoint* next_cue = cues()->GetCueByIndex(i + 1);
165 const int64_t cluster_payload_size =
166 next_cue->cluster_pos() - cue->cluster_pos() - cluster_header_size;
167 if (mkvmuxer::WriteUIntSize(dest, cluster_payload_size, cluster_size_size))
169 if (!ReadSkip(source, cluster_size_size))
173 int64_t to_copy = cluster_payload_size + cluster_id_size;
174 if (dest->WriteFromFile(source, to_copy) != to_copy)
178 const int64_t webm_delta_time = next_cue->time() - cue->time();
179 const int64_t delta_time = FromWebMTimecode(webm_delta_time);
180 UpdateProgress(delta_time);
184 const uint64_t last_cluster_payload_size = last_size - cluster_header_size;
185 if (mkvmuxer::WriteUIntSize(dest, last_cluster_payload_size,
188 if (!ReadSkip(source, cluster_size_size))
192 return dest->WriteFromFile(source) ==
193 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)