| 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, |
| 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), 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), |
| 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); |
| 250 // Alpha channel, if present, is stored in a BlockAdditional next to the |
| 251 // associated opaque Block, see |
| 252 // https://matroska.org/technical/specs/index.html#BlockAdditional. |
| 253 // This follows Method 1 for VP8 encoding of A-channel described on |
| 254 // http://wiki.webmproject.org/alpha-channel. |
| 255 video_track->set_max_block_additional_id(1); |
| 256 |
| 243 // Segment's timestamps should be in milliseconds, DCHECK it. See | 257 // Segment's timestamps should be in milliseconds, DCHECK it. See |
| 244 // http://www.webmproject.org/docs/container/#muxer-guidelines | 258 // http://www.webmproject.org/docs/container/#muxer-guidelines |
| 245 DCHECK_EQ(1000000ull, segment_.GetSegmentInfo()->timecode_scale()); | 259 DCHECK_EQ(1000000ull, segment_.GetSegmentInfo()->timecode_scale()); |
| 246 } | 260 } |
| 247 | 261 |
| 248 void WebmMuxer::AddAudioTrack(const media::AudioParameters& params) { | 262 void WebmMuxer::AddAudioTrack(const media::AudioParameters& params) { |
| 249 DVLOG(1) << __func__ << " " << params.AsHumanReadableString(); | 263 DVLOG(1) << __func__ << " " << params.AsHumanReadableString(); |
| 250 DCHECK(thread_checker_.CalledOnValidThread()); | 264 DCHECK(thread_checker_.CalledOnValidThread()); |
| 251 DCHECK_EQ(0u, audio_track_index_) | 265 DCHECK_EQ(0u, audio_track_index_) |
| 252 << "WebmMuxer audio can only be initialised once."; | 266 << "WebmMuxer audio can only be initialised once."; |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 301 } | 315 } |
| 302 | 316 |
| 303 void WebmMuxer::ElementStartNotify(mkvmuxer::uint64 element_id, | 317 void WebmMuxer::ElementStartNotify(mkvmuxer::uint64 element_id, |
| 304 mkvmuxer::int64 position) { | 318 mkvmuxer::int64 position) { |
| 305 // This method gets pinged before items are sent to |write_data_callback_|. | 319 // This method gets pinged before items are sent to |write_data_callback_|. |
| 306 DCHECK_GE(position, position_.ValueOrDefault(0)) | 320 DCHECK_GE(position, position_.ValueOrDefault(0)) |
| 307 << "Can't go back in a live WebM stream."; | 321 << "Can't go back in a live WebM stream."; |
| 308 } | 322 } |
| 309 | 323 |
| 310 bool WebmMuxer::AddFrame(std::unique_ptr<std::string> encoded_data, | 324 bool WebmMuxer::AddFrame(std::unique_ptr<std::string> encoded_data, |
| 325 std::unique_ptr<std::string> encoded_alpha, |
| 311 uint8_t track_index, | 326 uint8_t track_index, |
| 312 base::TimeDelta timestamp, | 327 base::TimeDelta timestamp, |
| 313 bool is_key_frame) { | 328 bool is_key_frame) { |
| 314 DCHECK(thread_checker_.CalledOnValidThread()); | 329 DCHECK(thread_checker_.CalledOnValidThread()); |
| 315 DCHECK(!has_video_ || video_track_index_); | 330 DCHECK(!has_video_ || video_track_index_); |
| 316 DCHECK(!has_audio_ || audio_track_index_); | 331 DCHECK(!has_audio_ || audio_track_index_); |
| 317 | 332 |
| 318 most_recent_timestamp_ = | 333 most_recent_timestamp_ = |
| 319 std::max(most_recent_timestamp_, timestamp - total_time_in_pause_); | 334 std::max(most_recent_timestamp_, timestamp - total_time_in_pause_); |
| 320 | 335 |
| 321 if (force_one_libwebm_error_) { | 336 if (force_one_libwebm_error_) { |
| 322 DVLOG(1) << "Forcing a libwebm error"; | 337 DVLOG(1) << "Forcing a libwebm error"; |
| 323 force_one_libwebm_error_ = false; | 338 force_one_libwebm_error_ = false; |
| 324 return false; | 339 return false; |
| 325 } | 340 } |
| 326 | 341 |
| 327 DCHECK(encoded_data->data()); | 342 DCHECK(encoded_data->data()); |
| 328 return segment_.AddFrame( | 343 if (!encoded_alpha || encoded_alpha->empty()) { |
| 344 return segment_.AddFrame( |
| 345 reinterpret_cast<const uint8_t*>(encoded_data->data()), |
| 346 encoded_data->size(), track_index, |
| 347 most_recent_timestamp_.InMicroseconds() * |
| 348 base::Time::kNanosecondsPerMicrosecond, |
| 349 is_key_frame); |
| 350 } |
| 351 |
| 352 DCHECK(encoded_alpha->data()); |
| 353 return segment_.AddFrameWithAdditional( |
| 329 reinterpret_cast<const uint8_t*>(encoded_data->data()), | 354 reinterpret_cast<const uint8_t*>(encoded_data->data()), |
| 330 encoded_data->size(), track_index, | 355 encoded_data->size(), |
| 356 reinterpret_cast<const uint8_t*>(encoded_alpha->data()), |
| 357 encoded_alpha->size(), 1 /* add_id */, track_index, |
| 331 most_recent_timestamp_.InMicroseconds() * | 358 most_recent_timestamp_.InMicroseconds() * |
| 332 base::Time::kNanosecondsPerMicrosecond, | 359 base::Time::kNanosecondsPerMicrosecond, |
| 333 is_key_frame); | 360 is_key_frame); |
| 334 } | 361 } |
| 335 | 362 |
| 336 WebmMuxer::EncodedVideoFrame::EncodedVideoFrame( | 363 WebmMuxer::EncodedVideoFrame::EncodedVideoFrame( |
| 337 std::unique_ptr<std::string> data, | 364 std::unique_ptr<std::string> data, |
| 365 std::unique_ptr<std::string> alpha_data, |
| 338 base::TimeTicks timestamp, | 366 base::TimeTicks timestamp, |
| 339 bool is_keyframe) | 367 bool is_keyframe) |
| 340 : data(std::move(data)), timestamp(timestamp), is_keyframe(is_keyframe) {} | 368 : data(std::move(data)), |
| 369 alpha_data(std::move(alpha_data)), |
| 370 timestamp(timestamp), |
| 371 is_keyframe(is_keyframe) {} |
| 341 | 372 |
| 342 WebmMuxer::EncodedVideoFrame::~EncodedVideoFrame() {} | 373 WebmMuxer::EncodedVideoFrame::~EncodedVideoFrame() {} |
| 343 | 374 |
| 344 } // namespace media | 375 } // namespace media |
| OLD | NEW |