Shaka Packager SDK
Loading...
Searching...
No Matches
fragmenter.cc
1// Copyright 2014 Google LLC. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file or at
5// https://developers.google.com/open-source/licenses/bsd
6
7#include <packager/media/formats/mp4/fragmenter.h>
8
9#include <algorithm>
10#include <limits>
11
12#include <absl/log/check.h>
13
14#include <packager/macros/status.h>
15#include <packager/media/base/audio_stream_info.h>
16#include <packager/media/base/buffer_writer.h>
17#include <packager/media/base/media_sample.h>
18#include <packager/media/formats/mp4/box_definitions.h>
19#include <packager/media/formats/mp4/key_frame_info.h>
20
21namespace shaka {
22namespace media {
23namespace mp4 {
24
25namespace {
26const int64_t kInvalidTime = std::numeric_limits<int64_t>::max();
27
28int64_t GetSeekPreroll(const StreamInfo& stream_info) {
29 if (stream_info.stream_type() != kStreamAudio)
30 return 0;
31 const AudioStreamInfo& audio_stream_info =
32 static_cast<const AudioStreamInfo&>(stream_info);
33 return audio_stream_info.seek_preroll_ns();
34}
35
36void NewSampleEncryptionEntry(const DecryptConfig& decrypt_config,
37 bool use_constant_iv,
38 TrackFragment* traf) {
39 SampleEncryption& sample_encryption = traf->sample_encryption;
40 SampleEncryptionEntry sample_encryption_entry;
41 if (!use_constant_iv)
42 sample_encryption_entry.initialization_vector = decrypt_config.iv();
43 sample_encryption_entry.subsamples = decrypt_config.subsamples();
44 sample_encryption.sample_encryption_entries.push_back(
45 sample_encryption_entry);
46 traf->auxiliary_size.sample_info_sizes.push_back(
47 sample_encryption_entry.ComputeSize());
48}
49
50} // namespace
51
52Fragmenter::Fragmenter(std::shared_ptr<const StreamInfo> stream_info,
53 TrackFragment* traf,
54 int64_t edit_list_offset)
55 : stream_info_(std::move(stream_info)),
56 traf_(traf),
57 edit_list_offset_(edit_list_offset),
58 seek_preroll_(GetSeekPreroll(*stream_info_)),
59 earliest_presentation_time_(kInvalidTime),
60 first_sap_time_(kInvalidTime) {
61 DCHECK(stream_info_);
62 DCHECK(traf);
63}
64
65Fragmenter::~Fragmenter() {}
66
67Status Fragmenter::AddSample(const MediaSample& sample) {
68 const int64_t pts = sample.pts();
69 const int64_t dts = sample.dts();
70 const int64_t duration = sample.duration();
71 if (duration == 0)
72 LOG(WARNING) << "Unexpected sample with zero duration @ dts " << dts;
73
74 if (!fragment_initialized_)
75 RETURN_IF_ERROR(InitializeFragment(dts));
76
77 if (sample.side_data_size() > 0)
78 LOG(WARNING) << "MP4 samples do not support side data. Side data ignored.";
79
80 // Fill in sample parameters. It will be optimized later.
81 traf_->runs[0].sample_sizes.push_back(
82 static_cast<uint32_t>(sample.data_size()));
83 traf_->runs[0].sample_durations.push_back(duration);
84 traf_->runs[0].sample_flags.push_back(
85 sample.is_key_frame() ? TrackFragmentHeader::kUnset
86 : TrackFragmentHeader::kNonKeySampleMask);
87
88 if (sample.decrypt_config()) {
89 NewSampleEncryptionEntry(
90 *sample.decrypt_config(),
91 !stream_info_->encryption_config().constant_iv.empty(), traf_);
92 }
93
94 if (stream_info_->stream_type() == StreamType::kStreamVideo &&
95 sample.is_key_frame()) {
96 key_frame_infos_.push_back({pts, data_->Size(), sample.data_size()});
97 }
98
99 data_->AppendArray(sample.data(), sample.data_size());
100
101 traf_->runs[0].sample_composition_time_offsets.push_back(pts - dts);
102 if (pts != dts)
103 traf_->runs[0].flags |= TrackFragmentRun::kSampleCompTimeOffsetsPresentMask;
104
105 // Exclude the part of sample with negative pts out of duration calculation as
106 // they are not presented.
107 if (pts < 0) {
108 const int64_t end_pts = pts + duration;
109 if (end_pts > 0) {
110 // Include effective presentation duration.
111 fragment_duration_ += end_pts;
112
113 earliest_presentation_time_ = 0;
114 if (sample.is_key_frame())
115 first_sap_time_ = 0;
116 }
117 } else {
118 fragment_duration_ += duration;
119
120 if (earliest_presentation_time_ > pts)
121 earliest_presentation_time_ = pts;
122
123 if (sample.is_key_frame()) {
124 if (first_sap_time_ == kInvalidTime)
125 first_sap_time_ = pts;
126 }
127 }
128 return Status::OK;
129}
130
131Status Fragmenter::InitializeFragment(int64_t first_sample_dts) {
132 fragment_initialized_ = true;
133 fragment_finalized_ = false;
134
135 // |first_sample_dts| is adjusted by the edit list offset. The offset should
136 // be un-applied in |decode_time|, so when applying the Edit List, the result
137 // dts is |first_sample_dts|.
138 const int64_t dts_before_edit = first_sample_dts + edit_list_offset_;
139 traf_->decode_time.decode_time = dts_before_edit;
140
141 traf_->runs.clear();
142 traf_->runs.resize(1);
143 traf_->runs[0].flags = TrackFragmentRun::kDataOffsetPresentMask;
144 traf_->auxiliary_size.sample_info_sizes.clear();
145 traf_->auxiliary_offset.offsets.clear();
146 traf_->sample_encryption.sample_encryption_entries.clear();
147 traf_->sample_group_descriptions.clear();
148 traf_->sample_to_groups.clear();
149 traf_->header.sample_description_index = 1; // 1-based.
150 traf_->header.flags = TrackFragmentHeader::kDefaultBaseIsMoofMask |
151 TrackFragmentHeader::kSampleDescriptionIndexPresentMask;
152
153 fragment_duration_ = 0;
154 earliest_presentation_time_ = kInvalidTime;
155 first_sap_time_ = kInvalidTime;
156 data_.reset(new BufferWriter());
157 key_frame_infos_.clear();
158 return Status::OK;
159}
160
162 if (!fragment_initialized_)
163 return Status::OK;
164
165 if (stream_info_->is_encrypted()) {
166 Status status = FinalizeFragmentForEncryption();
167 if (!status.ok())
168 return status;
169 }
170
171 // Optimize trun box.
172 traf_->runs[0].sample_count =
173 static_cast<uint32_t>(traf_->runs[0].sample_sizes.size());
174 if (OptimizeSampleEntries(&traf_->runs[0].sample_durations,
175 &traf_->header.default_sample_duration)) {
176 traf_->header.flags |=
177 TrackFragmentHeader::kDefaultSampleDurationPresentMask;
178 } else {
179 traf_->runs[0].flags |= TrackFragmentRun::kSampleDurationPresentMask;
180 }
181 if (OptimizeSampleEntries(&traf_->runs[0].sample_sizes,
182 &traf_->header.default_sample_size)) {
183 traf_->header.flags |= TrackFragmentHeader::kDefaultSampleSizePresentMask;
184 } else {
185 traf_->runs[0].flags |= TrackFragmentRun::kSampleSizePresentMask;
186 }
187 if (OptimizeSampleEntries(&traf_->runs[0].sample_flags,
188 &traf_->header.default_sample_flags)) {
189 traf_->header.flags |= TrackFragmentHeader::kDefaultSampleFlagsPresentMask;
190 } else {
191 traf_->runs[0].flags |= TrackFragmentRun::kSampleFlagsPresentMask;
192 }
193
194 // Add SampleToGroup boxes. A SampleToGroup box with grouping type of 'roll'
195 // needs to be added if there is seek preroll, referencing sample group
196 // description in track level; Also need to add SampleToGroup boxes
197 // correponding to every SampleGroupDescription boxes, referencing sample
198 // group description in fragment level.
199 DCHECK_EQ(traf_->sample_to_groups.size(), 0u);
200 if (seek_preroll_ > 0) {
201 traf_->sample_to_groups.resize(traf_->sample_to_groups.size() + 1);
202 SampleToGroup& sample_to_group = traf_->sample_to_groups.back();
203 sample_to_group.grouping_type = FOURCC_roll;
204
205 sample_to_group.entries.resize(1);
206 SampleToGroupEntry& sample_to_group_entry = sample_to_group.entries.back();
207 sample_to_group_entry.sample_count = traf_->runs[0].sample_count;
208 sample_to_group_entry.group_description_index =
209 SampleToGroupEntry::kTrackGroupDescriptionIndexBase + 1;
210 }
211 for (const auto& sample_group_description :
212 traf_->sample_group_descriptions) {
213 traf_->sample_to_groups.resize(traf_->sample_to_groups.size() + 1);
214 SampleToGroup& sample_to_group = traf_->sample_to_groups.back();
215 sample_to_group.grouping_type = sample_group_description.grouping_type;
216
217 sample_to_group.entries.resize(1);
218 SampleToGroupEntry& sample_to_group_entry = sample_to_group.entries.back();
219 sample_to_group_entry.sample_count = traf_->runs[0].sample_count;
220 sample_to_group_entry.group_description_index =
221 SampleToGroupEntry::kTrackFragmentGroupDescriptionIndexBase + 1;
222 }
223
224 fragment_finalized_ = true;
225 fragment_initialized_ = false;
226 return Status::OK;
227}
228
230 // NOTE: Daisy chain is not supported currently.
231 reference->reference_type = false;
232 reference->subsegment_duration = fragment_duration_;
233 reference->starts_with_sap = StartsWithSAP();
234 if (kInvalidTime == first_sap_time_) {
235 reference->sap_type = SegmentReference::TypeUnknown;
236 reference->sap_delta_time = 0;
237 } else {
238 reference->sap_type = SegmentReference::Type1;
239 reference->sap_delta_time = first_sap_time_ - earliest_presentation_time_;
240 }
241 reference->earliest_presentation_time = earliest_presentation_time_;
242}
243
244Status Fragmenter::FinalizeFragmentForEncryption() {
245 SampleEncryption& sample_encryption = traf_->sample_encryption;
246 if (sample_encryption.sample_encryption_entries.empty()) {
247 // This fragment is not encrypted.
248 // There are two sample description entries, an encrypted entry and a clear
249 // entry, are generated. The 1-based clear entry index is always 2.
250 const uint32_t kClearSampleDescriptionIndex = 2;
251 traf_->header.sample_description_index = kClearSampleDescriptionIndex;
252 return Status::OK;
253 }
254 if (sample_encryption.sample_encryption_entries.size() !=
255 traf_->runs[0].sample_sizes.size()) {
256 LOG(ERROR) << "Partially encrypted segment is not supported";
257 return Status(error::MUXER_FAILURE,
258 "Partially encrypted segment is not supported.");
259 }
260
261 const SampleEncryptionEntry& sample_encryption_entry =
262 sample_encryption.sample_encryption_entries.front();
263 const bool use_subsample_encryption =
264 !sample_encryption_entry.subsamples.empty();
265 if (use_subsample_encryption)
266 traf_->sample_encryption.flags |= SampleEncryption::kUseSubsampleEncryption;
267 traf_->sample_encryption.iv_size = static_cast<uint8_t>(
268 sample_encryption_entry.initialization_vector.size());
269
270 // The offset will be adjusted in Segmenter after knowing moof size.
271 traf_->auxiliary_offset.offsets.push_back(0);
272
273 // Optimize saiz box.
274 SampleAuxiliaryInformationSize& saiz = traf_->auxiliary_size;
275 saiz.sample_count = static_cast<uint32_t>(saiz.sample_info_sizes.size());
276 DCHECK_EQ(saiz.sample_info_sizes.size(),
277 traf_->sample_encryption.sample_encryption_entries.size());
278 if (!OptimizeSampleEntries(&saiz.sample_info_sizes,
279 &saiz.default_sample_info_size)) {
280 saiz.default_sample_info_size = 0;
281 }
282
283 // It should only happen with full sample encryption + constant iv, i.e.
284 // 'cbcs' applying to audio.
285 if (saiz.default_sample_info_size == 0 && saiz.sample_info_sizes.empty()) {
286 DCHECK(!use_subsample_encryption);
287 // ISO/IEC 23001-7:2016(E) The sample auxiliary information would then be
288 // empty and should be omitted. Clear saiz and saio boxes so they are not
289 // written.
290 saiz.sample_count = 0;
291 traf_->auxiliary_offset.offsets.clear();
292 }
293 return Status::OK;
294}
295
296bool Fragmenter::StartsWithSAP() const {
297 DCHECK(!traf_->runs.empty());
298 uint32_t start_sample_flag;
299 if (traf_->runs[0].flags & TrackFragmentRun::kSampleFlagsPresentMask) {
300 DCHECK(!traf_->runs[0].sample_flags.empty());
301 start_sample_flag = traf_->runs[0].sample_flags[0];
302 } else {
303 DCHECK(traf_->header.flags &
304 TrackFragmentHeader::kDefaultSampleFlagsPresentMask);
305 start_sample_flag = traf_->header.default_sample_flags;
306 }
307 return (start_sample_flag & TrackFragmentHeader::kNonKeySampleMask) == 0;
308}
309
310} // namespace mp4
311} // namespace media
312} // namespace shaka
Class to hold a media sample.
bool OptimizeSampleEntries(std::vector< T > *entries, T *default_value)
Definition fragmenter.h:108
Status AddSample(const MediaSample &sample)
Definition fragmenter.cc:67
Status InitializeFragment(int64_t first_sample_dts)
void GenerateSegmentReference(SegmentReference *reference) const
Fill reference with current fragment information.
Status FinalizeFragment()
Finalize and optimize the fragment.
Fragmenter(std::shared_ptr< const StreamInfo > info, TrackFragment *traf, int64_t edit_list_offset)
Definition fragmenter.cc:52
All the methods that are virtual are virtual for mocking.