7 #include <packager/media/formats/mp4/segmenter.h>
11 #include <absl/log/check.h>
12 #include <absl/log/log.h>
14 #include <packager/media/base/buffer_writer.h>
15 #include <packager/media/base/id3_tag.h>
16 #include <packager/media/base/media_sample.h>
17 #include <packager/media/base/muxer_options.h>
18 #include <packager/media/base/muxer_util.h>
19 #include <packager/media/base/stream_info.h>
20 #include <packager/media/chunking/chunking_handler.h>
21 #include <packager/media/event/progress_listener.h>
22 #include <packager/media/formats/mp4/box_definitions.h>
23 #include <packager/media/formats/mp4/fragmenter.h>
24 #include <packager/media/formats/mp4/key_frame_info.h>
25 #include <packager/version/version.h>
33 int64_t Rescale(int64_t time_in_old_scale,
36 return static_cast<double>(time_in_old_scale) / old_scale * new_scale;
41 Segmenter::Segmenter(
const MuxerOptions& options,
42 std::unique_ptr<FileType> ftyp,
43 std::unique_ptr<Movie> moov)
45 ftyp_(std::move(ftyp)),
46 moov_(std::move(moov)),
47 moof_(new MovieFragment()),
48 fragment_buffer_(new BufferWriter()),
49 sidx_(new SegmentIndex()) {}
51 Segmenter::~Segmenter() {}
53 Status Segmenter::Initialize(
54 const std::vector<std::shared_ptr<const StreamInfo>>& streams,
57 DCHECK_LT(0u, streams.size());
58 muxer_listener_ = muxer_listener;
59 progress_listener_ = progress_listener;
60 moof_->header.sequence_number = 0;
62 moof_->tracks.resize(streams.size());
63 fragmenters_.resize(streams.size());
64 stream_durations_.resize(streams.size());
66 for (uint32_t i = 0; i < streams.size(); ++i) {
67 moof_->tracks[i].header.track_id = i + 1;
68 if (streams[i]->stream_type() == kStreamVideo) {
70 if (sidx_->reference_id == 0)
71 sidx_->reference_id = i + 1;
74 const EditList& edit_list = moov_->tracks[i].edit.list;
75 int64_t edit_list_offset = 0;
76 if (edit_list.edits.size() > 0) {
77 DCHECK_EQ(edit_list.edits.size(), 1u);
78 edit_list_offset = edit_list.edits.front().media_time;
81 fragmenters_[i].reset(
82 new Fragmenter(streams[i], &moof_->tracks[i], edit_list_offset));
86 if (sidx_->reference_id == 0)
87 sidx_->reference_id = 1;
88 sidx_->timescale = streams[GetReferenceStreamId()]->time_scale();
91 progress_target_ = streams[GetReferenceStreamId()]->duration();
94 moov_->header.timescale = sidx_->timescale;
95 moof_->header.sequence_number = 1;
98 const std::string version = GetPackagerVersion();
99 if (!version.empty()) {
100 moov_->metadata.handler.handler_type = FOURCC_ID32;
101 moov_->metadata.id3v2.language.code =
"eng";
105 CHECK(id3_tag.
WriteToVector(&moov_->metadata.id3v2.id3v2_data));
107 return DoInitialize();
110 Status Segmenter::Finalize() {
114 moov_->extends.header.fragment_duration = 0;
115 for (
size_t i = 0; i < stream_durations_.size(); ++i) {
117 Rescale(stream_durations_[i], moov_->tracks[i].media.header.timescale,
118 moov_->header.timescale);
120 static_cast<int64_t
>(moov_->extends.header.fragment_duration))
121 moov_->extends.header.fragment_duration = duration;
126 Status Segmenter::AddSample(
size_t stream_id,
const MediaSample& sample) {
128 if (moov_->extends.tracks[stream_id].default_sample_duration == 0) {
129 moov_->extends.tracks[stream_id].default_sample_duration =
133 DCHECK_LT(stream_id, fragmenters_.size());
134 Fragmenter* fragmenter = fragmenters_[stream_id].get();
135 if (fragmenter->fragment_finalized()) {
136 return Status(error::FRAGMENT_FINALIZED,
137 "Current fragment is finalized already.");
140 Status status = fragmenter->
AddSample(sample);
146 if (num_samples_ < 2) {
147 sample_durations_[num_samples_] = sample.duration();
150 stream_durations_[stream_id] += sample.duration();
154 Status Segmenter::FinalizeSegment(
size_t stream_id,
156 if (segment_info.key_rotation_encryption_config) {
157 FinalizeFragmentForKeyRotation(
158 stream_id, segment_info.is_encrypted,
159 *segment_info.key_rotation_encryption_config);
162 DCHECK_LT(stream_id, fragmenters_.size());
163 Fragmenter* specified_fragmenter = fragmenters_[stream_id].get();
164 DCHECK(specified_fragmenter);
170 for (
const std::unique_ptr<Fragmenter>& fragmenter : fragmenters_) {
171 if (!fragmenter->fragment_finalized())
180 uint64_t next_traf_position = moof_->HeaderSize() + moof_->header.box_size();
181 for (
size_t i = 0; i < moof_->tracks.size(); ++i) {
183 if (traf.auxiliary_offset.offsets.size() > 0) {
184 DCHECK_EQ(traf.auxiliary_offset.offsets.size(), 1u);
185 DCHECK(!traf.sample_encryption.sample_encryption_entries.empty());
187 next_traf_position += traf.
box_size();
190 traf.auxiliary_offset.offsets[0] =
191 next_traf_position - traf.sample_encryption.
box_size() +
195 traf.runs[0].data_offset = data_offset + mdat.data_size;
196 mdat.data_size +=
static_cast<uint32_t
>(fragmenters_[i]->data()->Size());
200 sidx_->references.resize(sidx_->references.size() + 1);
201 fragmenters_[GetReferenceStreamId()]->GenerateSegmentReference(
202 &sidx_->references[sidx_->references.size() - 1]);
203 sidx_->references[sidx_->references.size() - 1].referenced_size =
204 data_offset + mdat.data_size;
206 const uint64_t moof_start_offset = fragment_buffer_->Size();
209 moof_->Write(fragment_buffer_.get());
212 bool first_key_frame =
true;
213 for (
const std::unique_ptr<Fragmenter>& fragmenter : fragmenters_) {
218 if (!fragmenter->key_frame_infos().empty() && first_key_frame) {
220 fragmenter->key_frame_infos().front();
221 first_key_frame =
false;
222 key_frame_infos_.push_back(
223 {key_frame_info.timestamp, moof_start_offset,
224 fragment_buffer_->Size() - moof_start_offset + key_frame_info.size});
226 fragment_buffer_->AppendBuffer(*fragmenter->data());
230 ++moof_->header.sequence_number;
232 for (std::unique_ptr<Fragmenter>& fragmenter : fragmenters_)
233 fragmenter->ClearFragmentFinalized();
235 if (segment_info.is_chunk) {
237 status = DoFinalizeChunk(segment_info.segment_number);
242 if (!segment_info.is_subsegment || segment_info.is_final_chunk_in_seg) {
244 status = DoFinalizeSegment(segment_info.segment_number);
247 sidx_->references.clear();
248 key_frame_infos_.clear();
254 int32_t Segmenter::GetReferenceTimeScale()
const {
255 return moov_->header.timescale;
258 double Segmenter::GetDuration()
const {
259 int64_t duration = moov_->extends.header.fragment_duration;
264 return static_cast<double>(duration) / moov_->header.timescale;
267 void Segmenter::UpdateProgress(uint64_t progress) {
268 accumulated_progress_ += progress;
270 if (!progress_listener_)
return;
271 if (progress_target_ == 0)
return;
275 if (accumulated_progress_ >= progress_target_) {
276 progress_listener_->OnProgress(1.0);
278 progress_listener_->OnProgress(
static_cast<double>(accumulated_progress_) /
283 void Segmenter::SetComplete() {
284 if (!progress_listener_)
return;
285 progress_listener_->OnProgress(1.0);
288 uint32_t Segmenter::GetReferenceStreamId() {
290 return sidx_->reference_id - 1;
293 void Segmenter::FinalizeFragmentForKeyRotation(
295 bool fragment_encrypted,
297 if (options_.mp4_params.include_pssh_in_stream) {
299 const auto& key_system_info = encryption_config.key_system_info;
301 if (system.psshs.empty())
303 ProtectionSystemSpecificHeader pssh;
304 pssh.raw_box = system.psshs;
305 moof_->pssh.push_back(pssh);
309 <<
"Key rotation and no pssh in stream may not work well together.";
319 if (!fragment_encrypted)
322 DCHECK_LT(stream_id, moof_->tracks.size());
323 TrackFragment& traf = moof_->tracks[stream_id];
324 traf.sample_group_descriptions.resize(traf.sample_group_descriptions.size() +
326 SampleGroupDescription& sample_group_description =
327 traf.sample_group_descriptions.back();
328 sample_group_description.grouping_type = FOURCC_seig;
330 sample_group_description.cenc_sample_encryption_info_entries.resize(1);
331 CencSampleEncryptionInfoEntry& sample_group_entry =
332 sample_group_description.cenc_sample_encryption_info_entries.back();
333 sample_group_entry.is_protected = 1;
334 sample_group_entry.per_sample_iv_size = encryption_config.per_sample_iv_size;
335 sample_group_entry.constant_iv = encryption_config.constant_iv;
336 sample_group_entry.crypt_byte_block = encryption_config.crypt_byte_block;
337 sample_group_entry.skip_byte_block = encryption_config.skip_byte_block;
338 sample_group_entry.key_id = encryption_config.key_id;
All the methods that are virtual are virtual for mocking.