| 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 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 99 WebmMuxer::WebmMuxer(VideoCodec codec, | 99 WebmMuxer::WebmMuxer(VideoCodec codec, |
| 100 bool has_video, | 100 bool has_video, |
| 101 bool has_audio, | 101 bool has_audio, |
| 102 const WriteDataCB& write_data_callback) | 102 const WriteDataCB& write_data_callback) |
| 103 : video_codec_(codec), | 103 : video_codec_(codec), |
| 104 video_track_index_(0), | 104 video_track_index_(0), |
| 105 audio_track_index_(0), | 105 audio_track_index_(0), |
| 106 has_video_(has_video), | 106 has_video_(has_video), |
| 107 has_audio_(has_audio), | 107 has_audio_(has_audio), |
| 108 write_data_callback_(write_data_callback), | 108 write_data_callback_(write_data_callback), |
| 109 position_(0) { | 109 position_(0), |
| 110 force_one_libwebm_error_(false) { |
| 110 DCHECK(has_video_ || has_audio_); | 111 DCHECK(has_video_ || has_audio_); |
| 111 DCHECK(!write_data_callback_.is_null()); | 112 DCHECK(!write_data_callback_.is_null()); |
| 112 DCHECK(codec == kCodecVP8 || codec == kCodecVP9 || codec == kCodecH264) | 113 DCHECK(codec == kCodecVP8 || codec == kCodecVP9 || codec == kCodecH264) |
| 113 << " Unsupported codec: " << GetCodecName(codec); | 114 << " Unsupported codec: " << GetCodecName(codec); |
| 114 | 115 |
| 115 segment_.Init(this); | 116 segment_.Init(this); |
| 116 segment_.set_mode(mkvmuxer::Segment::kLive); | 117 segment_.set_mode(mkvmuxer::Segment::kLive); |
| 117 segment_.OutputCues(false); | 118 segment_.OutputCues(false); |
| 118 | 119 |
| 119 mkvmuxer::SegmentInfo* const info = segment_.GetSegmentInfo(); | 120 mkvmuxer::SegmentInfo* const info = segment_.GetSegmentInfo(); |
| 120 info->set_writing_app("Chrome"); | 121 info->set_writing_app("Chrome"); |
| 121 info->set_muxing_app("Chrome"); | 122 info->set_muxing_app("Chrome"); |
| 122 | 123 |
| 123 // Creation is done on a different thread than main activities. | 124 // Creation is done on a different thread than main activities. |
| 124 thread_checker_.DetachFromThread(); | 125 thread_checker_.DetachFromThread(); |
| 125 } | 126 } |
| 126 | 127 |
| 127 WebmMuxer::~WebmMuxer() { | 128 WebmMuxer::~WebmMuxer() { |
| 128 // 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 |
| 129 // stream, but is a good practice. | 130 // stream, but is a good practice. |
| 130 DCHECK(thread_checker_.CalledOnValidThread()); | 131 DCHECK(thread_checker_.CalledOnValidThread()); |
| 131 segment_.Finalize(); | 132 segment_.Finalize(); |
| 132 } | 133 } |
| 133 | 134 |
| 134 void WebmMuxer::OnEncodedVideo(const VideoParameters& params, | 135 bool WebmMuxer::OnEncodedVideo(const VideoParameters& params, |
| 135 std::unique_ptr<std::string> encoded_data, | 136 std::unique_ptr<std::string> encoded_data, |
| 136 base::TimeTicks timestamp, | 137 base::TimeTicks timestamp, |
| 137 bool is_key_frame) { | 138 bool is_key_frame) { |
| 138 DVLOG(1) << __func__ << " - " << encoded_data->size() << "B"; | 139 DVLOG(1) << __func__ << " - " << encoded_data->size() << "B"; |
| 139 DCHECK(thread_checker_.CalledOnValidThread()); | 140 DCHECK(thread_checker_.CalledOnValidThread()); |
| 140 | 141 |
| 141 if (!video_track_index_) { | 142 if (!video_track_index_) { |
| 142 // |track_index_|, cannot be zero (!), initialize WebmMuxer in that case. | 143 // |track_index_|, cannot be zero (!), initialize WebmMuxer in that case. |
| 143 // http://www.matroska.org/technical/specs/index.html#Tracks | 144 // http://www.matroska.org/technical/specs/index.html#Tracks |
| 144 AddVideoTrack(params.visible_rect_size, GetFrameRate(params)); | 145 AddVideoTrack(params.visible_rect_size, GetFrameRate(params)); |
| 145 if (first_frame_timestamp_video_.is_null()) | 146 if (first_frame_timestamp_video_.is_null()) |
| 146 first_frame_timestamp_video_ = timestamp; | 147 first_frame_timestamp_video_ = timestamp; |
| 147 } | 148 } |
| 148 | 149 |
| 149 // TODO(ajose): Support multiple tracks: http://crbug.com/528523 | 150 // TODO(ajose): Support multiple tracks: http://crbug.com/528523 |
| 150 if (has_audio_ && !audio_track_index_) { | 151 if (has_audio_ && !audio_track_index_) { |
| 151 DVLOG(1) << __func__ << ": delaying until audio track ready."; | 152 DVLOG(1) << __func__ << ": delaying until audio track ready."; |
| 152 if (is_key_frame) // Upon Key frame reception, empty the encoded queue. | 153 if (is_key_frame) // Upon Key frame reception, empty the encoded queue. |
| 153 encoded_frames_queue_.clear(); | 154 encoded_frames_queue_.clear(); |
| 154 | 155 |
| 155 encoded_frames_queue_.push_back(base::MakeUnique<EncodedVideoFrame>( | 156 encoded_frames_queue_.push_back(base::MakeUnique<EncodedVideoFrame>( |
| 156 std::move(encoded_data), timestamp, is_key_frame)); | 157 std::move(encoded_data), timestamp, is_key_frame)); |
| 157 return; | 158 return true; |
| 158 } | 159 } |
| 159 | 160 |
| 160 // Dump all saved encoded video frames if any. | 161 // Any saved encoded video frames must have been dumped in OnEncodedAudio(); |
| 161 while (!encoded_frames_queue_.empty()) { | 162 DCHECK(encoded_frames_queue_.empty()); |
| 162 AddFrame( | |
| 163 std::move(encoded_frames_queue_.front()->data), video_track_index_, | |
| 164 encoded_frames_queue_.front()->timestamp - first_frame_timestamp_video_, | |
| 165 encoded_frames_queue_.front()->is_keyframe); | |
| 166 encoded_frames_queue_.pop_front(); | |
| 167 } | |
| 168 | 163 |
| 169 AddFrame(std::move(encoded_data), video_track_index_, | 164 return AddFrame(std::move(encoded_data), video_track_index_, |
| 170 timestamp - first_frame_timestamp_video_, is_key_frame); | 165 timestamp - first_frame_timestamp_video_, is_key_frame); |
| 171 } | 166 } |
| 172 | 167 |
| 173 void WebmMuxer::OnEncodedAudio(const media::AudioParameters& params, | 168 bool WebmMuxer::OnEncodedAudio(const media::AudioParameters& params, |
| 174 std::unique_ptr<std::string> encoded_data, | 169 std::unique_ptr<std::string> encoded_data, |
| 175 base::TimeTicks timestamp) { | 170 base::TimeTicks timestamp) { |
| 176 DVLOG(2) << __func__ << " - " << encoded_data->size() << "B"; | 171 DVLOG(2) << __func__ << " - " << encoded_data->size() << "B"; |
| 177 DCHECK(thread_checker_.CalledOnValidThread()); | 172 DCHECK(thread_checker_.CalledOnValidThread()); |
| 178 | 173 |
| 179 if (!audio_track_index_) { | 174 if (!audio_track_index_) { |
| 180 AddAudioTrack(params); | 175 AddAudioTrack(params); |
| 181 if (first_frame_timestamp_audio_.is_null()) | 176 if (first_frame_timestamp_audio_.is_null()) |
| 182 first_frame_timestamp_audio_ = timestamp; | 177 first_frame_timestamp_audio_ = timestamp; |
| 183 } | 178 } |
| 184 | 179 |
| 185 // TODO(ajose): Don't drop audio data: http://crbug.com/547948 | 180 // TODO(ajose): Don't drop audio data: http://crbug.com/547948 |
| 186 // TODO(ajose): Support multiple tracks: http://crbug.com/528523 | 181 // TODO(ajose): Support multiple tracks: http://crbug.com/528523 |
| 187 if (has_video_ && !video_track_index_) { | 182 if (has_video_ && !video_track_index_) { |
| 188 DVLOG(1) << __func__ << ": delaying until video track ready."; | 183 DVLOG(1) << __func__ << ": delaying until video track ready."; |
| 189 return; | 184 return true; |
| 190 } | 185 } |
| 191 | 186 |
| 192 // Dump all saved encoded video frames if any. | 187 // Dump all saved encoded video frames if any. |
| 193 while (!encoded_frames_queue_.empty()) { | 188 while (!encoded_frames_queue_.empty()) { |
| 194 AddFrame( | 189 const bool res = AddFrame( |
| 195 std::move(encoded_frames_queue_.front()->data), video_track_index_, | 190 base::MakeUnique<std::string>(*encoded_frames_queue_.front()->data), |
| 191 video_track_index_, |
| 196 encoded_frames_queue_.front()->timestamp - first_frame_timestamp_video_, | 192 encoded_frames_queue_.front()->timestamp - first_frame_timestamp_video_, |
| 197 encoded_frames_queue_.front()->is_keyframe); | 193 encoded_frames_queue_.front()->is_keyframe); |
| 194 if (!res) |
| 195 return false; |
| 198 encoded_frames_queue_.pop_front(); | 196 encoded_frames_queue_.pop_front(); |
| 199 } | 197 } |
| 200 | 198 |
| 201 AddFrame(std::move(encoded_data), audio_track_index_, | 199 return AddFrame(std::move(encoded_data), audio_track_index_, |
| 202 timestamp - first_frame_timestamp_audio_, | 200 timestamp - first_frame_timestamp_audio_, |
| 203 true /* is_key_frame -- always true for audio */); | 201 true /* is_key_frame -- always true for audio */); |
| 204 } | 202 } |
| 205 | 203 |
| 206 void WebmMuxer::Pause() { | 204 void WebmMuxer::Pause() { |
| 207 DVLOG(1) << __func__; | 205 DVLOG(1) << __func__; |
| 208 DCHECK(thread_checker_.CalledOnValidThread()); | 206 DCHECK(thread_checker_.CalledOnValidThread()); |
| 209 if (!elapsed_time_in_pause_) | 207 if (!elapsed_time_in_pause_) |
| 210 elapsed_time_in_pause_.reset(new base::ElapsedTimer()); | 208 elapsed_time_in_pause_.reset(new base::ElapsedTimer()); |
| 211 } | 209 } |
| 212 | 210 |
| 213 void WebmMuxer::Resume() { | 211 void WebmMuxer::Resume() { |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 302 return false; | 300 return false; |
| 303 } | 301 } |
| 304 | 302 |
| 305 void WebmMuxer::ElementStartNotify(mkvmuxer::uint64 element_id, | 303 void WebmMuxer::ElementStartNotify(mkvmuxer::uint64 element_id, |
| 306 mkvmuxer::int64 position) { | 304 mkvmuxer::int64 position) { |
| 307 // This method gets pinged before items are sent to |write_data_callback_|. | 305 // This method gets pinged before items are sent to |write_data_callback_|. |
| 308 DCHECK_GE(position, position_.ValueOrDefault(0)) | 306 DCHECK_GE(position, position_.ValueOrDefault(0)) |
| 309 << "Can't go back in a live WebM stream."; | 307 << "Can't go back in a live WebM stream."; |
| 310 } | 308 } |
| 311 | 309 |
| 312 void WebmMuxer::AddFrame(std::unique_ptr<std::string> encoded_data, | 310 bool WebmMuxer::AddFrame(std::unique_ptr<std::string> encoded_data, |
| 313 uint8_t track_index, | 311 uint8_t track_index, |
| 314 base::TimeDelta timestamp, | 312 base::TimeDelta timestamp, |
| 315 bool is_key_frame) { | 313 bool is_key_frame) { |
| 316 DCHECK(thread_checker_.CalledOnValidThread()); | 314 DCHECK(thread_checker_.CalledOnValidThread()); |
| 317 DCHECK(!has_video_ || video_track_index_); | 315 DCHECK(!has_video_ || video_track_index_); |
| 318 DCHECK(!has_audio_ || audio_track_index_); | 316 DCHECK(!has_audio_ || audio_track_index_); |
| 319 | 317 |
| 320 most_recent_timestamp_ = | 318 most_recent_timestamp_ = |
| 321 std::max(most_recent_timestamp_, timestamp - total_time_in_pause_); | 319 std::max(most_recent_timestamp_, timestamp - total_time_in_pause_); |
| 322 | 320 |
| 323 segment_.AddFrame(reinterpret_cast<const uint8_t*>(encoded_data->data()), | 321 if (force_one_libwebm_error_) { |
| 324 encoded_data->size(), track_index, | 322 DVLOG(1) << "Forcing a libwebm error"; |
| 325 most_recent_timestamp_.InMicroseconds() * | 323 force_one_libwebm_error_ = false; |
| 326 base::Time::kNanosecondsPerMicrosecond, | 324 return false; |
| 327 is_key_frame); | 325 } |
| 326 |
| 327 DCHECK(encoded_data->data()); |
| 328 return segment_.AddFrame( |
| 329 reinterpret_cast<const uint8_t*>(encoded_data->data()), |
| 330 encoded_data->size(), track_index, |
| 331 most_recent_timestamp_.InMicroseconds() * |
| 332 base::Time::kNanosecondsPerMicrosecond, |
| 333 is_key_frame); |
| 328 } | 334 } |
| 329 | 335 |
| 330 WebmMuxer::EncodedVideoFrame::EncodedVideoFrame( | 336 WebmMuxer::EncodedVideoFrame::EncodedVideoFrame( |
| 331 std::unique_ptr<std::string> data, | 337 std::unique_ptr<std::string> data, |
| 332 base::TimeTicks timestamp, | 338 base::TimeTicks timestamp, |
| 333 bool is_keyframe) | 339 bool is_keyframe) |
| 334 : data(std::move(data)), timestamp(timestamp), is_keyframe(is_keyframe) {} | 340 : data(std::move(data)), timestamp(timestamp), is_keyframe(is_keyframe) {} |
| 335 | 341 |
| 336 WebmMuxer::EncodedVideoFrame::~EncodedVideoFrame() {} | 342 WebmMuxer::EncodedVideoFrame::~EncodedVideoFrame() {} |
| 337 | 343 |
| 338 } // namespace media | 344 } // namespace media |
| OLD | NEW |