7 #include "packager/media/formats/mp4/segmenter.h"
11 #include "packager/base/logging.h"
12 #include "packager/media/base/aes_cryptor.h"
13 #include "packager/media/base/buffer_writer.h"
14 #include "packager/media/base/key_source.h"
15 #include "packager/media/base/media_sample.h"
16 #include "packager/media/base/muxer_options.h"
17 #include "packager/media/base/muxer_util.h"
18 #include "packager/media/base/video_stream_info.h"
19 #include "packager/media/event/muxer_listener.h"
20 #include "packager/media/event/progress_listener.h"
21 #include "packager/media/formats/mp4/box_definitions.h"
22 #include "packager/media/formats/mp4/key_rotation_fragmenter.h"
23 #include "packager/version/version.h"
30 const size_t kCencKeyIdSize = 16u;
33 const int kCencSchemeVersion = 0x00010000;
36 const uint8_t kKeyRotationDefaultKeyId[] = {
37 0, 0, 0, 0, 0, 0, 0, 0,
38 0, 0, 0, 0, 0, 0, 0, 0
42 struct ProtectionPattern {
43 uint8_t crypt_byte_block;
44 uint8_t skip_byte_block;
47 static_assert(arraysize(kKeyRotationDefaultKeyId) == kCencKeyIdSize,
48 "cenc_key_id_must_be_size_16");
50 uint64_t Rescale(uint64_t time_in_old_scale,
53 return static_cast<double>(time_in_old_scale) / old_scale * new_scale;
56 ProtectionPattern GetProtectionPattern(FourCC protection_scheme,
57 TrackType track_type) {
58 ProtectionPattern pattern;
59 if (protection_scheme != FOURCC_cbcs && protection_scheme != FOURCC_cens) {
61 pattern.crypt_byte_block = 0u;
62 pattern.skip_byte_block = 0u;
63 }
else if (track_type != kVideo) {
71 pattern.crypt_byte_block = 1u;
72 pattern.skip_byte_block = 0u;
75 const uint8_t kCryptByteBlock = 1u;
76 const uint8_t kSkipByteBlock = 9u;
77 pattern.crypt_byte_block = kCryptByteBlock;
78 pattern.skip_byte_block = kSkipByteBlock;
83 void GenerateSinf(
const EncryptionKey& encryption_key,
85 FourCC protection_scheme,
86 ProtectionPattern pattern,
87 ProtectionSchemeInfo* sinf) {
88 sinf->format.format = old_type;
90 DCHECK_NE(protection_scheme, FOURCC_NULL);
91 sinf->type.type = protection_scheme;
92 sinf->type.version = kCencSchemeVersion;
94 auto& track_encryption = sinf->info.track_encryption;
95 track_encryption.default_is_protected = 1;
96 DCHECK(!encryption_key.iv.empty());
97 if (protection_scheme == FOURCC_cbcs) {
100 track_encryption.default_per_sample_iv_size = 0;
101 track_encryption.default_constant_iv = encryption_key.iv;
103 track_encryption.default_per_sample_iv_size =
104 static_cast<uint8_t
>(encryption_key.iv.size());
106 track_encryption.default_crypt_byte_block = pattern.crypt_byte_block;
107 track_encryption.default_skip_byte_block = pattern.skip_byte_block;
108 track_encryption.default_kid = encryption_key.key_id;
111 void GenerateEncryptedSampleEntry(
const EncryptionKey& encryption_key,
112 double clear_lead_in_seconds,
113 FourCC protection_scheme,
114 ProtectionPattern pattern,
115 SampleDescription* description) {
117 if (description->type == kVideo) {
118 DCHECK_EQ(1u, description->video_entries.size());
121 if (clear_lead_in_seconds > 0)
122 description->video_entries.push_back(description->video_entries[0]);
125 VideoSampleEntry& entry = description->video_entries[0];
126 GenerateSinf(encryption_key, entry.format, protection_scheme, pattern,
128 entry.format = FOURCC_encv;
130 DCHECK_EQ(kAudio, description->type);
131 DCHECK_EQ(1u, description->audio_entries.size());
134 if (clear_lead_in_seconds > 0)
135 description->audio_entries.push_back(description->audio_entries[0]);
138 AudioSampleEntry& entry = description->audio_entries[0];
139 GenerateSinf(encryption_key, entry.format, protection_scheme, pattern,
141 entry.format = FOURCC_enca;
147 Segmenter::Segmenter(
const MuxerOptions& options,
148 std::unique_ptr<FileType> ftyp,
149 std::unique_ptr<Movie> moov)
151 ftyp_(std::move(ftyp)),
152 moov_(std::move(moov)),
153 moof_(new MovieFragment()),
154 fragment_buffer_(new BufferWriter()),
155 sidx_(new SegmentIndex()),
156 muxer_listener_(NULL),
157 progress_listener_(NULL),
159 accumulated_progress_(0),
160 sample_duration_(0u) {}
162 Segmenter::~Segmenter() {}
165 const std::vector<std::shared_ptr<StreamInfo>>& streams,
169 uint32_t max_sd_pixels,
170 uint32_t max_hd_pixels,
171 uint32_t max_uhd1_pixels,
172 double clear_lead_in_seconds,
173 double crypto_period_duration_in_seconds,
174 FourCC protection_scheme) {
175 DCHECK_LT(0u, streams.size());
176 muxer_listener_ = muxer_listener;
177 progress_listener_ = progress_listener;
178 moof_->header.sequence_number = 0;
180 moof_->tracks.resize(streams.size());
181 fragmenters_.resize(streams.size());
182 const bool key_rotation_enabled = crypto_period_duration_in_seconds != 0;
183 const bool kInitialEncryptionInfo =
true;
185 for (uint32_t i = 0; i < streams.size(); ++i) {
186 moof_->tracks[i].header.track_id = i + 1;
187 if (streams[i]->stream_type() == kStreamVideo) {
189 if (sidx_->reference_id == 0)
190 sidx_->reference_id = i + 1;
192 if (!encryption_key_source) {
193 fragmenters_[i].reset(
new Fragmenter(streams[i], &moof_->tracks[i]));
197 KeySource::TrackType track_type = GetTrackTypeForEncryption(
198 *streams[i], max_sd_pixels, max_hd_pixels, max_uhd1_pixels);
200 moov_->tracks[i].media.information.sample_table.description;
201 ProtectionPattern pattern =
202 GetProtectionPattern(protection_scheme, description.type);
204 if (key_rotation_enabled) {
207 encryption_key.key_id.assign(
208 kKeyRotationDefaultKeyId,
209 kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId));
211 &encryption_key.iv)) {
212 return Status(error::INTERNAL_ERROR,
"Failed to generate random iv.");
214 GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds,
215 protection_scheme, pattern, &description);
216 if (muxer_listener_) {
217 muxer_listener_->OnEncryptionInfoReady(
218 kInitialEncryptionInfo, protection_scheme, encryption_key.key_id,
219 encryption_key.iv, encryption_key.key_system_info);
223 moof_.get(), streams[i], &moof_->tracks[i], encryption_key_source,
225 crypto_period_duration_in_seconds * streams[i]->time_scale(),
226 clear_lead_in_seconds * streams[i]->time_scale(), protection_scheme,
227 pattern.crypt_byte_block, pattern.skip_byte_block, muxer_listener_));
231 std::unique_ptr<EncryptionKey> encryption_key(
new EncryptionKey());
233 encryption_key_source->
GetKey(track_type, encryption_key.get());
236 if (encryption_key->iv.empty()) {
238 &encryption_key->iv)) {
239 return Status(error::INTERNAL_ERROR,
"Failed to generate random iv.");
243 GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
244 protection_scheme, pattern, &description);
246 if (moov_->pssh.empty()) {
247 moov_->pssh.resize(encryption_key->key_system_info.size());
248 for (
size_t i = 0; i < encryption_key->key_system_info.size(); i++) {
249 moov_->pssh[i].raw_box = encryption_key->key_system_info[i].CreateBox();
252 if (muxer_listener_) {
253 muxer_listener_->OnEncryptionInfoReady(
254 kInitialEncryptionInfo, protection_scheme, encryption_key->key_id,
255 encryption_key->iv, encryption_key->key_system_info);
260 streams[i], &moof_->tracks[i], std::move(encryption_key),
261 clear_lead_in_seconds * streams[i]->time_scale(), protection_scheme,
262 pattern.crypt_byte_block, pattern.skip_byte_block, muxer_listener_));
266 for (uint32_t i = 0; i < streams.size(); ++i)
267 fragmenters_[i]->set_use_decoding_timestamp_in_timeline(
true);
271 if (sidx_->reference_id == 0)
272 sidx_->reference_id = 1;
273 sidx_->timescale = streams[GetReferenceStreamId()]->time_scale();
276 progress_target_ = streams[GetReferenceStreamId()]->duration();
279 moov_->header.timescale = sidx_->timescale;
280 moof_->header.sequence_number = 1;
283 const std::string version = GetPackagerVersion();
284 if (!version.empty()) {
285 moov_->metadata.handler.handler_type = FOURCC_ID32;
286 moov_->metadata.id3v2.language.code =
"eng";
287 moov_->metadata.id3v2.private_frame.owner = GetPackagerProjectUrl();
288 moov_->metadata.id3v2.private_frame.value = version;
290 return DoInitialize();
297 for (std::vector<Track>::iterator track = moov_->tracks.begin();
298 track != moov_->tracks.end();
300 track->header.duration = Rescale(track->media.header.duration,
301 track->media.header.timescale,
302 moov_->header.timescale);
303 if (track->header.duration > moov_->header.duration)
304 moov_->header.duration = track->header.duration;
306 moov_->extends.header.fragment_duration = moov_->header.duration;
312 std::shared_ptr<MediaSample> sample) {
314 if (moov_->extends.tracks[stream_id].default_sample_duration == 0) {
315 moov_->extends.tracks[stream_id].default_sample_duration =
319 DCHECK_LT(stream_id, fragmenters_.size());
320 Fragmenter* fragmenter = fragmenters_[stream_id].get();
321 if (fragmenter->fragment_finalized()) {
322 return Status(error::FRAGMENT_FINALIZED,
323 "Current fragment is finalized already.");
326 Status status = fragmenter->AddSample(sample);
330 if (sample_duration_ == 0)
331 sample_duration_ = sample->duration();
332 moov_->tracks[stream_id].media.header.duration += sample->duration();
337 DCHECK_LT(stream_id, fragmenters_.size());
338 Fragmenter* fragmenter = fragmenters_[stream_id].get();
340 fragmenter->FinalizeFragment();
343 for (
const std::unique_ptr<Fragmenter>& fragmenter : fragmenters_) {
344 if (!fragmenter->fragment_finalized())
353 uint64_t next_traf_position = moof_->HeaderSize() + moof_->header.box_size();
354 for (
size_t i = 0; i < moof_->tracks.size(); ++i) {
356 if (traf.auxiliary_offset.offsets.size() > 0) {
357 DCHECK_EQ(traf.auxiliary_offset.offsets.size(), 1u);
358 DCHECK(!traf.sample_encryption.sample_encryption_entries.empty());
360 next_traf_position += traf.
box_size();
363 traf.auxiliary_offset.offsets[0] =
364 next_traf_position - traf.sample_encryption.
box_size() +
368 traf.runs[0].data_offset = data_offset + mdat.data_size;
369 mdat.data_size +=
static_cast<uint32_t
>(fragmenters_[i]->data()->Size());
373 sidx_->references.resize(sidx_->references.size() + 1);
374 fragmenters_[GetReferenceStreamId()]->GenerateSegmentReference(
375 &sidx_->references[sidx_->references.size() - 1]);
376 sidx_->references[sidx_->references.size() - 1].referenced_size =
377 data_offset + mdat.data_size;
380 moof_->Write(fragment_buffer_.get());
382 for (
const std::unique_ptr<Fragmenter>& fragmenter : fragmenters_)
383 fragment_buffer_->AppendBuffer(*fragmenter->data());
386 ++moof_->header.sequence_number;
388 for (std::unique_ptr<Fragmenter>& fragmenter : fragmenters_)
389 fragmenter->ClearFragmentFinalized();
390 if (!is_subsegment) {
391 Status status = DoFinalizeSegment();
393 sidx_->references.clear();
399 uint32_t Segmenter::GetReferenceTimeScale()
const {
400 return moov_->header.timescale;
404 if (moov_->header.timescale == 0) {
409 return static_cast<double>(moov_->header.duration) / moov_->header.timescale;
413 accumulated_progress_ += progress;
415 if (!progress_listener_)
return;
416 if (progress_target_ == 0)
return;
420 if (accumulated_progress_ >= progress_target_) {
423 progress_listener_->
OnProgress(static_cast<double>(accumulated_progress_) /
428 void Segmenter::SetComplete() {
429 if (!progress_listener_)
return;
433 uint32_t Segmenter::GetReferenceStreamId() {
435 return sidx_->reference_id - 1;