7 #include "packager/media/formats/mp4/segmenter.h"
11 #include "packager/base/logging.h"
12 #include "packager/base/stl_util.h"
13 #include "packager/media/base/aes_cryptor.h"
14 #include "packager/media/base/buffer_writer.h"
15 #include "packager/media/base/key_source.h"
16 #include "packager/media/base/media_sample.h"
17 #include "packager/media/base/media_stream.h"
18 #include "packager/media/base/muxer_options.h"
19 #include "packager/media/base/muxer_util.h"
20 #include "packager/media/base/video_stream_info.h"
21 #include "packager/media/event/muxer_listener.h"
22 #include "packager/media/event/progress_listener.h"
23 #include "packager/media/formats/mp4/box_definitions.h"
24 #include "packager/media/formats/mp4/key_rotation_fragmenter.h"
32 const uint8_t kCryptByteBlock = 1u;
33 const uint8_t kSkipByteBlock = 9u;
35 const size_t kCencKeyIdSize = 16u;
38 const int kCencSchemeVersion = 0x00010000;
41 const uint8_t kKeyRotationDefaultKeyId[] = {
42 0, 0, 0, 0, 0, 0, 0, 0,
43 0, 0, 0, 0, 0, 0, 0, 0
46 COMPILE_ASSERT(arraysize(kKeyRotationDefaultKeyId) == kCencKeyIdSize,
47 cenc_key_id_must_be_size_16);
49 uint64_t Rescale(uint64_t time_in_old_scale,
52 return static_cast<double>(time_in_old_scale) / old_scale * new_scale;
55 uint8_t GetCryptByteBlock(FourCC protection_scheme) {
56 return (protection_scheme == FOURCC_cbcs || protection_scheme == FOURCC_cens)
61 uint8_t GetSkipByteBlock(FourCC protection_scheme) {
62 return (protection_scheme == FOURCC_cbcs || protection_scheme == FOURCC_cens)
67 void GenerateSinf(
const EncryptionKey& encryption_key,
69 FourCC protection_scheme,
70 ProtectionSchemeInfo* sinf) {
71 sinf->format.format = old_type;
73 DCHECK_NE(protection_scheme, FOURCC_NULL);
74 sinf->type.type = protection_scheme;
75 sinf->type.version = kCencSchemeVersion;
77 auto& track_encryption = sinf->info.track_encryption;
78 track_encryption.default_is_protected = 1;
79 DCHECK(!encryption_key.iv.empty());
80 if (protection_scheme == FOURCC_cbcs) {
83 track_encryption.default_per_sample_iv_size = 0;
84 track_encryption.default_constant_iv = encryption_key.iv;
86 track_encryption.default_per_sample_iv_size = encryption_key.iv.size();
88 track_encryption.default_crypt_byte_block =
89 GetCryptByteBlock(protection_scheme);
90 track_encryption.default_skip_byte_block =
91 GetSkipByteBlock(protection_scheme);
92 track_encryption.default_kid = encryption_key.key_id;
95 void GenerateEncryptedSampleEntry(
const EncryptionKey& encryption_key,
96 double clear_lead_in_seconds,
97 FourCC protection_scheme,
98 SampleDescription* description) {
100 if (description->type == kVideo) {
101 DCHECK_EQ(1u, description->video_entries.size());
104 if (clear_lead_in_seconds > 0)
105 description->video_entries.push_back(description->video_entries[0]);
108 VideoSampleEntry& entry = description->video_entries[0];
109 GenerateSinf(encryption_key, entry.format, protection_scheme, &entry.sinf);
110 entry.format = FOURCC_encv;
112 DCHECK_EQ(kAudio, description->type);
113 DCHECK_EQ(1u, description->audio_entries.size());
116 if (clear_lead_in_seconds > 0)
117 description->audio_entries.push_back(description->audio_entries[0]);
120 AudioSampleEntry& entry = description->audio_entries[0];
121 GenerateSinf(encryption_key, entry.format, protection_scheme, &entry.sinf);
122 entry.format = FOURCC_enca;
128 Segmenter::Segmenter(
const MuxerOptions& options,
129 scoped_ptr<FileType> ftyp,
130 scoped_ptr<Movie> moov)
134 moof_(new MovieFragment()),
135 fragment_buffer_(new BufferWriter()),
136 sidx_(new SegmentIndex()),
137 muxer_listener_(NULL),
138 progress_listener_(NULL),
140 accumulated_progress_(0),
141 sample_duration_(0u) {}
143 Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
149 uint32_t max_sd_pixels,
150 double clear_lead_in_seconds,
151 double crypto_period_duration_in_seconds,
152 FourCC protection_scheme) {
153 DCHECK_LT(0u, streams.size());
154 muxer_listener_ = muxer_listener;
155 progress_listener_ = progress_listener;
156 moof_->header.sequence_number = 0;
158 moof_->tracks.resize(streams.size());
159 segment_durations_.resize(streams.size());
160 fragmenters_.resize(streams.size());
161 const bool key_rotation_enabled = crypto_period_duration_in_seconds != 0;
162 const bool kInitialEncryptionInfo =
true;
164 for (uint32_t i = 0; i < streams.size(); ++i) {
165 stream_map_[streams[i]] = i;
166 moof_->tracks[i].header.track_id = i + 1;
167 if (streams[i]->info()->stream_type() == kStreamVideo) {
169 if (sidx_->reference_id == 0)
170 sidx_->reference_id = i + 1;
172 if (!encryption_key_source) {
173 fragmenters_[i] =
new Fragmenter(streams[i]->info(), &moof_->tracks[i]);
177 FourCC local_protection_scheme = protection_scheme;
178 if (streams[i]->info()->stream_type() != kStreamVideo) {
181 if (protection_scheme == FOURCC_cbcs)
182 local_protection_scheme = FOURCC_cbc1;
183 else if (protection_scheme == FOURCC_cens)
184 local_protection_scheme = FOURCC_cenc;
187 KeySource::TrackType track_type =
188 GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels);
190 moov_->tracks[i].media.information.sample_table.description;
192 if (key_rotation_enabled) {
195 encryption_key.key_id.assign(
196 kKeyRotationDefaultKeyId,
197 kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId));
199 &encryption_key.iv)) {
200 return Status(error::INTERNAL_ERROR,
"Failed to generate random iv.");
202 GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds,
203 local_protection_scheme, &description);
204 if (muxer_listener_) {
205 muxer_listener_->OnEncryptionInfoReady(
206 kInitialEncryptionInfo, local_protection_scheme,
207 encryption_key.key_id, encryption_key.iv,
208 encryption_key.key_system_info);
212 moof_.get(), streams[i]->info(), &moof_->tracks[i],
213 encryption_key_source, track_type,
214 crypto_period_duration_in_seconds * streams[i]->info()->time_scale(),
215 clear_lead_in_seconds * streams[i]->info()->time_scale(),
216 local_protection_scheme, GetCryptByteBlock(local_protection_scheme),
217 GetSkipByteBlock(local_protection_scheme), muxer_listener_);
221 scoped_ptr<EncryptionKey> encryption_key(
new EncryptionKey());
223 encryption_key_source->
GetKey(track_type, encryption_key.get());
226 if (encryption_key->iv.empty()) {
228 &encryption_key->iv)) {
229 return Status(error::INTERNAL_ERROR,
"Failed to generate random iv.");
233 GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
234 local_protection_scheme, &description);
236 if (moov_->pssh.empty()) {
237 moov_->pssh.resize(encryption_key->key_system_info.size());
238 for (
size_t i = 0; i < encryption_key->key_system_info.size(); i++) {
239 moov_->pssh[i].raw_box = encryption_key->key_system_info[i].CreateBox();
242 if (muxer_listener_) {
243 muxer_listener_->OnEncryptionInfoReady(
244 kInitialEncryptionInfo, local_protection_scheme,
245 encryption_key->key_id, encryption_key->iv,
246 encryption_key->key_system_info);
251 streams[i]->info(), &moof_->tracks[i], encryption_key.Pass(),
252 clear_lead_in_seconds * streams[i]->info()->time_scale(),
253 local_protection_scheme, GetCryptByteBlock(local_protection_scheme),
254 GetSkipByteBlock(local_protection_scheme));
258 if (sidx_->reference_id == 0)
259 sidx_->reference_id = 1;
260 sidx_->timescale = streams[GetReferenceStreamId()]->info()->time_scale();
263 progress_target_ = streams[GetReferenceStreamId()]->info()->duration();
266 moov_->header.timescale = sidx_->timescale;
267 moof_->header.sequence_number = 1;
270 moov_->metadata.handler.handler_type = FOURCC_ID32;
271 moov_->metadata.id3v2.language.code =
"eng";
272 moov_->metadata.id3v2.private_frame.owner =
273 "https://github.com/google/shaka-packager";
275 return DoInitialize();
279 for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
280 it != fragmenters_.end();
282 Status status = FinalizeFragment(
true, *it);
290 for (std::vector<Track>::iterator track = moov_->tracks.begin();
291 track != moov_->tracks.end();
293 track->header.duration = Rescale(track->media.header.duration,
294 track->media.header.timescale,
295 moov_->header.timescale);
296 if (track->header.duration > moov_->header.duration)
297 moov_->header.duration = track->header.duration;
299 moov_->extends.header.fragment_duration = moov_->header.duration;
305 scoped_refptr<MediaSample> sample) {
308 DCHECK(stream_map_.find(stream) != stream_map_.end());
309 uint32_t stream_id = stream_map_[stream];
310 Fragmenter* fragmenter = fragmenters_[stream_id];
313 if (moov_->extends.tracks[stream_id].default_sample_duration == 0) {
314 moov_->extends.tracks[stream_id].default_sample_duration =
318 if (fragmenter->fragment_finalized()) {
319 return Status(error::FRAGMENT_FINALIZED,
320 "Current fragment is finalized already.");
323 bool finalize_fragment =
false;
324 if (fragmenter->fragment_duration() >=
327 finalize_fragment =
true;
330 bool finalize_segment =
false;
331 if (segment_durations_[stream_id] >=
334 finalize_segment =
true;
335 finalize_fragment =
true;
340 if (finalize_fragment) {
341 status = FinalizeFragment(finalize_segment, fragmenter);
350 if (sample_duration_ == 0)
351 sample_duration_ = sample->duration();
352 moov_->tracks[stream_id].media.header.duration += sample->duration();
353 segment_durations_[stream_id] += sample->duration();
354 DCHECK_GE(segment_durations_[stream_id], fragmenter->fragment_duration());
358 uint32_t Segmenter::GetReferenceTimeScale()
const {
359 return moov_->header.timescale;
363 if (moov_->header.timescale == 0) {
368 return static_cast<double>(moov_->header.duration) / moov_->header.timescale;
372 accumulated_progress_ += progress;
374 if (!progress_listener_)
return;
375 if (progress_target_ == 0)
return;
379 if (accumulated_progress_ >= progress_target_) {
382 progress_listener_->
OnProgress(static_cast<double>(accumulated_progress_) /
387 void Segmenter::SetComplete() {
388 if (!progress_listener_)
return;
392 Status Segmenter::FinalizeSegment() {
393 Status status = DoFinalizeSegment();
396 sidx_->references.clear();
397 std::vector<uint64_t>::iterator it = segment_durations_.begin();
398 for (; it != segment_durations_.end(); ++it)
404 uint32_t Segmenter::GetReferenceStreamId() {
406 return sidx_->reference_id - 1;
409 Status Segmenter::FinalizeFragment(
bool finalize_segment,
410 Fragmenter* fragmenter) {
411 fragmenter->FinalizeFragment();
414 for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
415 it != fragmenters_.end();
417 if (!(*it)->fragment_finalized())
424 uint64_t data_offset = moof_->ComputeSize() + mdat.HeaderSize();
426 uint64_t next_traf_position = moof_->HeaderSize() + moof_->header.box_size();
427 for (
size_t i = 0; i < moof_->tracks.size(); ++i) {
428 TrackFragment& traf = moof_->tracks[i];
429 if (traf.auxiliary_offset.offsets.size() > 0) {
430 DCHECK_EQ(traf.auxiliary_offset.offsets.size(), 1u);
431 DCHECK(!traf.sample_encryption.sample_encryption_entries.empty());
433 next_traf_position += traf.box_size();
436 traf.auxiliary_offset.offsets[0] =
437 next_traf_position - traf.sample_encryption.box_size() +
438 traf.sample_encryption.HeaderSize() +
441 traf.runs[0].data_offset = data_offset + mdat.data_size;
442 mdat.data_size += fragmenters_[i]->data()->Size();
446 sidx_->references.resize(sidx_->references.size() + 1);
447 fragmenters_[GetReferenceStreamId()]->GenerateSegmentReference(
448 &sidx_->references[sidx_->references.size() - 1]);
449 sidx_->references[sidx_->references.size() - 1].referenced_size =
450 data_offset + mdat.data_size;
453 moof_->Write(fragment_buffer_.get());
454 mdat.WriteHeader(fragment_buffer_.get());
455 for (Fragmenter* fragmenter : fragmenters_)
456 fragment_buffer_->AppendBuffer(*fragmenter->data());
459 ++moof_->header.sequence_number;
461 if (finalize_segment)
462 return FinalizeSegment();