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 muxer_listener_ = muxer_listener;
85 progress_target_ = info_->duration();
86 progress_listener_ = progress_listener;
89 segment_info_.set_timecode_scale(kTimecodeScale);
91 const std::string version = GetPackagerVersion();
92 if (!version.empty()) {
93 segment_info_.set_writing_app(
94 (GetPackagerProjectUrl() +
" version " + version).c_str());
97 if (options().segment_template.empty()) {
101 segment_info_.set_duration(1);
106 unsigned int seed = 0;
107 std::unique_ptr<mkvmuxer::Track> track;
109 switch (info_->stream_type()) {
111 std::unique_ptr<VideoTrack> video_track(
new VideoTrack(&seed));
112 status = InitializeVideoTrack(static_cast<VideoStreamInfo*>(info_),
114 track = std::move(video_track);
118 std::unique_ptr<AudioTrack> audio_track(
new AudioTrack(&seed));
119 status = InitializeAudioTrack(static_cast<AudioStreamInfo*>(info_),
121 track = std::move(audio_track);
125 NOTIMPLEMENTED() <<
"Not implemented for stream type: "
126 << info_->stream_type();
127 status =
Status(error::UNIMPLEMENTED,
"Not implemented for stream type");
132 if (info_->is_encrypted()) {
133 if (info->encryption_config().per_sample_iv_size != kWebMIvSize)
134 return Status(error::MUXER_FAILURE,
"Incorrect size WebM encryption IV.");
135 status = UpdateTrackForEncryption(info_->encryption_config().key_id,
141 tracks_.AddTrack(track.get(), info_->track_id());
143 track_id_ = track->number();
146 return DoInitialize();
151 prev_sample_->pts() - first_timestamp_ + prev_sample_->duration();
152 segment_info_.set_duration(FromBmffTimestamp(duration));
157 if (sample_duration_ == 0) {
158 first_timestamp_ = sample->pts();
159 sample_duration_ = sample->duration();
173 if (new_segment_ || new_subsegment_) {
174 status = NewSegment(sample->pts(), new_subsegment_);
176 status = WriteFrame(
false );
181 if (info_->is_encrypted())
182 UpdateFrameForEncryption(sample.get());
184 new_subsegment_ =
false;
185 new_segment_ =
false;
186 prev_sample_ = sample;
191 uint64_t duration_timestamp,
192 bool is_subsegment) {
194 new_subsegment_ =
true;
197 return WriteFrame(
true );
200 float Segmenter::GetDurationInSeconds()
const {
201 return WebMTimecodeToNs(segment_info_.duration(),
202 segment_info_.timecode_scale()) /
203 static_cast<double>(kSecondsToNs);
206 uint64_t Segmenter::FromBmffTimestamp(uint64_t bmff_timestamp) {
207 return NsToWebMTimecode(
208 BmffTimestampToNs(bmff_timestamp, info_->time_scale()),
209 segment_info_.timecode_scale());
212 uint64_t Segmenter::FromWebMTimecode(uint64_t webm_timecode) {
213 return NsToBmffTimestamp(
214 WebMTimecodeToNs(webm_timecode, segment_info_.timecode_scale()),
215 info_->time_scale());
219 Status error_status(error::FILE_FAILURE,
"Error writing segment header.");
221 if (!WriteEbmlHeader(writer))
224 if (WriteID(writer, mkvmuxer::kMkvSegment) != 0)
227 const uint64_t segment_size_size = 8;
228 segment_payload_pos_ = writer->
Position() + segment_size_size;
231 if (WriteUIntSize(writer, file_size - segment_payload_pos_,
232 segment_size_size) != 0)
234 if (!seek_head_.Write(writer))
237 if (SerializeInt(writer, mkvmuxer::kEbmlUnknownValue, segment_size_size) !=
241 if (!seek_head_.WriteVoid(writer))
245 seek_head_.set_info_pos(writer->
Position() - segment_payload_pos_);
246 if (!segment_info_.Write(writer))
249 seek_head_.set_tracks_pos(writer->
Position() - segment_payload_pos_);
250 if (!tracks_.Write(writer))
256 Status Segmenter::SetCluster(uint64_t start_webm_timecode,
259 const uint64_t scale = segment_info_.timecode_scale();
260 cluster_.reset(
new mkvmuxer::Cluster(start_webm_timecode, position, scale));
261 cluster_->Init(writer);
266 accumulated_progress_ += progress;
267 if (!progress_listener_ || progress_target_ == 0)
272 if (accumulated_progress_ >= progress_target_) {
275 progress_listener_->
OnProgress(static_cast<double>(accumulated_progress_) /
282 if (info->codec() == kCodecVP8) {
283 track->set_codec_id(mkvmuxer::Tracks::kVp8CodecId);
284 }
else if (info->codec() == kCodecVP9) {
285 track->set_codec_id(mkvmuxer::Tracks::kVp9CodecId);
290 if (!vp_config.
ParseMP4(info->codec_config())) {
291 return Status(error::INTERNAL_ERROR,
292 "Unable to parse VP9 codec configuration");
295 mkvmuxer::Colour colour;
296 if (vp_config.matrix_coefficients() != AVCOL_SPC_UNSPECIFIED) {
297 colour.set_matrix_coefficients(vp_config.matrix_coefficients());
299 if (vp_config.transfer_characteristics() != AVCOL_TRC_UNSPECIFIED) {
300 colour.set_transfer_characteristics(vp_config.transfer_characteristics());
302 if (vp_config.color_primaries() != AVCOL_PRI_UNSPECIFIED) {
303 colour.set_primaries(vp_config.color_primaries());
305 if (!track->SetColour(colour)) {
306 return Status(error::INTERNAL_ERROR,
307 "Failed to setup color element for VPx streams");
310 std::vector<uint8_t> codec_config;
312 if (!track->SetCodecPrivate(codec_config.data(), codec_config.size())) {
313 return Status(error::INTERNAL_ERROR,
314 "Private codec data required for VPx streams");
317 LOG(ERROR) <<
"Only VP8 and VP9 video codecs are supported in WebM.";
318 return Status(error::UNIMPLEMENTED,
319 "Only VP8 and VP9 video codecs are supported in WebM.");
322 track->set_uid(info->track_id());
323 if (!info->language().empty())
324 track->set_language(info->language().c_str());
325 track->set_type(mkvmuxer::Tracks::kVideo);
326 track->set_width(info->width());
327 track->set_height(info->height());
328 track->set_display_height(info->height());
329 track->set_display_width(info->width() * info->
pixel_width() /
334 Status Segmenter::InitializeAudioTrack(
const AudioStreamInfo* info,
336 if (info->codec() == kCodecOpus) {
337 track->set_codec_id(mkvmuxer::Tracks::kOpusCodecId);
338 }
else if (info->codec() == kCodecVorbis) {
339 track->set_codec_id(mkvmuxer::Tracks::kVorbisCodecId);
341 LOG(ERROR) <<
"Only Vorbis and Opus audio codec are supported in WebM.";
342 return Status(error::UNIMPLEMENTED,
343 "Only Vorbis and Opus audio codecs are supported in WebM.");
345 if (!track->SetCodecPrivate(info->codec_config().data(),
346 info->codec_config().size())) {
347 return Status(error::INTERNAL_ERROR,
348 "Private codec data required for audio streams");
351 track->set_uid(info->track_id());
352 if (!info->language().empty())
353 track->set_language(info->language().c_str());
354 track->set_type(mkvmuxer::Tracks::kAudio);
355 track->set_sample_rate(info->sampling_frequency());
356 track->set_channels(info->num_channels());
357 track->set_seek_pre_roll(info->seek_preroll_ns());
358 track->set_codec_delay(info->codec_delay_ns());
362 Status Segmenter::WriteFrame(
bool write_duration) {
366 mkvmuxer::Frame frame;
368 if (!frame.Init(prev_sample_->data(), prev_sample_->data_size())) {
369 return Status(error::MUXER_FAILURE,
370 "Error adding sample to segment: Frame::Init failed");
373 if (write_duration) {
375 BmffTimestampToNs(prev_sample_->duration(), info_->time_scale()));
377 frame.set_is_key(prev_sample_->is_key_frame());
379 BmffTimestampToNs(prev_sample_->pts(), info_->time_scale()));
380 frame.set_track_number(track_id_);
382 if (prev_sample_->side_data_size() > 0) {
383 uint64_t block_add_id;
386 CHECK_GT(prev_sample_->side_data_size(),
sizeof(block_add_id));
387 memcpy(&block_add_id, prev_sample_->side_data(),
sizeof(block_add_id));
388 if (!frame.AddAdditionalData(
389 prev_sample_->side_data() +
sizeof(block_add_id),
390 prev_sample_->side_data_size() -
sizeof(block_add_id),
393 error::MUXER_FAILURE,
394 "Error adding sample to segment: Frame::AddAditionalData Failed");
398 if (!prev_sample_->is_key_frame() && !frame.CanBeSimpleBlock()) {
399 frame.set_reference_block_timestamp(
400 BmffTimestampToNs(reference_frame_timestamp_, info_->time_scale()));
405 if (cluster_->GetRelativeTimecode(NsToWebMTimecode(
406 frame.timestamp(), cluster_->timecode_scale())) < 0) {
407 const double segment_duration =
408 static_cast<double>(frame.timestamp()) / kSecondsToNs;
409 LOG(ERROR) <<
"Error adding sample to segment: segment too large, "
410 << segment_duration <<
" seconds.";
411 return Status(error::MUXER_FAILURE,
412 "Error adding sample to segment: segment too large");
415 if (!cluster_->AddFrame(&frame)) {
416 return Status(error::MUXER_FAILURE,
417 "Error adding sample to segment: Cluster::AddFrame failed");
423 reference_frame_timestamp_ = prev_sample_->pts();