Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/muxers/webm_muxer.h" | 5 #include "media/muxers/webm_muxer.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/memory/ptr_util.h" | 10 #include "base/memory/ptr_util.h" |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 127 | 127 |
| 128 WebmMuxer::~WebmMuxer() { | 128 WebmMuxer::~WebmMuxer() { |
| 129 // No need to segment_.Finalize() since is not Seekable(), i.e. a live | 129 // No need to segment_.Finalize() since is not Seekable(), i.e. a live |
| 130 // stream, but is a good practice. | 130 // stream, but is a good practice. |
| 131 DCHECK(thread_checker_.CalledOnValidThread()); | 131 DCHECK(thread_checker_.CalledOnValidThread()); |
| 132 segment_.Finalize(); | 132 segment_.Finalize(); |
| 133 } | 133 } |
| 134 | 134 |
| 135 bool WebmMuxer::OnEncodedVideo(const VideoParameters& params, | 135 bool WebmMuxer::OnEncodedVideo(const VideoParameters& params, |
| 136 std::unique_ptr<std::string> encoded_data, | 136 std::unique_ptr<std::string> encoded_data, |
| 137 std::unique_ptr<std::string> encoded_alpha_data, | |
| 137 base::TimeTicks timestamp, | 138 base::TimeTicks timestamp, |
| 138 bool is_key_frame) { | 139 bool is_key_frame) { |
| 139 DVLOG(1) << __func__ << " - " << encoded_data->size() << "B"; | 140 DVLOG(1) << __func__ << " - " << encoded_data->size() << "B"; |
| 140 DCHECK(thread_checker_.CalledOnValidThread()); | 141 DCHECK(thread_checker_.CalledOnValidThread()); |
| 141 | 142 |
| 142 if (!video_track_index_) { | 143 if (!video_track_index_) { |
| 143 // |track_index_|, cannot be zero (!), initialize WebmMuxer in that case. | 144 // |track_index_|, cannot be zero (!), initialize WebmMuxer in that case. |
| 144 // http://www.matroska.org/technical/specs/index.html#Tracks | 145 // http://www.matroska.org/technical/specs/index.html#Tracks |
| 145 AddVideoTrack(params.visible_rect_size, GetFrameRate(params)); | 146 AddVideoTrack(params.visible_rect_size, GetFrameRate(params)); |
| 146 if (first_frame_timestamp_video_.is_null()) | 147 if (first_frame_timestamp_video_.is_null()) |
| 147 first_frame_timestamp_video_ = timestamp; | 148 first_frame_timestamp_video_ = timestamp; |
| 148 } | 149 } |
| 149 | 150 |
| 150 // TODO(ajose): Support multiple tracks: http://crbug.com/528523 | 151 // TODO(ajose): Support multiple tracks: http://crbug.com/528523 |
| 151 if (has_audio_ && !audio_track_index_) { | 152 if (has_audio_ && !audio_track_index_) { |
| 152 DVLOG(1) << __func__ << ": delaying until audio track ready."; | 153 DVLOG(1) << __func__ << ": delaying until audio track ready."; |
| 153 if (is_key_frame) // Upon Key frame reception, empty the encoded queue. | 154 if (is_key_frame) // Upon Key frame reception, empty the encoded queue. |
| 154 encoded_frames_queue_.clear(); | 155 encoded_frames_queue_.clear(); |
| 155 | 156 |
| 156 encoded_frames_queue_.push_back(base::MakeUnique<EncodedVideoFrame>( | 157 encoded_frames_queue_.push_back(base::MakeUnique<EncodedVideoFrame>( |
| 157 std::move(encoded_data), timestamp, is_key_frame)); | 158 std::move(encoded_data), std::move(encoded_alpha_data), timestamp, |
| 159 is_key_frame)); | |
| 158 return true; | 160 return true; |
| 159 } | 161 } |
| 160 | 162 |
| 161 // Any saved encoded video frames must have been dumped in OnEncodedAudio(); | 163 // Any saved encoded video frames must have been dumped in OnEncodedAudio(); |
| 162 DCHECK(encoded_frames_queue_.empty()); | 164 DCHECK(encoded_frames_queue_.empty()); |
| 163 | 165 |
| 164 return AddFrame(std::move(encoded_data), video_track_index_, | 166 return AddFrame(std::move(encoded_data), std::move(encoded_alpha_data), |
| 165 timestamp - first_frame_timestamp_video_, is_key_frame); | 167 video_track_index_, timestamp - first_frame_timestamp_video_, |
| 168 is_key_frame); | |
| 166 } | 169 } |
| 167 | 170 |
| 168 bool WebmMuxer::OnEncodedAudio(const media::AudioParameters& params, | 171 bool WebmMuxer::OnEncodedAudio(const media::AudioParameters& params, |
| 169 std::unique_ptr<std::string> encoded_data, | 172 std::unique_ptr<std::string> encoded_data, |
| 170 base::TimeTicks timestamp) { | 173 base::TimeTicks timestamp) { |
| 171 DVLOG(2) << __func__ << " - " << encoded_data->size() << "B"; | 174 DVLOG(2) << __func__ << " - " << encoded_data->size() << "B"; |
| 172 DCHECK(thread_checker_.CalledOnValidThread()); | 175 DCHECK(thread_checker_.CalledOnValidThread()); |
| 173 | 176 |
| 174 if (!audio_track_index_) { | 177 if (!audio_track_index_) { |
| 175 AddAudioTrack(params); | 178 AddAudioTrack(params); |
| 176 if (first_frame_timestamp_audio_.is_null()) | 179 if (first_frame_timestamp_audio_.is_null()) |
| 177 first_frame_timestamp_audio_ = timestamp; | 180 first_frame_timestamp_audio_ = timestamp; |
| 178 } | 181 } |
| 179 | 182 |
| 180 // TODO(ajose): Don't drop audio data: http://crbug.com/547948 | 183 // TODO(ajose): Don't drop audio data: http://crbug.com/547948 |
| 181 // TODO(ajose): Support multiple tracks: http://crbug.com/528523 | 184 // TODO(ajose): Support multiple tracks: http://crbug.com/528523 |
| 182 if (has_video_ && !video_track_index_) { | 185 if (has_video_ && !video_track_index_) { |
| 183 DVLOG(1) << __func__ << ": delaying until video track ready."; | 186 DVLOG(1) << __func__ << ": delaying until video track ready."; |
| 184 return true; | 187 return true; |
| 185 } | 188 } |
| 186 | 189 |
| 187 // Dump all saved encoded video frames if any. | 190 // Dump all saved encoded video frames if any. |
| 188 while (!encoded_frames_queue_.empty()) { | 191 while (!encoded_frames_queue_.empty()) { |
| 189 const bool res = AddFrame( | 192 const bool res = AddFrame( |
| 190 base::MakeUnique<std::string>(*encoded_frames_queue_.front()->data), | 193 base::MakeUnique<std::string>(*encoded_frames_queue_.front()->data), |
| 194 encoded_frames_queue_.front()->alpha_data | |
| 195 ? base::MakeUnique<std::string>( | |
| 196 *encoded_frames_queue_.front()->alpha_data) | |
| 197 : nullptr, | |
| 191 video_track_index_, | 198 video_track_index_, |
| 192 encoded_frames_queue_.front()->timestamp - first_frame_timestamp_video_, | 199 encoded_frames_queue_.front()->timestamp - first_frame_timestamp_video_, |
| 193 encoded_frames_queue_.front()->is_keyframe); | 200 encoded_frames_queue_.front()->is_keyframe); |
| 194 if (!res) | 201 if (!res) |
| 195 return false; | 202 return false; |
| 196 encoded_frames_queue_.pop_front(); | 203 encoded_frames_queue_.pop_front(); |
| 197 } | 204 } |
| 198 | 205 return AddFrame(std::move(encoded_data), nullptr, audio_track_index_, |
| 199 return AddFrame(std::move(encoded_data), audio_track_index_, | |
| 200 timestamp - first_frame_timestamp_audio_, | 206 timestamp - first_frame_timestamp_audio_, |
| 201 true /* is_key_frame -- always true for audio */); | 207 true /* is_key_frame -- always true for audio */); |
| 202 } | 208 } |
| 203 | 209 |
| 204 void WebmMuxer::Pause() { | 210 void WebmMuxer::Pause() { |
| 205 DVLOG(1) << __func__; | 211 DVLOG(1) << __func__; |
| 206 DCHECK(thread_checker_.CalledOnValidThread()); | 212 DCHECK(thread_checker_.CalledOnValidThread()); |
| 207 if (!elapsed_time_in_pause_) | 213 if (!elapsed_time_in_pause_) |
| 208 elapsed_time_in_pause_.reset(new base::ElapsedTimer()); | 214 elapsed_time_in_pause_.reset(new base::ElapsedTimer()); |
| 209 } | 215 } |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 233 reinterpret_cast<mkvmuxer::VideoTrack*>( | 239 reinterpret_cast<mkvmuxer::VideoTrack*>( |
| 234 segment_.GetTrackByNumber(video_track_index_)); | 240 segment_.GetTrackByNumber(video_track_index_)); |
| 235 DCHECK(video_track); | 241 DCHECK(video_track); |
| 236 video_track->set_codec_id(MkvCodeIcForMediaVideoCodecId(video_codec_)); | 242 video_track->set_codec_id(MkvCodeIcForMediaVideoCodecId(video_codec_)); |
| 237 DCHECK_EQ(0ull, video_track->crop_right()); | 243 DCHECK_EQ(0ull, video_track->crop_right()); |
| 238 DCHECK_EQ(0ull, video_track->crop_left()); | 244 DCHECK_EQ(0ull, video_track->crop_left()); |
| 239 DCHECK_EQ(0ull, video_track->crop_top()); | 245 DCHECK_EQ(0ull, video_track->crop_top()); |
| 240 DCHECK_EQ(0ull, video_track->crop_bottom()); | 246 DCHECK_EQ(0ull, video_track->crop_bottom()); |
| 241 DCHECK_EQ(0.0f, video_track->frame_rate()); | 247 DCHECK_EQ(0.0f, video_track->frame_rate()); |
| 242 | 248 |
| 249 video_track->SetAlphaMode(mkvmuxer::VideoTrack::kAlpha); | |
|
mcasas
2017/03/08 22:38:48
Do we have to do it always, regardless of
alpha be
emircan
2017/03/09 01:45:41
According to my offline discussion with vignesh@,
| |
| 250 video_track->set_max_block_additional_id(1); | |
|
mcasas
2017/03/08 22:38:48
We need a comment as to why we make this additiona
emircan
2017/03/09 01:45:41
Done.
| |
| 251 | |
| 243 // Segment's timestamps should be in milliseconds, DCHECK it. See | 252 // Segment's timestamps should be in milliseconds, DCHECK it. See |
| 244 // http://www.webmproject.org/docs/container/#muxer-guidelines | 253 // http://www.webmproject.org/docs/container/#muxer-guidelines |
| 245 DCHECK_EQ(1000000ull, segment_.GetSegmentInfo()->timecode_scale()); | 254 DCHECK_EQ(1000000ull, segment_.GetSegmentInfo()->timecode_scale()); |
| 246 } | 255 } |
| 247 | 256 |
| 248 void WebmMuxer::AddAudioTrack(const media::AudioParameters& params) { | 257 void WebmMuxer::AddAudioTrack(const media::AudioParameters& params) { |
| 249 DVLOG(1) << __func__ << " " << params.AsHumanReadableString(); | 258 DVLOG(1) << __func__ << " " << params.AsHumanReadableString(); |
| 250 DCHECK(thread_checker_.CalledOnValidThread()); | 259 DCHECK(thread_checker_.CalledOnValidThread()); |
| 251 DCHECK_EQ(0u, audio_track_index_) | 260 DCHECK_EQ(0u, audio_track_index_) |
| 252 << "WebmMuxer audio can only be initialised once."; | 261 << "WebmMuxer audio can only be initialised once."; |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 301 } | 310 } |
| 302 | 311 |
| 303 void WebmMuxer::ElementStartNotify(mkvmuxer::uint64 element_id, | 312 void WebmMuxer::ElementStartNotify(mkvmuxer::uint64 element_id, |
| 304 mkvmuxer::int64 position) { | 313 mkvmuxer::int64 position) { |
| 305 // This method gets pinged before items are sent to |write_data_callback_|. | 314 // This method gets pinged before items are sent to |write_data_callback_|. |
| 306 DCHECK_GE(position, position_.ValueOrDefault(0)) | 315 DCHECK_GE(position, position_.ValueOrDefault(0)) |
| 307 << "Can't go back in a live WebM stream."; | 316 << "Can't go back in a live WebM stream."; |
| 308 } | 317 } |
| 309 | 318 |
| 310 bool WebmMuxer::AddFrame(std::unique_ptr<std::string> encoded_data, | 319 bool WebmMuxer::AddFrame(std::unique_ptr<std::string> encoded_data, |
| 320 std::unique_ptr<std::string> encoded_alpha_data, | |
| 311 uint8_t track_index, | 321 uint8_t track_index, |
| 312 base::TimeDelta timestamp, | 322 base::TimeDelta timestamp, |
| 313 bool is_key_frame) { | 323 bool is_key_frame) { |
| 314 DCHECK(thread_checker_.CalledOnValidThread()); | 324 DCHECK(thread_checker_.CalledOnValidThread()); |
| 315 DCHECK(!has_video_ || video_track_index_); | 325 DCHECK(!has_video_ || video_track_index_); |
| 316 DCHECK(!has_audio_ || audio_track_index_); | 326 DCHECK(!has_audio_ || audio_track_index_); |
| 317 | 327 |
| 318 most_recent_timestamp_ = | 328 most_recent_timestamp_ = |
| 319 std::max(most_recent_timestamp_, timestamp - total_time_in_pause_); | 329 std::max(most_recent_timestamp_, timestamp - total_time_in_pause_); |
| 320 | 330 |
| 321 if (force_one_libwebm_error_) { | 331 if (force_one_libwebm_error_) { |
| 322 DVLOG(1) << "Forcing a libwebm error"; | 332 DVLOG(1) << "Forcing a libwebm error"; |
| 323 force_one_libwebm_error_ = false; | 333 force_one_libwebm_error_ = false; |
| 324 return false; | 334 return false; |
| 325 } | 335 } |
| 326 | 336 |
| 327 DCHECK(encoded_data->data()); | 337 DCHECK(encoded_data->data()); |
| 328 return segment_.AddFrame( | 338 if (!encoded_alpha_data || encoded_alpha_data->empty()) { |
|
vignesh
2017/03/08 17:26:14
Just checking: Have you verified if this combinati
emircan
2017/03/08 23:53:58
It tested a alpha-opaque-alpha canvas recording on
vignesh
2017/03/09 07:13:46
MSE is Media Source Extensions [1]. It's how most
emircan
2017/03/09 19:18:53
Sure. The way we are playing back Blob in the demo
| |
| 339 return segment_.AddFrame( | |
| 340 reinterpret_cast<const uint8_t*>(encoded_data->data()), | |
| 341 encoded_data->size(), track_index, | |
| 342 most_recent_timestamp_.InMicroseconds() * | |
| 343 base::Time::kNanosecondsPerMicrosecond, | |
| 344 is_key_frame); | |
| 345 } | |
| 346 | |
| 347 DCHECK(encoded_alpha_data->data()); | |
| 348 return segment_.AddFrameWithAdditional( | |
| 329 reinterpret_cast<const uint8_t*>(encoded_data->data()), | 349 reinterpret_cast<const uint8_t*>(encoded_data->data()), |
| 330 encoded_data->size(), track_index, | 350 encoded_data->size(), |
| 351 reinterpret_cast<const uint8_t*>(encoded_alpha_data->data()), | |
| 352 encoded_alpha_data->size(), 1, track_index, | |
|
mcasas
2017/03/08 22:38:48
What's this "1"? It's
/* add_id */
emircan
2017/03/09 01:45:40
Added the comment. I just followed this from the w
| |
| 331 most_recent_timestamp_.InMicroseconds() * | 353 most_recent_timestamp_.InMicroseconds() * |
| 332 base::Time::kNanosecondsPerMicrosecond, | 354 base::Time::kNanosecondsPerMicrosecond, |
| 333 is_key_frame); | 355 is_key_frame); |
| 334 } | 356 } |
| 335 | 357 |
| 336 WebmMuxer::EncodedVideoFrame::EncodedVideoFrame( | 358 WebmMuxer::EncodedVideoFrame::EncodedVideoFrame( |
| 337 std::unique_ptr<std::string> data, | 359 std::unique_ptr<std::string> data, |
| 360 std::unique_ptr<std::string> alpha_data, | |
| 338 base::TimeTicks timestamp, | 361 base::TimeTicks timestamp, |
| 339 bool is_keyframe) | 362 bool is_keyframe) |
| 340 : data(std::move(data)), timestamp(timestamp), is_keyframe(is_keyframe) {} | 363 : data(std::move(data)), |
| 364 alpha_data(std::move(alpha_data)), | |
| 365 timestamp(timestamp), | |
| 366 is_keyframe(is_keyframe) {} | |
| 341 | 367 |
| 342 WebmMuxer::EncodedVideoFrame::~EncodedVideoFrame() {} | 368 WebmMuxer::EncodedVideoFrame::~EncodedVideoFrame() {} |
| 343 | 369 |
| 344 } // namespace media | 370 } // namespace media |
| OLD | NEW |