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
41 COMPILE_ASSERT(arraysize(kKeyRotationDefaultKeyId) == kCencKeyIdSize,
42 cenc_key_id_must_be_size_16);
44 uint64_t Rescale(uint64_t time_in_old_scale,
47 return static_cast<double>(time_in_old_scale) / old_scale * new_scale;
50 void GenerateSinf(
const EncryptionKey& encryption_key,
52 ProtectionSchemeInfo* sinf) {
53 sinf->format.format = old_type;
54 sinf->type.type = FOURCC_CENC;
55 sinf->type.version = kCencSchemeVersion;
56 sinf->info.track_encryption.is_encrypted =
true;
57 sinf->info.track_encryption.default_iv_size =
58 encryption_key.iv.empty() ? kDefaultIvSize : encryption_key.iv.size();
59 sinf->info.track_encryption.default_kid = encryption_key.key_id;
62 void GenerateEncryptedSampleEntry(
const EncryptionKey& encryption_key,
63 double clear_lead_in_seconds,
64 SampleDescription* description) {
66 if (description->type == kVideo) {
67 DCHECK_EQ(1u, description->video_entries.size());
70 if (clear_lead_in_seconds > 0)
71 description->video_entries.push_back(description->video_entries[0]);
74 VideoSampleEntry& entry = description->video_entries[0];
75 GenerateSinf(encryption_key, entry.format, &entry.sinf);
76 entry.format = FOURCC_ENCV;
78 DCHECK_EQ(kAudio, description->type);
79 DCHECK_EQ(1u, description->audio_entries.size());
82 if (clear_lead_in_seconds > 0)
83 description->audio_entries.push_back(description->audio_entries[0]);
86 AudioSampleEntry& entry = description->audio_entries[0];
87 GenerateSinf(encryption_key, entry.format, &entry.sinf);
88 entry.format = FOURCC_ENCA;
92 uint8_t GetNaluLengthSize(
const StreamInfo& stream_info) {
93 if (stream_info.stream_type() != kStreamVideo)
95 const VideoStreamInfo& video_stream_info =
96 static_cast<const VideoStreamInfo&
>(stream_info);
97 return video_stream_info.nalu_length_size();
100 KeySource::TrackType GetTrackTypeForEncryption(
const StreamInfo& stream_info,
101 uint32_t max_sd_pixels) {
102 if (stream_info.stream_type() == kStreamAudio)
103 return KeySource::TRACK_TYPE_AUDIO;
105 DCHECK_EQ(kStreamVideo, stream_info.stream_type());
106 const VideoStreamInfo& video_stream_info =
107 static_cast<const VideoStreamInfo&
>(stream_info);
108 uint32_t pixels = video_stream_info.width() * video_stream_info.height();
109 return (pixels > max_sd_pixels) ? KeySource::TRACK_TYPE_HD
110 : KeySource::TRACK_TYPE_SD;
115 Segmenter::Segmenter(
const MuxerOptions& options,
116 scoped_ptr<FileType> ftyp,
117 scoped_ptr<Movie> moov)
121 moof_(new MovieFragment()),
122 fragment_buffer_(new BufferWriter()),
123 sidx_(new SegmentIndex()),
124 muxer_listener_(NULL),
125 progress_listener_(NULL),
127 accumulated_progress_(0),
128 sample_duration_(0u) {}
130 Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
136 uint32_t max_sd_pixels,
137 double clear_lead_in_seconds,
138 double crypto_period_duration_in_seconds) {
139 DCHECK_LT(0u, streams.size());
140 muxer_listener_ = muxer_listener;
141 progress_listener_ = progress_listener;
142 moof_->header.sequence_number = 0;
144 moof_->tracks.resize(streams.size());
145 segment_durations_.resize(streams.size());
146 fragmenters_.resize(streams.size());
147 const bool key_rotation_enabled = crypto_period_duration_in_seconds != 0;
148 const bool kInitialEncryptionInfo =
true;
150 for (uint32_t i = 0; i < streams.size(); ++i) {
151 stream_map_[streams[i]] = i;
152 moof_->tracks[i].header.track_id = i + 1;
153 if (streams[i]->info()->stream_type() == kStreamVideo) {
155 if (sidx_->reference_id == 0)
156 sidx_->reference_id = i + 1;
158 if (!encryption_key_source) {
159 fragmenters_[i] =
new Fragmenter(&moof_->tracks[i]);
163 uint8_t nalu_length_size = GetNaluLengthSize(*streams[i]->info());
164 KeySource::TrackType track_type =
165 GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels);
167 moov_->tracks[i].media.information.sample_table.description;
169 if (key_rotation_enabled) {
172 encryption_key.key_id.assign(
173 kKeyRotationDefaultKeyId,
174 kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId));
175 GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds,
177 if (muxer_listener_) {
178 muxer_listener_->OnEncryptionInfoReady(
179 kInitialEncryptionInfo, encryption_key_source->
UUID(),
180 encryption_key_source->
SystemName(), encryption_key.key_id,
181 std::vector<uint8_t>());
187 encryption_key_source,
189 crypto_period_duration_in_seconds * streams[i]->info()->time_scale(),
190 clear_lead_in_seconds * streams[i]->info()->time_scale(),
196 scoped_ptr<EncryptionKey> encryption_key(
new EncryptionKey());
198 encryption_key_source->
GetKey(track_type, encryption_key.get());
202 GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
206 if (moov_->pssh.empty()) {
207 moov_->pssh.resize(1);
208 moov_->pssh[0].raw_box = encryption_key->pssh;
211 if (muxer_listener_) {
212 muxer_listener_->OnEncryptionInfoReady(
213 kInitialEncryptionInfo,
214 encryption_key_source->
UUID(), encryption_key_source->
SystemName(),
215 encryption_key->key_id, encryption_key->pssh);
221 encryption_key.Pass(),
222 clear_lead_in_seconds * streams[i]->info()->time_scale(),
227 if (sidx_->reference_id == 0)
228 sidx_->reference_id = 1;
229 sidx_->timescale = streams[GetReferenceStreamId()]->info()->time_scale();
232 progress_target_ = streams[GetReferenceStreamId()]->info()->duration();
235 moov_->header.timescale = sidx_->timescale;
236 moof_->header.sequence_number = 1;
237 return DoInitialize();
241 for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
242 it != fragmenters_.end();
244 Status status = FinalizeFragment(
true, *it);
252 for (std::vector<Track>::iterator track = moov_->tracks.begin();
253 track != moov_->tracks.end();
255 track->header.duration = Rescale(track->media.header.duration,
256 track->media.header.timescale,
257 moov_->header.timescale);
258 if (track->header.duration > moov_->header.duration)
259 moov_->header.duration = track->header.duration;
261 moov_->extends.header.fragment_duration = moov_->header.duration;
267 scoped_refptr<MediaSample> sample) {
270 DCHECK(stream_map_.find(stream) != stream_map_.end());
271 uint32_t stream_id = stream_map_[stream];
272 Fragmenter* fragmenter = fragmenters_[stream_id];
275 if (moov_->extends.tracks[stream_id].default_sample_duration == 0) {
276 moov_->extends.tracks[stream_id].default_sample_duration =
280 if (fragmenter->fragment_finalized()) {
281 return Status(error::FRAGMENT_FINALIZED,
282 "Current fragment is finalized already.");
285 bool finalize_fragment =
false;
286 if (fragmenter->fragment_duration() >=
289 finalize_fragment =
true;
292 bool finalize_segment =
false;
293 if (segment_durations_[stream_id] >=
296 finalize_segment =
true;
297 finalize_fragment =
true;
302 if (finalize_fragment) {
303 status = FinalizeFragment(finalize_segment, fragmenter);
312 if (sample_duration_ == 0)
313 sample_duration_ = sample->duration();
314 moov_->tracks[stream_id].media.header.duration += sample->duration();
315 segment_durations_[stream_id] += sample->duration();
316 DCHECK_GE(segment_durations_[stream_id], fragmenter->fragment_duration());
320 uint32_t Segmenter::GetReferenceTimeScale()
const {
321 return moov_->header.timescale;
325 if (moov_->header.timescale == 0) {
330 return static_cast<double>(moov_->header.duration) / moov_->header.timescale;
334 accumulated_progress_ += progress;
336 if (!progress_listener_)
return;
337 if (progress_target_ == 0)
return;
341 if (accumulated_progress_ >= progress_target_) {
344 progress_listener_->
OnProgress(static_cast<double>(accumulated_progress_) /
349 void Segmenter::SetComplete() {
350 if (!progress_listener_)
return;
354 Status Segmenter::FinalizeSegment() {
355 Status status = DoFinalizeSegment();
358 sidx_->references.clear();
359 std::vector<uint64_t>::iterator it = segment_durations_.begin();
360 for (; it != segment_durations_.end(); ++it)
366 uint32_t Segmenter::GetReferenceStreamId() {
368 return sidx_->reference_id - 1;
371 Status Segmenter::FinalizeFragment(
bool finalize_segment,
372 Fragmenter* fragmenter) {
373 fragmenter->FinalizeFragment();
376 for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
377 it != fragmenters_.end();
379 if (!(*it)->fragment_finalized())
386 uint64_t base = moof_->ComputeSize() + mdat.ComputeSize();
387 for (
size_t i = 0; i < moof_->tracks.size(); ++i) {
388 TrackFragment& traf = moof_->tracks[i];
389 Fragmenter* fragmenter = fragmenters_[i];
390 if (fragmenter->aux_data()->Size() > 0) {
391 traf.auxiliary_offset.offsets[0] += base;
392 base += fragmenter->aux_data()->Size();
394 traf.runs[0].data_offset += base;
395 base += fragmenter->data()->Size();
399 sidx_->references.resize(sidx_->references.size() + 1);
400 fragmenters_[GetReferenceStreamId()]->GenerateSegmentReference(
401 &sidx_->references[sidx_->references.size() - 1]);
402 sidx_->references[sidx_->references.size() - 1].referenced_size = base;
405 moof_->Write(fragment_buffer_.get());
407 for (
size_t i = 0; i < moof_->tracks.size(); ++i) {
408 Fragmenter* fragmenter = fragmenters_[i];
410 fragmenter->aux_data()->Size() + fragmenter->data()->Size();
411 mdat.WriteHeader(fragment_buffer_.get());
412 if (fragmenter->aux_data()->Size()) {
413 fragment_buffer_->AppendBuffer(*fragmenter->aux_data());
415 fragment_buffer_->AppendBuffer(*fragmenter->data());
419 ++moof_->header.sequence_number;
421 if (finalize_segment)
422 return FinalizeSegment();