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 int64_t kTimecodeScale = 1000000;
34 int64_t kSecondsToNs = 1000000000L;
37 Segmenter::Segmenter(
const MuxerOptions& options) : options_(options) {}
39 Segmenter::~Segmenter() {}
44 muxer_listener_ = muxer_listener;
48 progress_target_ = info_->duration();
49 progress_listener_ = progress_listener;
52 segment_info_.set_timecode_scale(kTimecodeScale);
54 const std::string version = GetPackagerVersion();
55 if (!version.empty()) {
56 segment_info_.set_writing_app(
57 (GetPackagerProjectUrl() +
" version " + version).c_str());
60 if (options().segment_template.empty()) {
64 segment_info_.set_duration(1);
69 unsigned int seed = 0;
70 std::unique_ptr<mkvmuxer::Track> track;
72 switch (info_->stream_type()) {
74 std::unique_ptr<VideoTrack> video_track(
new VideoTrack(&seed));
75 status = InitializeVideoTrack(static_cast<VideoStreamInfo*>(info_),
77 track = std::move(video_track);
81 std::unique_ptr<AudioTrack> audio_track(
new AudioTrack(&seed));
82 status = InitializeAudioTrack(static_cast<AudioStreamInfo*>(info_),
84 track = std::move(audio_track);
88 NOTIMPLEMENTED() <<
"Not implemented for stream type: "
89 << info_->stream_type();
90 status =
Status(error::UNIMPLEMENTED,
"Not implemented for stream type");
95 if (info_->is_encrypted()) {
96 if (info->encryption_config().per_sample_iv_size != kWebMIvSize)
97 return Status(error::MUXER_FAILURE,
"Incorrect size WebM encryption IV.");
98 status = UpdateTrackForEncryption(info_->encryption_config().key_id,
104 tracks_.AddTrack(track.get(), info_->track_id());
106 track_id_ = track->number();
109 return DoInitialize();
114 prev_sample_->pts() - first_timestamp_ + prev_sample_->duration();
115 segment_info_.set_duration(FromBMFFTimescale(duration));
120 if (sample_duration_ == 0) {
121 first_timestamp_ = sample->pts();
122 sample_duration_ = sample->duration();
136 if (new_segment_ || new_subsegment_) {
137 status = NewSegment(sample->pts(), new_subsegment_);
139 status = WriteFrame(
false );
144 if (info_->is_encrypted())
145 UpdateFrameForEncryption(sample.get());
147 new_subsegment_ =
false;
148 new_segment_ =
false;
149 prev_sample_ = sample;
154 uint64_t duration_timescale,
155 bool is_subsegment) {
157 new_subsegment_ =
true;
160 return WriteFrame(
true );
164 return static_cast<float>(segment_info_.duration()) *
165 segment_info_.timecode_scale() / kSecondsToNs;
168 uint64_t Segmenter::FromBMFFTimescale(uint64_t time_timescale) {
170 const int64_t time_ns =
171 kSecondsToNs * time_timescale / info_->time_scale();
172 return time_ns / segment_info_.timecode_scale();
175 uint64_t Segmenter::FromWebMTimecode(uint64_t time_webm_timecode) {
177 const int64_t time_ns = time_webm_timecode * segment_info_.timecode_scale();
178 return time_ns * info_->time_scale() / kSecondsToNs;
182 Status error_status(error::FILE_FAILURE,
"Error writing segment header.");
184 if (!WriteEbmlHeader(writer))
187 if (WriteID(writer, mkvmuxer::kMkvSegment) != 0)
190 const uint64_t segment_size_size = 8;
191 segment_payload_pos_ = writer->
Position() + segment_size_size;
194 if (WriteUIntSize(writer, file_size - segment_payload_pos_,
195 segment_size_size) != 0)
197 if (!seek_head_.Write(writer))
200 if (SerializeInt(writer, mkvmuxer::kEbmlUnknownValue, segment_size_size) !=
204 if (!seek_head_.WriteVoid(writer))
208 seek_head_.set_info_pos(writer->
Position() - segment_payload_pos_);
209 if (!segment_info_.Write(writer))
212 seek_head_.set_tracks_pos(writer->
Position() - segment_payload_pos_);
213 if (!tracks_.Write(writer))
219 Status Segmenter::SetCluster(uint64_t start_webm_timecode,
222 const uint64_t scale = segment_info_.timecode_scale();
223 cluster_.reset(
new mkvmuxer::Cluster(start_webm_timecode, position, scale));
224 cluster_->Init(writer);
229 accumulated_progress_ += progress;
230 if (!progress_listener_ || progress_target_ == 0)
235 if (accumulated_progress_ >= progress_target_) {
238 progress_listener_->
OnProgress(static_cast<double>(accumulated_progress_) /
245 if (info->codec() == kCodecVP8) {
246 track->set_codec_id(mkvmuxer::Tracks::kVp8CodecId);
247 }
else if (info->codec() == kCodecVP9) {
248 track->set_codec_id(mkvmuxer::Tracks::kVp9CodecId);
253 if (!vp_config.
ParseMP4(info->codec_config())) {
254 return Status(error::INTERNAL_ERROR,
255 "Unable to parse VP9 codec configuration");
258 mkvmuxer::Colour colour;
259 if (vp_config.matrix_coefficients() != AVCOL_SPC_UNSPECIFIED) {
260 colour.set_matrix_coefficients(vp_config.matrix_coefficients());
262 if (vp_config.transfer_characteristics() != AVCOL_TRC_UNSPECIFIED) {
263 colour.set_transfer_characteristics(vp_config.transfer_characteristics());
265 if (vp_config.color_primaries() != AVCOL_PRI_UNSPECIFIED) {
266 colour.set_primaries(vp_config.color_primaries());
268 if (!track->SetColour(colour)) {
269 return Status(error::INTERNAL_ERROR,
270 "Failed to setup color element for VPx streams");
273 std::vector<uint8_t> codec_config;
275 if (!track->SetCodecPrivate(codec_config.data(), codec_config.size())) {
276 return Status(error::INTERNAL_ERROR,
277 "Private codec data required for VPx streams");
280 LOG(ERROR) <<
"Only VP8 and VP9 video codecs are supported.";
281 return Status(error::UNIMPLEMENTED,
282 "Only VP8 and VP9 video codecs are supported.");
285 track->set_uid(info->track_id());
286 if (!info->language().empty())
287 track->set_language(info->language().c_str());
288 track->set_type(mkvmuxer::Tracks::kVideo);
289 track->set_width(info->width());
290 track->set_height(info->height());
291 track->set_display_height(info->height());
292 track->set_display_width(info->width() * info->
pixel_width() /
297 Status Segmenter::InitializeAudioTrack(
const AudioStreamInfo* info,
299 if (info->codec() == kCodecOpus) {
300 track->set_codec_id(mkvmuxer::Tracks::kOpusCodecId);
301 }
else if (info->codec() == kCodecVorbis) {
302 track->set_codec_id(mkvmuxer::Tracks::kVorbisCodecId);
304 LOG(ERROR) <<
"Only Vorbis and Opus audio codec is supported.";
305 return Status(error::UNIMPLEMENTED,
306 "Only Vorbis and Opus audio codecs are supported.");
308 if (!track->SetCodecPrivate(info->codec_config().data(),
309 info->codec_config().size())) {
310 return Status(error::INTERNAL_ERROR,
311 "Private codec data required for audio streams");
314 track->set_uid(info->track_id());
315 if (!info->language().empty())
316 track->set_language(info->language().c_str());
317 track->set_type(mkvmuxer::Tracks::kAudio);
318 track->set_sample_rate(info->sampling_frequency());
319 track->set_channels(info->num_channels());
320 track->set_seek_pre_roll(info->seek_preroll_ns());
321 track->set_codec_delay(info->codec_delay_ns());
325 Status Segmenter::WriteFrame(
bool write_duration) {
329 mkvmuxer::Frame frame;
331 if (!frame.Init(prev_sample_->data(), prev_sample_->data_size())) {
332 return Status(error::MUXER_FAILURE,
333 "Error adding sample to segment: Frame::Init failed");
336 if (write_duration) {
337 const uint64_t duration_ns =
338 prev_sample_->duration() * kSecondsToNs / info_->time_scale();
339 frame.set_duration(duration_ns);
341 frame.set_is_key(prev_sample_->is_key_frame());
342 frame.set_timestamp(prev_sample_->pts() * kSecondsToNs / info_->time_scale());
343 frame.set_track_number(track_id_);
345 if (prev_sample_->side_data_size() > 0) {
346 uint64_t block_add_id;
349 CHECK_GT(prev_sample_->side_data_size(),
sizeof(block_add_id));
350 memcpy(&block_add_id, prev_sample_->side_data(),
sizeof(block_add_id));
351 if (!frame.AddAdditionalData(
352 prev_sample_->side_data() +
sizeof(block_add_id),
353 prev_sample_->side_data_size() -
sizeof(block_add_id),
356 error::MUXER_FAILURE,
357 "Error adding sample to segment: Frame::AddAditionalData Failed");
361 if (!prev_sample_->is_key_frame() && !frame.CanBeSimpleBlock()) {
362 const int64_t timestamp_ns =
363 reference_frame_timestamp_ * kSecondsToNs / info_->time_scale();
364 frame.set_reference_block_timestamp(timestamp_ns);
369 if (cluster_->GetRelativeTimecode(frame.timestamp() /
370 cluster_->timecode_scale()) < 0) {
371 const double segment_duration =
372 static_cast<double>(frame.timestamp()) / kSecondsToNs;
373 LOG(ERROR) <<
"Error adding sample to segment: segment too large, "
374 << segment_duration <<
" seconds.";
375 return Status(error::MUXER_FAILURE,
376 "Error adding sample to segment: segment too large");
379 if (!cluster_->AddFrame(&frame)) {
380 return Status(error::MUXER_FAILURE,
381 "Error adding sample to segment: Cluster::AddFrame failed");
387 reference_frame_timestamp_ = prev_sample_->pts();