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/video_stream_info.h"
20 #include "packager/media/event/muxer_listener.h"
21 #include "packager/media/event/progress_listener.h"
22 #include "packager/media/formats/mp4/box_definitions.h"
23 #include "packager/media/formats/mp4/key_rotation_fragmenter.h"
25 namespace edash_packager {
31 const uint8_t kCryptByteBlock = 1u;
32 const uint8_t kSkipByteBlock = 9u;
34 const size_t kCencKeyIdSize = 16u;
37 const int kCencSchemeVersion = 0x00010000;
40 const uint8_t kKeyRotationDefaultKeyId[] = {
41 0, 0, 0, 0, 0, 0, 0, 0,
42 0, 0, 0, 0, 0, 0, 0, 0
45 COMPILE_ASSERT(arraysize(kKeyRotationDefaultKeyId) == kCencKeyIdSize,
46 cenc_key_id_must_be_size_16);
48 uint64_t Rescale(uint64_t time_in_old_scale,
51 return static_cast<double>(time_in_old_scale) / old_scale * new_scale;
54 uint8_t GetCryptByteBlock(FourCC protection_scheme) {
55 return (protection_scheme == FOURCC_cbcs || protection_scheme == FOURCC_cens)
60 uint8_t GetSkipByteBlock(FourCC protection_scheme) {
61 return (protection_scheme == FOURCC_cbcs || protection_scheme == FOURCC_cens)
66 void GenerateSinf(
const EncryptionKey& encryption_key,
68 FourCC protection_scheme,
69 ProtectionSchemeInfo* sinf) {
70 sinf->format.format = old_type;
72 DCHECK_NE(protection_scheme, FOURCC_NULL);
73 sinf->type.type = protection_scheme;
74 sinf->type.version = kCencSchemeVersion;
76 auto& track_encryption = sinf->info.track_encryption;
77 track_encryption.default_is_protected = 1;
78 DCHECK(!encryption_key.iv.empty());
79 if (protection_scheme == FOURCC_cbcs) {
82 track_encryption.default_per_sample_iv_size = 0;
83 track_encryption.default_constant_iv = encryption_key.iv;
85 track_encryption.default_per_sample_iv_size = encryption_key.iv.size();
87 track_encryption.default_crypt_byte_block =
88 GetCryptByteBlock(protection_scheme);
89 track_encryption.default_skip_byte_block =
90 GetSkipByteBlock(protection_scheme);
91 track_encryption.default_kid = encryption_key.key_id;
94 void GenerateEncryptedSampleEntry(
const EncryptionKey& encryption_key,
95 double clear_lead_in_seconds,
96 FourCC protection_scheme,
97 SampleDescription* description) {
99 if (description->type == kVideo) {
100 DCHECK_EQ(1u, description->video_entries.size());
103 if (clear_lead_in_seconds > 0)
104 description->video_entries.push_back(description->video_entries[0]);
107 VideoSampleEntry& entry = description->video_entries[0];
108 GenerateSinf(encryption_key, entry.format, protection_scheme, &entry.sinf);
109 entry.format = FOURCC_encv;
111 DCHECK_EQ(kAudio, description->type);
112 DCHECK_EQ(1u, description->audio_entries.size());
115 if (clear_lead_in_seconds > 0)
116 description->audio_entries.push_back(description->audio_entries[0]);
119 AudioSampleEntry& entry = description->audio_entries[0];
120 GenerateSinf(encryption_key, entry.format, protection_scheme, &entry.sinf);
121 entry.format = FOURCC_enca;
125 KeySource::TrackType GetTrackTypeForEncryption(
const StreamInfo& stream_info,
126 uint32_t max_sd_pixels) {
127 if (stream_info.stream_type() == kStreamAudio)
128 return KeySource::TRACK_TYPE_AUDIO;
130 DCHECK_EQ(kStreamVideo, stream_info.stream_type());
131 const VideoStreamInfo& video_stream_info =
132 static_cast<const VideoStreamInfo&
>(stream_info);
133 uint32_t pixels = video_stream_info.width() * video_stream_info.height();
134 return (pixels > max_sd_pixels) ? KeySource::TRACK_TYPE_HD
135 : KeySource::TRACK_TYPE_SD;
140 Segmenter::Segmenter(
const MuxerOptions& options,
141 scoped_ptr<FileType> ftyp,
142 scoped_ptr<Movie> moov)
146 moof_(new MovieFragment()),
147 fragment_buffer_(new BufferWriter()),
148 sidx_(new SegmentIndex()),
149 muxer_listener_(NULL),
150 progress_listener_(NULL),
152 accumulated_progress_(0),
153 sample_duration_(0u) {}
155 Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
161 uint32_t max_sd_pixels,
162 double clear_lead_in_seconds,
163 double crypto_period_duration_in_seconds,
164 FourCC protection_scheme) {
165 DCHECK_LT(0u, streams.size());
166 muxer_listener_ = muxer_listener;
167 progress_listener_ = progress_listener;
168 moof_->header.sequence_number = 0;
170 moof_->tracks.resize(streams.size());
171 segment_durations_.resize(streams.size());
172 fragmenters_.resize(streams.size());
173 const bool key_rotation_enabled = crypto_period_duration_in_seconds != 0;
174 const bool kInitialEncryptionInfo =
true;
176 for (uint32_t i = 0; i < streams.size(); ++i) {
177 stream_map_[streams[i]] = i;
178 moof_->tracks[i].header.track_id = i + 1;
179 if (streams[i]->info()->stream_type() == kStreamVideo) {
181 if (sidx_->reference_id == 0)
182 sidx_->reference_id = i + 1;
184 if (!encryption_key_source) {
185 fragmenters_[i] =
new Fragmenter(&moof_->tracks[i]);
189 FourCC local_protection_scheme = protection_scheme;
190 if (streams[i]->info()->stream_type() != kStreamVideo) {
193 if (protection_scheme == FOURCC_cbcs)
194 local_protection_scheme = FOURCC_cbc1;
195 else if (protection_scheme == FOURCC_cens)
196 local_protection_scheme = FOURCC_cenc;
199 KeySource::TrackType track_type =
200 GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels);
202 moov_->tracks[i].media.information.sample_table.description;
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 local_protection_scheme, &description);
216 if (muxer_listener_) {
217 muxer_listener_->OnEncryptionInfoReady(
218 kInitialEncryptionInfo, encryption_key.key_id, encryption_key.iv,
219 encryption_key.key_system_info);
223 moof_.get(), streams[i]->info(), &moof_->tracks[i],
224 encryption_key_source, track_type,
225 crypto_period_duration_in_seconds * streams[i]->info()->time_scale(),
226 clear_lead_in_seconds * streams[i]->info()->time_scale(),
227 local_protection_scheme, GetCryptByteBlock(local_protection_scheme),
228 GetSkipByteBlock(local_protection_scheme), muxer_listener_);
232 scoped_ptr<EncryptionKey> encryption_key(
new EncryptionKey());
234 encryption_key_source->
GetKey(track_type, encryption_key.get());
237 if (encryption_key->iv.empty()) {
239 &encryption_key->iv)) {
240 return Status(error::INTERNAL_ERROR,
"Failed to generate random iv.");
244 GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
245 local_protection_scheme, &description);
247 if (moov_->pssh.empty()) {
248 moov_->pssh.resize(encryption_key->key_system_info.size());
249 for (
size_t i = 0; i < encryption_key->key_system_info.size(); i++) {
250 moov_->pssh[i].raw_box = encryption_key->key_system_info[i].CreateBox();
253 if (muxer_listener_) {
254 muxer_listener_->OnEncryptionInfoReady(kInitialEncryptionInfo,
255 encryption_key->key_id,
257 encryption_key->key_system_info);
262 streams[i]->info(), &moof_->tracks[i], encryption_key.Pass(),
263 clear_lead_in_seconds * streams[i]->info()->time_scale(),
264 local_protection_scheme, GetCryptByteBlock(local_protection_scheme),
265 GetSkipByteBlock(local_protection_scheme));
269 if (sidx_->reference_id == 0)
270 sidx_->reference_id = 1;
271 sidx_->timescale = streams[GetReferenceStreamId()]->info()->time_scale();
274 progress_target_ = streams[GetReferenceStreamId()]->info()->duration();
277 moov_->header.timescale = sidx_->timescale;
278 moof_->header.sequence_number = 1;
281 moov_->metadata.handler.handler_type = FOURCC_ID32;
282 moov_->metadata.id3v2.language.code =
"eng";
283 moov_->metadata.id3v2.private_frame.owner =
284 "https://github.com/google/edash-packager";
286 return DoInitialize();
290 for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
291 it != fragmenters_.end();
293 Status status = FinalizeFragment(
true, *it);
301 for (std::vector<Track>::iterator track = moov_->tracks.begin();
302 track != moov_->tracks.end();
304 track->header.duration = Rescale(track->media.header.duration,
305 track->media.header.timescale,
306 moov_->header.timescale);
307 if (track->header.duration > moov_->header.duration)
308 moov_->header.duration = track->header.duration;
310 moov_->extends.header.fragment_duration = moov_->header.duration;
316 scoped_refptr<MediaSample> sample) {
319 DCHECK(stream_map_.find(stream) != stream_map_.end());
320 uint32_t stream_id = stream_map_[stream];
321 Fragmenter* fragmenter = fragmenters_[stream_id];
324 if (moov_->extends.tracks[stream_id].default_sample_duration == 0) {
325 moov_->extends.tracks[stream_id].default_sample_duration =
329 if (fragmenter->fragment_finalized()) {
330 return Status(error::FRAGMENT_FINALIZED,
331 "Current fragment is finalized already.");
334 bool finalize_fragment =
false;
335 if (fragmenter->fragment_duration() >=
338 finalize_fragment =
true;
341 bool finalize_segment =
false;
342 if (segment_durations_[stream_id] >=
345 finalize_segment =
true;
346 finalize_fragment =
true;
351 if (finalize_fragment) {
352 status = FinalizeFragment(finalize_segment, fragmenter);
361 if (sample_duration_ == 0)
362 sample_duration_ = sample->duration();
363 moov_->tracks[stream_id].media.header.duration += sample->duration();
364 segment_durations_[stream_id] += sample->duration();
365 DCHECK_GE(segment_durations_[stream_id], fragmenter->fragment_duration());
369 uint32_t Segmenter::GetReferenceTimeScale()
const {
370 return moov_->header.timescale;
374 if (moov_->header.timescale == 0) {
379 return static_cast<double>(moov_->header.duration) / moov_->header.timescale;
383 accumulated_progress_ += progress;
385 if (!progress_listener_)
return;
386 if (progress_target_ == 0)
return;
390 if (accumulated_progress_ >= progress_target_) {
393 progress_listener_->
OnProgress(static_cast<double>(accumulated_progress_) /
398 void Segmenter::SetComplete() {
399 if (!progress_listener_)
return;
403 Status Segmenter::FinalizeSegment() {
404 Status status = DoFinalizeSegment();
407 sidx_->references.clear();
408 std::vector<uint64_t>::iterator it = segment_durations_.begin();
409 for (; it != segment_durations_.end(); ++it)
415 uint32_t Segmenter::GetReferenceStreamId() {
417 return sidx_->reference_id - 1;
420 Status Segmenter::FinalizeFragment(
bool finalize_segment,
421 Fragmenter* fragmenter) {
422 fragmenter->FinalizeFragment();
425 for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
426 it != fragmenters_.end();
428 if (!(*it)->fragment_finalized())
435 uint64_t data_offset = moof_->ComputeSize() + mdat.HeaderSize();
437 uint64_t next_traf_position = moof_->HeaderSize() + moof_->header.box_size();
438 for (
size_t i = 0; i < moof_->tracks.size(); ++i) {
439 TrackFragment& traf = moof_->tracks[i];
440 if (traf.auxiliary_offset.offsets.size() > 0) {
441 DCHECK_EQ(traf.auxiliary_offset.offsets.size(), 1u);
442 DCHECK(!traf.sample_encryption.sample_encryption_entries.empty());
444 next_traf_position += traf.box_size();
447 traf.auxiliary_offset.offsets[0] =
448 next_traf_position - traf.sample_encryption.box_size() +
449 traf.sample_encryption.HeaderSize() +
452 traf.runs[0].data_offset = data_offset + mdat.data_size;
453 mdat.data_size += fragmenters_[i]->data()->Size();
457 sidx_->references.resize(sidx_->references.size() + 1);
458 fragmenters_[GetReferenceStreamId()]->GenerateSegmentReference(
459 &sidx_->references[sidx_->references.size() - 1]);
460 sidx_->references[sidx_->references.size() - 1].referenced_size =
461 data_offset + mdat.data_size;
464 moof_->Write(fragment_buffer_.get());
465 mdat.WriteHeader(fragment_buffer_.get());
466 for (Fragmenter* fragmenter : fragmenters_)
467 fragment_buffer_->AppendBuffer(*fragmenter->data());
470 ++moof_->header.sequence_number;
472 if (finalize_segment)
473 return FinalizeSegment();