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 std::vector<uint8_t> codec_config;
260 if (!track->SetCodecPrivate(codec_config.data(), codec_config.size())) {
261 return Status(error::INTERNAL_ERROR,
262 "Private codec data required for VP9 streams");
265 LOG(ERROR) <<
"Only VP8 and VP9 video codecs are supported.";
266 return Status(error::UNIMPLEMENTED,
267 "Only VP8 and VP9 video codecs are supported.");
270 track->set_uid(info->track_id());
271 if (!info->language().empty())
272 track->set_language(info->language().c_str());
273 track->set_type(mkvmuxer::Tracks::kVideo);
274 track->set_width(info->width());
275 track->set_height(info->height());
276 track->set_display_height(info->height());
277 track->set_display_width(info->width() * info->
pixel_width() /
282 Status Segmenter::InitializeAudioTrack(
const AudioStreamInfo* info,
284 if (info->codec() == kCodecOpus) {
285 track->set_codec_id(mkvmuxer::Tracks::kOpusCodecId);
286 }
else if (info->codec() == kCodecVorbis) {
287 track->set_codec_id(mkvmuxer::Tracks::kVorbisCodecId);
289 LOG(ERROR) <<
"Only Vorbis and Opus audio codec is supported.";
290 return Status(error::UNIMPLEMENTED,
291 "Only Vorbis and Opus audio codecs are supported.");
293 if (!track->SetCodecPrivate(info->codec_config().data(),
294 info->codec_config().size())) {
295 return Status(error::INTERNAL_ERROR,
296 "Private codec data required for audio streams");
299 track->set_uid(info->track_id());
300 if (!info->language().empty())
301 track->set_language(info->language().c_str());
302 track->set_type(mkvmuxer::Tracks::kAudio);
303 track->set_sample_rate(info->sampling_frequency());
304 track->set_channels(info->num_channels());
305 track->set_seek_pre_roll(info->seek_preroll_ns());
306 track->set_codec_delay(info->codec_delay_ns());
310 Status Segmenter::WriteFrame(
bool write_duration) {
314 mkvmuxer::Frame frame;
316 if (!frame.Init(prev_sample_->data(), prev_sample_->data_size())) {
317 return Status(error::MUXER_FAILURE,
318 "Error adding sample to segment: Frame::Init failed");
321 if (write_duration) {
322 const uint64_t duration_ns =
323 prev_sample_->duration() * kSecondsToNs / info_->time_scale();
324 frame.set_duration(duration_ns);
326 frame.set_is_key(prev_sample_->is_key_frame());
327 frame.set_timestamp(prev_sample_->pts() * kSecondsToNs / info_->time_scale());
328 frame.set_track_number(track_id_);
330 if (prev_sample_->side_data_size() > 0) {
331 uint64_t block_add_id;
334 CHECK_GT(prev_sample_->side_data_size(),
sizeof(block_add_id));
335 memcpy(&block_add_id, prev_sample_->side_data(),
sizeof(block_add_id));
336 if (!frame.AddAdditionalData(
337 prev_sample_->side_data() +
sizeof(block_add_id),
338 prev_sample_->side_data_size() -
sizeof(block_add_id),
341 error::MUXER_FAILURE,
342 "Error adding sample to segment: Frame::AddAditionalData Failed");
346 if (!prev_sample_->is_key_frame() && !frame.CanBeSimpleBlock()) {
347 const int64_t timestamp_ns =
348 reference_frame_timestamp_ * kSecondsToNs / info_->time_scale();
349 frame.set_reference_block_timestamp(timestamp_ns);
354 if (cluster_->GetRelativeTimecode(frame.timestamp() /
355 cluster_->timecode_scale()) < 0) {
356 const double segment_duration =
357 static_cast<double>(frame.timestamp()) / kSecondsToNs;
358 LOG(ERROR) <<
"Error adding sample to segment: segment too large, "
359 << segment_duration <<
" seconds.";
360 return Status(error::MUXER_FAILURE,
361 "Error adding sample to segment: segment too large");
364 if (!cluster_->AddFrame(&frame)) {
365 return Status(error::MUXER_FAILURE,
366 "Error adding sample to segment: Cluster::AddFrame failed");
372 reference_frame_timestamp_ = prev_sample_->pts();