7 #include "packager/media/formats/webm/segmenter.h"
9 #include "packager/base/time/time.h"
10 #include "packager/media/base/audio_stream_info.h"
11 #include "packager/media/base/media_sample.h"
12 #include "packager/media/base/media_stream.h"
13 #include "packager/media/base/muxer_options.h"
14 #include "packager/media/base/stream_info.h"
15 #include "packager/media/base/video_stream_info.h"
16 #include "packager/media/event/muxer_listener.h"
17 #include "packager/media/event/progress_listener.h"
18 #include "packager/third_party/libwebm/src/mkvmuxerutil.hpp"
19 #include "packager/third_party/libwebm/src/webmids.hpp"
21 namespace edash_packager {
25 int64_t kTimecodeScale = 1000000;
26 int64_t kSecondsToNs = 1000000000L;
29 Segmenter::Segmenter(
const MuxerOptions& options)
32 muxer_listener_(NULL),
33 progress_listener_(NULL),
35 accumulated_progress_(0),
38 segment_payload_pos_(0),
39 cluster_length_sec_(0),
40 segment_length_sec_(0),
43 Segmenter::~Segmenter() {}
50 uint32_t max_sd_pixels,
51 double clear_lead_in_seconds) {
52 muxer_listener_ = muxer_listener;
54 clear_lead_ = clear_lead_in_seconds;
57 progress_target_ = info_->duration();
58 progress_listener_ = progress_listener;
60 const std::string version_string =
61 "https://github.com/google/edash-packager version " +
65 segment_info_.set_timecode_scale(kTimecodeScale);
66 segment_info_.set_writing_app(version_string.c_str());
70 segment_info_.set_duration(1);
74 if (encryption_key_source) {
75 status = InitializeEncryptor(encryption_key_source, max_sd_pixels);
81 switch (info_->stream_type()) {
83 status = CreateVideoTrack(static_cast<VideoStreamInfo*>(info_));
86 status = CreateAudioTrack(static_cast<AudioStreamInfo*>(info_));
89 NOTIMPLEMENTED() <<
"Not implemented for stream type: "
90 << info_->stream_type();
91 status =
Status(error::UNIMPLEMENTED,
"Not implemented for stream type");
96 return DoInitialize(writer.Pass());
100 segment_info_.set_duration(FromBMFFTimescale(total_duration_));
105 if (sample_duration_ == 0) {
106 sample_duration_ = sample->duration();
116 status = NewSegment(sample->pts());
119 status = NewSegment(sample->pts());
120 segment_length_sec_ = 0;
121 cluster_length_sec_ = 0;
125 status = NewSubsegment(sample->pts());
126 cluster_length_sec_ = 0;
134 const bool encrypt_frame =
135 static_cast<double>(total_duration_) / info_->time_scale() >=
137 status = encryptor_->EncryptFrame(sample, encrypt_frame);
139 LOG(ERROR) <<
"Error encrypting frame.";
144 const int64_t time_ns =
145 sample->pts() * kSecondsToNs / info_->time_scale();
146 bool addframe_result;
147 if (sample->side_data_size() > 0) {
148 uint64_t block_add_id;
151 CHECK_GT(sample->side_data_size(),
sizeof(block_add_id));
152 memcpy(&block_add_id, sample->side_data(),
sizeof(block_add_id));
153 addframe_result = cluster_->AddFrameWithAdditional(
154 sample->data(), sample->data_size(),
155 sample->side_data() +
sizeof(block_add_id),
156 sample->side_data_size() -
sizeof(block_add_id), block_add_id,
157 track_id_, time_ns, sample->is_key_frame());
160 cluster_->AddFrame(sample->data(), sample->data_size(), track_id_,
161 time_ns, sample->is_key_frame());
163 if (!addframe_result) {
164 LOG(ERROR) <<
"Error adding sample to segment.";
165 return Status(error::FILE_FAILURE,
"Error adding sample to segment.");
168 const double duration_sec =
169 static_cast<double>(sample->duration()) / info_->time_scale();
170 cluster_length_sec_ += duration_sec;
171 segment_length_sec_ += duration_sec;
172 total_duration_ += sample->duration();
178 return static_cast<float>(segment_info_.duration()) *
179 segment_info_.timecode_scale() / kSecondsToNs;
182 uint64_t Segmenter::FromBMFFTimescale(uint64_t time_timescale) {
184 const int64_t time_ns =
185 kSecondsToNs * time_timescale / info_->time_scale();
186 return time_ns / segment_info_.timecode_scale();
189 uint64_t Segmenter::FromWebMTimecode(uint64_t time_webm_timecode) {
191 const int64_t time_ns = time_webm_timecode * segment_info_.timecode_scale();
192 return time_ns * info_->time_scale() / kSecondsToNs;
196 Status error_status(error::FILE_FAILURE,
"Error writing segment header.");
198 if (!WriteEbmlHeader(writer))
201 if (WriteID(writer, mkvmuxer::kMkvSegment) != 0)
204 const uint64_t segment_size_size = 8;
205 segment_payload_pos_ = writer->
Position() + segment_size_size;
208 if (WriteUIntSize(writer, file_size - segment_payload_pos_,
209 segment_size_size) != 0)
211 if (!seek_head_.Write(writer))
214 if (SerializeInt(writer, mkvmuxer::kEbmlUnknownValue, segment_size_size) !=
218 if (!seek_head_.WriteVoid(writer))
222 if (!segment_info_.Write(writer))
225 if (!tracks_.Write(writer))
231 Status Segmenter::SetCluster(uint64_t start_webm_timecode,
234 const uint64_t scale = segment_info_.timecode_scale();
235 cluster_.reset(
new mkvmuxer::Cluster(start_webm_timecode, position, scale));
236 cluster_->Init(writer);
241 accumulated_progress_ += progress;
242 if (!progress_listener_ || progress_target_ == 0)
247 if (accumulated_progress_ >= progress_target_) {
250 progress_listener_->
OnProgress(static_cast<double>(accumulated_progress_) /
257 unsigned int seed = 0;
258 mkvmuxer::VideoTrack* track =
new mkvmuxer::VideoTrack(&seed);
260 return Status(error::INTERNAL_ERROR,
"Failed to create video track.");
262 if (info->codec() == kCodecVP8) {
263 track->set_codec_id(mkvmuxer::Tracks::kVp8CodecId);
264 }
else if (info->codec() == kCodecVP9) {
265 track->set_codec_id(mkvmuxer::Tracks::kVp9CodecId);
267 LOG(ERROR) <<
"Only VP8 and VP9 video codec is supported.";
268 return Status(error::UNIMPLEMENTED,
269 "Only VP8 and VP9 video codecs are supported.");
272 track->set_uid(info->track_id());
273 if (!info->language().empty())
274 track->set_language(info->language().c_str());
275 track->set_type(mkvmuxer::Tracks::kVideo);
276 track->set_width(info->width());
277 track->set_height(info->height());
278 track->set_display_height(info->height());
279 track->set_display_width(info->width() * info->
pixel_width() /
283 encryptor_->AddTrackInfo(track);
285 tracks_.AddTrack(track, info->track_id());
286 track_id_ = track->number();
290 Status Segmenter::CreateAudioTrack(AudioStreamInfo* info) {
292 unsigned int seed = 0;
293 mkvmuxer::AudioTrack* track =
new mkvmuxer::AudioTrack(&seed);
295 return Status(error::INTERNAL_ERROR,
"Failed to create audio track.");
297 if (info->codec() == kCodecOpus) {
298 track->set_codec_id(mkvmuxer::Tracks::kOpusCodecId);
299 }
else if (info->codec() == kCodecVorbis) {
300 track->set_codec_id(mkvmuxer::Tracks::kVorbisCodecId);
302 LOG(ERROR) <<
"Only Vorbis and Opus audio codec is supported.";
303 return Status(error::UNIMPLEMENTED,
304 "Only Vorbis and Opus audio codecs are supported.");
306 if (!track->SetCodecPrivate(info->extra_data().data(),
307 info->extra_data().size())) {
308 return Status(error::INTERNAL_ERROR,
309 "Private codec data required for audio streams");
312 track->set_uid(info->track_id());
313 if (!info->language().empty())
314 track->set_language(info->language().c_str());
315 track->set_type(mkvmuxer::Tracks::kAudio);
316 track->set_sample_rate(info->sampling_frequency());
317 track->set_channels(info->num_channels());
320 encryptor_->AddTrackInfo(track);
322 tracks_.AddTrack(track, info->track_id());
323 track_id_ = track->number();
327 Status Segmenter::InitializeEncryptor(KeySource* key_source,
328 uint32_t max_sd_pixels) {
329 encryptor_.reset(
new Encryptor());
330 switch (info_->stream_type()) {
332 VideoStreamInfo* video_info =
static_cast<VideoStreamInfo*
>(info_);
333 uint32_t pixels = video_info->width() * video_info->height();
334 KeySource::TrackType type = (pixels > max_sd_pixels)
335 ? KeySource::TRACK_TYPE_HD
336 : KeySource::TRACK_TYPE_SD;
337 return encryptor_->Initialize(muxer_listener_, type, key_source);
340 return encryptor_->Initialize(
341 muxer_listener_, KeySource::TrackType::TRACK_TYPE_AUDIO, key_source);