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_handler.h"
12 #include "packager/media/base/media_sample.h"
13 #include "packager/media/base/muxer_options.h"
14 #include "packager/media/base/muxer_util.h"
15 #include "packager/media/base/stream_info.h"
16 #include "packager/media/base/video_stream_info.h"
17 #include "packager/media/codecs/vp_codec_configuration_record.h"
18 #include "packager/media/event/muxer_listener.h"
19 #include "packager/media/event/progress_listener.h"
20 #include "packager/media/formats/webm/encryptor.h"
21 #include "packager/media/formats/webm/webm_constants.h"
22 #include "packager/third_party/libwebm/src/mkvmuxerutil.hpp"
23 #include "packager/third_party/libwebm/src/webmids.hpp"
24 #include "packager/version/version.h"
26 using mkvmuxer::AudioTrack;
27 using mkvmuxer::VideoTrack;
33 const int64_t kTimecodeScale = 1000000;
34 const int64_t kSecondsToNs = 1000000000L;
37 uint64_t Round(
double value) {
38 return static_cast<uint64_t
>(value + 0.5);
52 uint64_t BmffTimestampToNs(uint64_t timestamp, uint64_t time_scale) {
55 return Round(static_cast<double>(timestamp) / time_scale * kSecondsToNs);
58 uint64_t NsToBmffTimestamp(uint64_t ns, uint64_t time_scale) {
61 return Round(static_cast<double>(ns) / kSecondsToNs * time_scale);
64 uint64_t NsToWebMTimecode(uint64_t ns, uint64_t timecode_scale) {
65 return ns / timecode_scale;
68 uint64_t WebMTimecodeToNs(uint64_t timecode, uint64_t timecode_scale) {
69 return timecode * timecode_scale;
74 Segmenter::Segmenter(
const MuxerOptions& options) : options_(options) {}
76 Segmenter::~Segmenter() {}
81 is_encrypted_ = info.is_encrypted();
82 duration_ = info.duration();
83 time_scale_ = info.time_scale();
85 muxer_listener_ = muxer_listener;
88 progress_target_ = info.duration();
89 progress_listener_ = progress_listener;
92 segment_info_.set_timecode_scale(kTimecodeScale);
94 const std::string version = GetPackagerVersion();
95 if (!version.empty()) {
96 segment_info_.set_writing_app(
97 (GetPackagerProjectUrl() +
" version " + version).c_str());
100 if (options().segment_template.empty()) {
104 segment_info_.set_duration(1);
109 unsigned int seed = 0;
110 std::unique_ptr<mkvmuxer::Track> track;
112 switch (info.stream_type()) {
114 std::unique_ptr<VideoTrack> video_track(
new VideoTrack(&seed));
115 status = InitializeVideoTrack(static_cast<const VideoStreamInfo&>(info),
117 track = std::move(video_track);
121 std::unique_ptr<AudioTrack> audio_track(
new AudioTrack(&seed));
122 status = InitializeAudioTrack(static_cast<const AudioStreamInfo&>(info),
124 track = std::move(audio_track);
128 NOTIMPLEMENTED() <<
"Not implemented for stream type: "
129 << info.stream_type();
130 status =
Status(error::UNIMPLEMENTED,
"Not implemented for stream type");
135 if (info.is_encrypted()) {
136 if (info.encryption_config().per_sample_iv_size != kWebMIvSize)
137 return Status(error::MUXER_FAILURE,
"Incorrect size WebM encryption IV.");
138 status = UpdateTrackForEncryption(info.encryption_config().key_id,
144 tracks_.AddTrack(track.get(), info.track_id());
146 track_id_ = track->number();
149 return DoInitialize();
154 prev_sample_->pts() - first_timestamp_ + prev_sample_->duration();
155 segment_info_.set_duration(FromBmffTimestamp(duration));
162 if (sample_duration_ == 0) {
163 first_timestamp_ = sample->pts();
164 sample_duration_ = sample->duration();
178 if (new_segment_ || new_subsegment_) {
179 status = NewSegment(sample->pts(), new_subsegment_);
181 status = WriteFrame(
false );
187 UpdateFrameForEncryption(sample.get());
189 new_subsegment_ =
false;
190 new_segment_ =
false;
191 prev_sample_ = sample;
196 uint64_t duration_timestamp,
197 bool is_subsegment) {
199 new_subsegment_ =
true;
202 return WriteFrame(
true );
205 float Segmenter::GetDurationInSeconds()
const {
206 return WebMTimecodeToNs(segment_info_.duration(),
207 segment_info_.timecode_scale()) /
208 static_cast<double>(kSecondsToNs);
211 uint64_t Segmenter::FromBmffTimestamp(uint64_t bmff_timestamp) {
212 return NsToWebMTimecode(
213 BmffTimestampToNs(bmff_timestamp, time_scale_),
214 segment_info_.timecode_scale());
217 uint64_t Segmenter::FromWebMTimecode(uint64_t webm_timecode) {
218 return NsToBmffTimestamp(
219 WebMTimecodeToNs(webm_timecode, segment_info_.timecode_scale()),
224 Status error_status(error::FILE_FAILURE,
"Error writing segment header.");
226 if (!WriteEbmlHeader(writer))
229 if (WriteID(writer, mkvmuxer::kMkvSegment) != 0)
232 const uint64_t segment_size_size = 8;
233 segment_payload_pos_ = writer->
Position() + segment_size_size;
236 if (WriteUIntSize(writer, file_size - segment_payload_pos_,
237 segment_size_size) != 0)
239 if (!seek_head_.Write(writer))
242 if (SerializeInt(writer, mkvmuxer::kEbmlUnknownValue, segment_size_size) !=
246 if (!seek_head_.WriteVoid(writer))
250 seek_head_.set_info_pos(writer->
Position() - segment_payload_pos_);
251 if (!segment_info_.Write(writer))
254 seek_head_.set_tracks_pos(writer->
Position() - segment_payload_pos_);
255 if (!tracks_.Write(writer))
261 Status Segmenter::SetCluster(uint64_t start_webm_timecode,
264 const uint64_t scale = segment_info_.timecode_scale();
265 cluster_.reset(
new mkvmuxer::Cluster(start_webm_timecode, position, scale));
266 cluster_->Init(writer);
271 accumulated_progress_ += progress;
272 if (!progress_listener_ || progress_target_ == 0)
277 if (accumulated_progress_ >= progress_target_) {
280 progress_listener_->
OnProgress(static_cast<double>(accumulated_progress_) /
287 if (info.codec() == kCodecVP8) {
288 track->set_codec_id(mkvmuxer::Tracks::kVp8CodecId);
289 }
else if (info.codec() == kCodecVP9) {
290 track->set_codec_id(mkvmuxer::Tracks::kVp9CodecId);
295 if (!vp_config.
ParseMP4(info.codec_config())) {
296 return Status(error::INTERNAL_ERROR,
297 "Unable to parse VP9 codec configuration");
300 mkvmuxer::Colour colour;
301 if (vp_config.matrix_coefficients() != AVCOL_SPC_UNSPECIFIED) {
302 colour.set_matrix_coefficients(vp_config.matrix_coefficients());
304 if (vp_config.transfer_characteristics() != AVCOL_TRC_UNSPECIFIED) {
305 colour.set_transfer_characteristics(vp_config.transfer_characteristics());
307 if (vp_config.color_primaries() != AVCOL_PRI_UNSPECIFIED) {
308 colour.set_primaries(vp_config.color_primaries());
310 if (!track->SetColour(colour)) {
311 return Status(error::INTERNAL_ERROR,
312 "Failed to setup color element for VPx streams");
315 std::vector<uint8_t> codec_config;
317 if (!track->SetCodecPrivate(codec_config.data(), codec_config.size())) {
318 return Status(error::INTERNAL_ERROR,
319 "Private codec data required for VPx streams");
322 LOG(ERROR) <<
"Only VP8 and VP9 video codecs are supported in WebM.";
323 return Status(error::UNIMPLEMENTED,
324 "Only VP8 and VP9 video codecs are supported in WebM.");
327 track->set_uid(info.track_id());
328 if (!info.language().empty())
329 track->set_language(info.language().c_str());
330 track->set_type(mkvmuxer::Tracks::kVideo);
331 track->set_width(info.width());
332 track->set_height(info.height());
333 track->set_display_height(info.height());
334 track->set_display_width(info.width() * info.
pixel_width() /
339 Status Segmenter::InitializeAudioTrack(
const AudioStreamInfo& info,
341 if (info.codec() == kCodecOpus) {
342 track->set_codec_id(mkvmuxer::Tracks::kOpusCodecId);
343 }
else if (info.codec() == kCodecVorbis) {
344 track->set_codec_id(mkvmuxer::Tracks::kVorbisCodecId);
346 LOG(ERROR) <<
"Only Vorbis and Opus audio codec are supported in WebM.";
347 return Status(error::UNIMPLEMENTED,
348 "Only Vorbis and Opus audio codecs are supported in WebM.");
350 if (!track->SetCodecPrivate(info.codec_config().data(),
351 info.codec_config().size())) {
352 return Status(error::INTERNAL_ERROR,
353 "Private codec data required for audio streams");
356 track->set_uid(info.track_id());
357 if (!info.language().empty())
358 track->set_language(info.language().c_str());
359 track->set_type(mkvmuxer::Tracks::kAudio);
360 track->set_sample_rate(info.sampling_frequency());
361 track->set_channels(info.num_channels());
362 track->set_seek_pre_roll(info.seek_preroll_ns());
363 track->set_codec_delay(info.codec_delay_ns());
367 Status Segmenter::WriteFrame(
bool write_duration) {
371 mkvmuxer::Frame frame;
373 if (!frame.Init(prev_sample_->data(), prev_sample_->data_size())) {
374 return Status(error::MUXER_FAILURE,
375 "Error adding sample to segment: Frame::Init failed");
378 if (write_duration) {
380 BmffTimestampToNs(prev_sample_->duration(), time_scale_));
382 frame.set_is_key(prev_sample_->is_key_frame());
384 BmffTimestampToNs(prev_sample_->pts(), time_scale_));
385 frame.set_track_number(track_id_);
387 if (prev_sample_->side_data_size() > 0) {
388 uint64_t block_add_id;
391 CHECK_GT(prev_sample_->side_data_size(),
sizeof(block_add_id));
392 memcpy(&block_add_id, prev_sample_->side_data(),
sizeof(block_add_id));
393 if (!frame.AddAdditionalData(
394 prev_sample_->side_data() +
sizeof(block_add_id),
395 prev_sample_->side_data_size() -
sizeof(block_add_id),
398 error::MUXER_FAILURE,
399 "Error adding sample to segment: Frame::AddAditionalData Failed");
403 if (!prev_sample_->is_key_frame() && !frame.CanBeSimpleBlock()) {
404 frame.set_reference_block_timestamp(
405 BmffTimestampToNs(reference_frame_timestamp_, time_scale_));
410 if (cluster_->GetRelativeTimecode(NsToWebMTimecode(
411 frame.timestamp(), cluster_->timecode_scale())) < 0) {
412 const double segment_duration =
413 static_cast<double>(frame.timestamp() -
414 WebMTimecodeToNs(cluster_->timecode(),
415 cluster_->timecode_scale())) /
417 LOG(ERROR) <<
"Error adding sample to segment: segment too large, "
419 <<
" seconds. Please check your GOP size and segment duration.";
420 return Status(error::MUXER_FAILURE,
421 "Error adding sample to segment: segment too large");
424 if (!cluster_->AddFrame(&frame)) {
425 return Status(error::MUXER_FAILURE,
426 "Error adding sample to segment: Cluster::AddFrame failed");
432 reference_frame_timestamp_ = prev_sample_->pts();