7 #include "packager/media/formats/mp4/single_segment_segmenter.h"
10 #include <openssl/err.h>
11 #include <openssl/rand.h>
13 #include "packager/base/files/file_util.h"
14 #include "packager/base/strings/stringprintf.h"
15 #include "packager/base/threading/platform_thread.h"
16 #include "packager/base/time/time.h"
17 #include "packager/media/base/buffer_writer.h"
18 #include "packager/media/base/media_stream.h"
19 #include "packager/media/base/muxer_options.h"
20 #include "packager/media/event/muxer_listener.h"
21 #include "packager/media/event/progress_listener.h"
22 #include "packager/media/file/file.h"
23 #include "packager/media/formats/mp4/box_definitions.h"
25 namespace edash_packager {
30 std::string TempFileName() {
31 int32_t tid =
static_cast<int32_t
>(base::PlatformThread::CurrentId());
33 if (RAND_bytes(reinterpret_cast<uint8_t*>(&rand),
sizeof(rand)) != 1) {
34 LOG(WARNING) <<
"RAND_bytes failed with error: "
35 << ERR_error_string(ERR_get_error(), NULL);
36 rand = base::Time::Now().ToInternalValue();
38 return base::StringPrintf(
"packager-tempfile-%x-%" PRIx64, tid, rand);
42 SingleSegmentSegmenter::SingleSegmentSegmenter(
const MuxerOptions& options,
43 scoped_ptr<FileType> ftyp,
44 scoped_ptr<Movie> moov)
45 : Segmenter(options, ftyp.Pass(), moov.Pass()) {}
47 SingleSegmentSegmenter::~SingleSegmentSegmenter() {
49 temp_file_.release()->Close();
50 if (!temp_file_name_.empty()) {
52 LOG(ERROR) <<
"Unable to delete temporary file " << temp_file_name_;
67 *size = vod_sidx_->ComputeSize();
71 Status SingleSegmentSegmenter::DoInitialize() {
78 set_progress_target(progress_target() * 2);
80 if (options().temp_dir.empty()) {
81 base::FilePath temp_file_path;
82 if (!base::CreateTemporaryFile(&temp_file_path)) {
83 LOG(ERROR) <<
"Failed to create temporary file.";
84 return Status(error::FILE_FAILURE,
"Unable to create temporary file.");
86 temp_file_name_ = temp_file_path.value();
89 base::FilePath(options().temp_dir).Append(TempFileName()).value();
91 temp_file_.reset(
File::Open(temp_file_name_.c_str(),
"w"));
94 : Status(error::FILE_FAILURE,
95 "Cannot open file to write " + temp_file_name_);
98 Status SingleSegmentSegmenter::DoFinalize() {
105 if (!temp_file_.release()->Close()) {
106 return Status(error::FILE_FAILURE,
107 "Cannot close the temp file " + temp_file_name_);
110 scoped_ptr<File, FileCloser> file(
111 File::Open(options().output_file_name.c_str(),
"w"));
113 return Status(error::FILE_FAILURE,
114 "Cannot open file to write " + options().output_file_name);
117 LOG(INFO) <<
"Update media header (moov) and rewrite the file to '"
121 scoped_ptr<BufferWriter> buffer(
new BufferWriter());
122 ftyp()->
Write(buffer.get());
123 moov()->
Write(buffer.get());
124 vod_sidx_->Write(buffer.get());
125 Status status = buffer->WriteToFile(file.get());
130 scoped_ptr<File, FileCloser> temp_file(
132 if (temp_file == NULL) {
133 return Status(error::FILE_FAILURE,
134 "Cannot open file to read " + temp_file_name_);
138 const uint64_t re_segment_progress_target = progress_target() * 0.5;
140 const int kBufSize = 0x200000;
141 scoped_ptr<uint8_t[]> buf(
new uint8_t[kBufSize]);
143 int64_t size = temp_file->Read(buf.get(), kBufSize);
146 }
else if (size < 0) {
147 return Status(error::FILE_FAILURE,
148 "Failed to read file " + temp_file_name_);
150 int64_t size_written = file->Write(buf.get(), size);
151 if (size_written != size) {
152 return Status(error::FILE_FAILURE,
153 "Failed to write file " + options().output_file_name);
156 re_segment_progress_target);
162 Status SingleSegmentSegmenter::DoFinalizeSegment() {
164 DCHECK(fragment_buffer());
168 std::vector<SegmentReference>& refs = sidx()->references;
169 SegmentReference& vod_ref = refs[0];
170 uint64_t first_sap_time =
171 refs[0].sap_delta_time + refs[0].earliest_presentation_time;
172 for (uint32_t i = 1; i < refs.size(); ++i) {
173 vod_ref.referenced_size += refs[i].referenced_size;
177 vod_ref.subsegment_duration += refs[i].subsegment_duration;
178 vod_ref.earliest_presentation_time = std::min(
179 vod_ref.earliest_presentation_time, refs[i].earliest_presentation_time);
181 if (vod_ref.sap_type == SegmentReference::TypeUnknown &&
182 refs[i].sap_type != SegmentReference::TypeUnknown) {
183 vod_ref.sap_type = refs[i].sap_type;
185 refs[i].sap_delta_time + refs[i].earliest_presentation_time;
189 if (vod_ref.sap_type != SegmentReference::TypeUnknown) {
190 vod_ref.sap_delta_time =
191 first_sap_time - vod_ref.earliest_presentation_time;
195 if (vod_sidx_ == NULL) {
196 vod_sidx_.reset(
new SegmentIndex());
197 vod_sidx_->reference_id = sidx()->reference_id;
198 vod_sidx_->timescale = sidx()->timescale;
200 if (vod_ref.earliest_presentation_time > 0) {
201 const double starting_time_in_seconds =
202 static_cast<double>(vod_ref.earliest_presentation_time) /
203 GetReferenceTimeScale();
205 if (starting_time_in_seconds > 0.5) {
210 LOG(WARNING) <<
"Warning! Non-zero starting time (in seconds): "
211 << starting_time_in_seconds
212 <<
". Manual adjustment of presentationTimeOffset in "
213 "mpd might be necessary.";
217 vod_sidx_->earliest_presentation_time = 0;
219 vod_sidx_->references.push_back(vod_ref);
222 size_t segment_size = fragment_buffer()->Size();
223 Status status = fragment_buffer()->
WriteToFile(temp_file_.get());
224 if (!status.ok())
return status;
227 if (muxer_listener()) {
229 muxer_listener()->OnNewSegment(vod_ref.earliest_presentation_time,
230 vod_ref.subsegment_duration, segment_size);