7 #include "packager/media/formats/mp4/segmenter.h"
11 #include "packager/base/stl_util.h"
12 #include "packager/media/base/buffer_writer.h"
13 #include "packager/media/base/key_source.h"
14 #include "packager/media/base/media_sample.h"
15 #include "packager/media/base/media_stream.h"
16 #include "packager/media/base/muxer_options.h"
17 #include "packager/media/base/video_stream_info.h"
18 #include "packager/media/event/muxer_listener.h"
19 #include "packager/media/event/progress_listener.h"
20 #include "packager/media/formats/mp4/box_definitions.h"
21 #include "packager/media/formats/mp4/key_rotation_fragmenter.h"
23 namespace edash_packager {
30 const size_t kDefaultIvSize = 8u;
31 const size_t kCencKeyIdSize = 16u;
34 const int kCencSchemeVersion = 0x00010000;
37 const uint8_t kKeyRotationDefaultKeyId[] = {
38 0, 0, 0, 0, 0, 0, 0, 0,
39 0, 0, 0, 0, 0, 0, 0, 0
42 COMPILE_ASSERT(arraysize(kKeyRotationDefaultKeyId) == kCencKeyIdSize,
43 cenc_key_id_must_be_size_16);
45 uint64_t Rescale(uint64_t time_in_old_scale,
48 return static_cast<double>(time_in_old_scale) / old_scale * new_scale;
51 void GenerateSinf(
const EncryptionKey& encryption_key,
53 EncryptionMode encryption_mode,
54 ProtectionSchemeInfo* sinf) {
55 sinf->format.format = old_type;
57 if (encryption_mode == kEncryptionModeAesCtr){
58 sinf->type.type = FOURCC_CENC;
59 }
else if (encryption_mode == kEncryptionModeAesCbc) {
60 sinf->type.type = FOURCC_CBC1;
63 sinf->type.version = kCencSchemeVersion;
64 sinf->info.track_encryption.is_encrypted =
true;
65 sinf->info.track_encryption.default_iv_size =
66 encryption_key.iv.empty() ? kDefaultIvSize : encryption_key.iv.size();
67 sinf->info.track_encryption.default_kid = encryption_key.key_id;
70 void GenerateEncryptedSampleEntry(
const EncryptionKey& encryption_key,
71 double clear_lead_in_seconds,
72 EncryptionMode encryption_mode,
73 SampleDescription* description) {
75 if (description->type == kVideo) {
76 DCHECK_EQ(1u, description->video_entries.size());
79 if (clear_lead_in_seconds > 0)
80 description->video_entries.push_back(description->video_entries[0]);
83 VideoSampleEntry& entry = description->video_entries[0];
84 GenerateSinf(encryption_key, entry.format, encryption_mode, &entry.sinf);
85 entry.format = FOURCC_ENCV;
87 DCHECK_EQ(kAudio, description->type);
88 DCHECK_EQ(1u, description->audio_entries.size());
91 if (clear_lead_in_seconds > 0)
92 description->audio_entries.push_back(description->audio_entries[0]);
95 AudioSampleEntry& entry = description->audio_entries[0];
96 GenerateSinf(encryption_key, entry.format, encryption_mode, &entry.sinf);
97 entry.format = FOURCC_ENCA;
101 KeySource::TrackType GetTrackTypeForEncryption(
const StreamInfo& stream_info,
102 uint32_t max_sd_pixels) {
103 if (stream_info.stream_type() == kStreamAudio)
104 return KeySource::TRACK_TYPE_AUDIO;
106 DCHECK_EQ(kStreamVideo, stream_info.stream_type());
107 const VideoStreamInfo& video_stream_info =
108 static_cast<const VideoStreamInfo&
>(stream_info);
109 uint32_t pixels = video_stream_info.width() * video_stream_info.height();
110 return (pixels > max_sd_pixels) ? KeySource::TRACK_TYPE_HD
111 : KeySource::TRACK_TYPE_SD;
116 Segmenter::Segmenter(
const MuxerOptions& options,
117 scoped_ptr<FileType> ftyp,
118 scoped_ptr<Movie> moov)
122 moof_(new MovieFragment()),
123 fragment_buffer_(new BufferWriter()),
124 sidx_(new SegmentIndex()),
125 muxer_listener_(NULL),
126 progress_listener_(NULL),
128 accumulated_progress_(0),
129 sample_duration_(0u) {}
131 Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
137 uint32_t max_sd_pixels,
138 double clear_lead_in_seconds,
139 double crypto_period_duration_in_seconds,
140 EncryptionMode encryption_mode) {
141 DCHECK_LT(0u, streams.size());
142 muxer_listener_ = muxer_listener;
143 progress_listener_ = progress_listener;
144 moof_->header.sequence_number = 0;
146 moof_->tracks.resize(streams.size());
147 segment_durations_.resize(streams.size());
148 fragmenters_.resize(streams.size());
149 const bool key_rotation_enabled = crypto_period_duration_in_seconds != 0;
150 const bool kInitialEncryptionInfo =
true;
152 for (uint32_t i = 0; i < streams.size(); ++i) {
153 stream_map_[streams[i]] = i;
154 moof_->tracks[i].header.track_id = i + 1;
155 if (streams[i]->info()->stream_type() == kStreamVideo) {
157 if (sidx_->reference_id == 0)
158 sidx_->reference_id = i + 1;
160 if (!encryption_key_source) {
161 fragmenters_[i] =
new Fragmenter(&moof_->tracks[i]);
165 KeySource::TrackType track_type =
166 GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels);
168 moov_->tracks[i].media.information.sample_table.description;
170 if (key_rotation_enabled) {
173 encryption_key.key_id.assign(
174 kKeyRotationDefaultKeyId,
175 kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId));
176 GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds,
177 encryption_mode, &description);
178 if (muxer_listener_) {
179 muxer_listener_->OnEncryptionInfoReady(
180 kInitialEncryptionInfo, encryption_key.key_id,
181 encryption_key.key_system_info);
185 moof_.get(), streams[i]->info(), &moof_->tracks[i],
186 encryption_key_source, track_type,
187 crypto_period_duration_in_seconds * streams[i]->info()->time_scale(),
188 clear_lead_in_seconds * streams[i]->info()->time_scale(),
189 muxer_listener_, encryption_mode);
193 scoped_ptr<EncryptionKey> encryption_key(
new EncryptionKey());
195 encryption_key_source->
GetKey(track_type, encryption_key.get());
199 GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
200 encryption_mode, &description);
202 if (moov_->pssh.empty()) {
203 moov_->pssh.resize(encryption_key->key_system_info.size());
204 for (
size_t i = 0; i < encryption_key->key_system_info.size(); i++) {
205 moov_->pssh[i].raw_box = encryption_key->key_system_info[i].CreateBox();
208 if (muxer_listener_) {
209 muxer_listener_->OnEncryptionInfoReady(kInitialEncryptionInfo,
210 encryption_key->key_id,
211 encryption_key->key_system_info);
216 streams[i]->info(), &moof_->tracks[i], encryption_key.Pass(),
217 clear_lead_in_seconds * streams[i]->info()->time_scale(),
222 if (sidx_->reference_id == 0)
223 sidx_->reference_id = 1;
224 sidx_->timescale = streams[GetReferenceStreamId()]->info()->time_scale();
227 progress_target_ = streams[GetReferenceStreamId()]->info()->duration();
230 moov_->header.timescale = sidx_->timescale;
231 moof_->header.sequence_number = 1;
234 moov_->metadata.handler.handler_type = FOURCC_ID32;
235 moov_->metadata.id3v2.language.code =
"eng";
236 moov_->metadata.id3v2.private_frame.owner =
237 "https://github.com/google/edash-packager";
239 return DoInitialize();
243 for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
244 it != fragmenters_.end();
246 Status status = FinalizeFragment(
true, *it);
254 for (std::vector<Track>::iterator track = moov_->tracks.begin();
255 track != moov_->tracks.end();
257 track->header.duration = Rescale(track->media.header.duration,
258 track->media.header.timescale,
259 moov_->header.timescale);
260 if (track->header.duration > moov_->header.duration)
261 moov_->header.duration = track->header.duration;
263 moov_->extends.header.fragment_duration = moov_->header.duration;
269 scoped_refptr<MediaSample> sample) {
272 DCHECK(stream_map_.find(stream) != stream_map_.end());
273 uint32_t stream_id = stream_map_[stream];
274 Fragmenter* fragmenter = fragmenters_[stream_id];
277 if (moov_->extends.tracks[stream_id].default_sample_duration == 0) {
278 moov_->extends.tracks[stream_id].default_sample_duration =
282 if (fragmenter->fragment_finalized()) {
283 return Status(error::FRAGMENT_FINALIZED,
284 "Current fragment is finalized already.");
287 bool finalize_fragment =
false;
288 if (fragmenter->fragment_duration() >=
291 finalize_fragment =
true;
294 bool finalize_segment =
false;
295 if (segment_durations_[stream_id] >=
298 finalize_segment =
true;
299 finalize_fragment =
true;
304 if (finalize_fragment) {
305 status = FinalizeFragment(finalize_segment, fragmenter);
314 if (sample_duration_ == 0)
315 sample_duration_ = sample->duration();
316 moov_->tracks[stream_id].media.header.duration += sample->duration();
317 segment_durations_[stream_id] += sample->duration();
318 DCHECK_GE(segment_durations_[stream_id], fragmenter->fragment_duration());
322 uint32_t Segmenter::GetReferenceTimeScale()
const {
323 return moov_->header.timescale;
327 if (moov_->header.timescale == 0) {
332 return static_cast<double>(moov_->header.duration) / moov_->header.timescale;
336 accumulated_progress_ += progress;
338 if (!progress_listener_)
return;
339 if (progress_target_ == 0)
return;
343 if (accumulated_progress_ >= progress_target_) {
346 progress_listener_->
OnProgress(static_cast<double>(accumulated_progress_) /
351 void Segmenter::SetComplete() {
352 if (!progress_listener_)
return;
356 Status Segmenter::FinalizeSegment() {
357 Status status = DoFinalizeSegment();
360 sidx_->references.clear();
361 std::vector<uint64_t>::iterator it = segment_durations_.begin();
362 for (; it != segment_durations_.end(); ++it)
368 uint32_t Segmenter::GetReferenceStreamId() {
370 return sidx_->reference_id - 1;
373 Status Segmenter::FinalizeFragment(
bool finalize_segment,
374 Fragmenter* fragmenter) {
375 fragmenter->FinalizeFragment();
378 for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
379 it != fragmenters_.end();
381 if (!(*it)->fragment_finalized())
388 uint64_t data_offset = moof_->ComputeSize() + mdat.HeaderSize();
390 uint64_t next_traf_position = moof_->HeaderSize() + moof_->header.box_size();
391 for (
size_t i = 0; i < moof_->tracks.size(); ++i) {
392 TrackFragment& traf = moof_->tracks[i];
393 if (traf.auxiliary_offset.offsets.size() > 0) {
394 DCHECK_EQ(traf.auxiliary_offset.offsets.size(), 1u);
395 DCHECK(!traf.sample_encryption.sample_encryption_entries.empty());
397 next_traf_position += traf.box_size();
400 traf.auxiliary_offset.offsets[0] =
401 next_traf_position - traf.sample_encryption.box_size() +
402 traf.sample_encryption.HeaderSize() +
405 traf.runs[0].data_offset = data_offset + mdat.data_size;
406 mdat.data_size += fragmenters_[i]->data()->Size();
410 sidx_->references.resize(sidx_->references.size() + 1);
411 fragmenters_[GetReferenceStreamId()]->GenerateSegmentReference(
412 &sidx_->references[sidx_->references.size() - 1]);
413 sidx_->references[sidx_->references.size() - 1].referenced_size =
414 data_offset + mdat.data_size;
417 moof_->Write(fragment_buffer_.get());
418 mdat.WriteHeader(fragment_buffer_.get());
419 for (Fragmenter* fragmenter : fragmenters_)
420 fragment_buffer_->AppendBuffer(*fragmenter->data());
423 ++moof_->header.sequence_number;
425 if (finalize_segment)
426 return FinalizeSegment();