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 "content/renderer/media/media_recorder_handler.h" | 5 #include "content/renderer/media/media_recorder_handler.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/location.h" | 8 #include "base/location.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "content/renderer/media/audio_track_recorder.h" | |
| 10 #include "content/renderer/media/video_track_recorder.h" | 11 #include "content/renderer/media/video_track_recorder.h" |
| 11 #include "content/renderer/media/webrtc_uma_histograms.h" | 12 #include "content/renderer/media/webrtc_uma_histograms.h" |
| 13 #include "media/audio/audio_parameters.h" | |
| 14 #include "media/base/audio_bus.h" | |
| 12 #include "media/base/bind_to_current_loop.h" | 15 #include "media/base/bind_to_current_loop.h" |
| 13 #include "media/base/video_frame.h" | 16 #include "media/base/video_frame.h" |
| 14 #include "third_party/WebKit/public/platform/WebMediaRecorderHandlerClient.h" | 17 #include "third_party/WebKit/public/platform/WebMediaRecorderHandlerClient.h" |
| 15 #include "third_party/WebKit/public/platform/WebString.h" | 18 #include "third_party/WebKit/public/platform/WebString.h" |
| 16 | 19 |
| 17 #if !defined(MEDIA_DISABLE_LIBWEBM) | 20 #if !defined(MEDIA_DISABLE_LIBWEBM) |
| 18 #include "media/capture/webm_muxer.h" | 21 #include "media/capture/webm_muxer.h" |
| 19 #endif | 22 #endif |
| 20 | 23 |
| 21 using base::TimeDelta; | 24 using base::TimeDelta; |
| 22 using base::TimeTicks; | 25 using base::TimeTicks; |
| 23 | 26 |
| 24 namespace content { | 27 namespace content { |
| 25 | 28 |
| 26 MediaRecorderHandler::MediaRecorderHandler() | 29 MediaRecorderHandler::MediaRecorderHandler() |
| 27 : use_vp9_(false), | 30 : use_vp9_(false), |
| 28 recording_(false), | 31 recording_(false), |
| 29 client_(nullptr), | 32 client_(nullptr), |
| 30 weak_factory_(this) { | 33 weak_factory_(this) {} |
| 31 DVLOG(3) << __FUNCTION__; | |
| 32 } | |
| 33 | 34 |
| 34 MediaRecorderHandler::~MediaRecorderHandler() { | 35 MediaRecorderHandler::~MediaRecorderHandler() { |
| 35 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 36 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 36 // Send a |last_in_slice| to our |client_|. | 37 // Send a |last_in_slice| to our |client_|. |
| 37 if (client_) | 38 if (client_) |
| 38 client_->writeData(nullptr, 0u, true); | 39 client_->writeData(nullptr, 0u, true); |
| 39 } | 40 } |
| 40 | 41 |
| 41 bool MediaRecorderHandler::canSupportMimeType( | 42 bool MediaRecorderHandler::canSupportMimeType( |
| 42 const blink::WebString& mimeType) { | 43 const blink::WebString& mimeType) { |
| 43 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 44 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 44 // TODO(mcasas): So far only empty or "video/vp{8,9}" are supported. | 45 // TODO(ajose): Not sure about some of these... |
|
mcasas
2015/11/19 00:49:52
See my comment below.
ajose
2015/11/19 22:17:18
Done.
| |
| 45 return mimeType.isEmpty() || mimeType.utf8().compare("video/vp8") == 0 || | 46 return mimeType.isEmpty() || mimeType.utf8().compare("video/vp8") == 0 || |
| 46 mimeType.utf8().compare("video/vp9") == 0; | 47 mimeType.utf8().compare("video/vp9") == 0 || |
| 48 mimeType.utf8().compare("audio/opus") == 0 || | |
| 49 mimeType.utf8().compare("video/opus") == 0 || | |
|
mcasas
2015/11/19 00:49:52
"video/opus" ?
Also, what if MIME was "video/vp8,
ajose
2015/11/19 22:17:18
Changed to use tokenizer.
| |
| 50 mimeType.utf8().compare("video/webm") == 0 || | |
| 51 mimeType.utf8().compare("audio/webm") == 0; | |
| 47 } | 52 } |
| 48 | 53 |
| 49 bool MediaRecorderHandler::initialize( | 54 bool MediaRecorderHandler::initialize( |
| 50 blink::WebMediaRecorderHandlerClient* client, | 55 blink::WebMediaRecorderHandlerClient* client, |
| 51 const blink::WebMediaStream& media_stream, | 56 const blink::WebMediaStream& media_stream, |
| 52 const blink::WebString& mimeType) { | 57 const blink::WebString& mimeType) { |
| 53 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 58 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 54 // Save histogram data so we can see how much MediaStream Recorder is used. | 59 // Save histogram data so we can see how much MediaStream Recorder is used. |
| 55 // The histogram counts the number of calls to the JS API. | 60 // The histogram counts the number of calls to the JS API. |
| 56 UpdateWebRTCMethodCount(WEBKIT_MEDIA_STREAM_RECORDER); | 61 UpdateWebRTCMethodCount(WEBKIT_MEDIA_STREAM_RECORDER); |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 75 | 80 |
| 76 bool MediaRecorderHandler::start(int timeslice) { | 81 bool MediaRecorderHandler::start(int timeslice) { |
| 77 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 82 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 78 DCHECK(!recording_); | 83 DCHECK(!recording_); |
| 79 DCHECK(!media_stream_.isNull()); | 84 DCHECK(!media_stream_.isNull()); |
| 80 DCHECK(timeslice_.is_zero()); | 85 DCHECK(timeslice_.is_zero()); |
| 81 | 86 |
| 82 timeslice_ = TimeDelta::FromMilliseconds(timeslice); | 87 timeslice_ = TimeDelta::FromMilliseconds(timeslice); |
| 83 slice_origin_timestamp_ = TimeTicks::Now(); | 88 slice_origin_timestamp_ = TimeTicks::Now(); |
| 84 | 89 |
| 85 blink::WebVector<blink::WebMediaStreamTrack> video_tracks; | 90 blink::WebVector<blink::WebMediaStreamTrack> video_tracks, audio_tracks; |
| 86 media_stream_.videoTracks(video_tracks); | 91 media_stream_.videoTracks(video_tracks); |
| 92 media_stream_.audioTracks(audio_tracks); | |
| 87 | 93 |
| 88 #if !defined(MEDIA_DISABLE_LIBWEBM) | 94 #if defined(MEDIA_DISABLE_LIBWEBM) |
| 89 DCHECK(!webm_muxer_); | |
| 90 | |
| 91 webm_muxer_.reset(new media::WebmMuxer( | |
| 92 use_vp9_ ? media::kCodecVP9 : media::kCodecVP8, video_tracks.size() > 0, | |
| 93 false /* no audio for now - http://crbug.com/528519 */, | |
| 94 base::Bind(&MediaRecorderHandler::WriteData, | |
| 95 weak_factory_.GetWeakPtr()))); | |
| 96 #else | |
| 97 LOG(WARNING) << "No muxer available"; | 95 LOG(WARNING) << "No muxer available"; |
| 98 return false; | 96 return false; |
| 99 #endif | 97 #endif |
| 100 | 98 |
| 101 if (video_tracks.isEmpty()) { | 99 DCHECK(!webm_muxer_); |
| 102 // TODO(mcasas): Add audio_tracks and update the code in this function | 100 |
| 103 // correspondingly, see http://crbug.com/528519. As of now, only video | 101 if (video_tracks.isEmpty() && audio_tracks.isEmpty()) { |
| 104 // tracks are supported. | 102 LOG(WARNING) << __FUNCTION__ << ": no media tracks."; |
| 105 LOG(WARNING) << "Recording no video tracks is not implemented"; | |
| 106 return false; | 103 return false; |
| 107 } | 104 } |
| 108 // TODO(mcasas): The muxer API supports only one video track. Extend it to | |
| 109 // several video tracks, see http://crbug.com/528523. | |
| 110 LOG_IF(WARNING, video_tracks.size() > 1u) << "Recording multiple video" | |
| 111 << " tracks is not implemented. Only recording first video track."; | |
| 112 const blink::WebMediaStreamTrack& video_track = video_tracks[0]; | |
| 113 if (video_track.isNull()) | |
| 114 return false; | |
| 115 | 105 |
| 116 const VideoTrackRecorder::OnEncodedVideoCB on_encoded_video_cb = | 106 webm_muxer_.reset(new media::WebmMuxer( |
| 117 media::BindToCurrentLoop(base::Bind(&MediaRecorderHandler::OnEncodedVideo, | 107 use_vp9_ ? media::kCodecVP9 : media::kCodecVP8, |
| 118 weak_factory_.GetWeakPtr())); | 108 video_tracks.size() > 0, audio_tracks.size() > 0, |
| 109 base::Bind(&MediaRecorderHandler::WriteData, | |
| 110 weak_factory_.GetWeakPtr()))); | |
| 119 | 111 |
| 120 video_recorders_.push_back(new VideoTrackRecorder(use_vp9_, | 112 if (!video_tracks.isEmpty()) { |
| 121 video_track, | 113 // TODO(mcasas): The muxer API supports only one video track. Extend it to |
| 122 on_encoded_video_cb)); | 114 // several video tracks, see http://crbug.com/528523. |
| 115 LOG_IF(WARNING, video_tracks.size() > 1u) | |
| 116 << "Recording multiple video tracks is not implemented. " | |
| 117 << "Only recording first video track."; | |
| 118 const blink::WebMediaStreamTrack& video_track = video_tracks[0]; | |
| 119 if (video_track.isNull()) | |
| 120 return false; | |
| 121 | |
| 122 const VideoTrackRecorder::OnEncodedVideoCB on_encoded_video_cb = | |
| 123 media::BindToCurrentLoop(base::Bind( | |
| 124 &MediaRecorderHandler::OnEncodedVideo, weak_factory_.GetWeakPtr())); | |
| 125 | |
| 126 video_recorders_.push_back( | |
| 127 new VideoTrackRecorder(use_vp9_, video_track, on_encoded_video_cb)); | |
| 128 } | |
| 129 | |
| 130 if (!audio_tracks.isEmpty()) { | |
| 131 // TODO(ajose): The muxer API supports only one audio track. Extend it to | |
| 132 // several tracks. | |
| 133 LOG_IF(WARNING, audio_tracks.size() > 1u) | |
| 134 << "Recording multiple audio" | |
| 135 << " tracks is not implemented. Only recording first audio track."; | |
| 136 const blink::WebMediaStreamTrack& audio_track = audio_tracks[0]; | |
| 137 if (audio_track.isNull()) | |
| 138 return false; | |
| 139 | |
| 140 const AudioTrackRecorder::OnEncodedAudioCB on_encoded_audio_cb = | |
| 141 media::BindToCurrentLoop(base::Bind( | |
| 142 &MediaRecorderHandler::OnEncodedAudio, weak_factory_.GetWeakPtr())); | |
| 143 | |
| 144 audio_recorders_.push_back( | |
| 145 new AudioTrackRecorder(audio_track, on_encoded_audio_cb)); | |
| 146 } | |
| 123 | 147 |
| 124 recording_ = true; | 148 recording_ = true; |
| 125 return true; | 149 return true; |
| 126 } | 150 } |
| 127 | 151 |
| 128 void MediaRecorderHandler::stop() { | 152 void MediaRecorderHandler::stop() { |
| 129 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 153 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 130 DCHECK(recording_); | 154 DCHECK(recording_); |
| 131 | 155 |
| 132 recording_ = false; | 156 recording_ = false; |
| 133 timeslice_ = TimeDelta::FromMilliseconds(0); | 157 timeslice_ = TimeDelta::FromMilliseconds(0); |
| 134 video_recorders_.clear(); | 158 video_recorders_.clear(); |
| 159 audio_recorders_.clear(); | |
| 135 #if !defined(MEDIA_DISABLE_LIBWEBM) | 160 #if !defined(MEDIA_DISABLE_LIBWEBM) |
| 136 webm_muxer_.reset(); | 161 webm_muxer_.reset(); |
| 137 #endif | 162 #endif |
| 138 } | 163 } |
| 139 | 164 |
| 140 void MediaRecorderHandler::pause() { | 165 void MediaRecorderHandler::pause() { |
| 141 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 166 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 142 DCHECK(recording_); | 167 DCHECK(recording_); |
| 143 recording_ = false; | 168 recording_ = false; |
| 144 for (const auto& video_recorder : video_recorders_) | 169 for (const auto& video_recorder : video_recorders_) |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 160 bool is_key_frame) { | 185 bool is_key_frame) { |
| 161 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 186 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 162 #if !defined(MEDIA_DISABLE_LIBWEBM) | 187 #if !defined(MEDIA_DISABLE_LIBWEBM) |
| 163 if (!webm_muxer_) | 188 if (!webm_muxer_) |
| 164 return; | 189 return; |
| 165 webm_muxer_->OnEncodedVideo(video_frame, encoded_data.Pass(), timestamp, | 190 webm_muxer_->OnEncodedVideo(video_frame, encoded_data.Pass(), timestamp, |
| 166 is_key_frame); | 191 is_key_frame); |
| 167 #endif | 192 #endif |
| 168 } | 193 } |
| 169 | 194 |
| 195 void MediaRecorderHandler::OnEncodedAudio(const media::AudioParameters& params, | |
| 196 scoped_ptr<std::string> encoded_data, | |
| 197 base::TimeTicks timestamp) { | |
| 198 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | |
| 199 if (webm_muxer_) | |
| 200 webm_muxer_->OnEncodedAudio(params, encoded_data.Pass(), timestamp); | |
| 201 } | |
| 202 | |
| 170 void MediaRecorderHandler::WriteData(base::StringPiece data) { | 203 void MediaRecorderHandler::WriteData(base::StringPiece data) { |
| 171 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 204 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 172 | 205 |
| 173 // Non-buffered mode does not need to check timestamps. | 206 // Non-buffered mode does not need to check timestamps. |
| 174 if (timeslice_.is_zero()) { | 207 if (timeslice_.is_zero()) { |
| 175 client_->writeData(data.data(), data.length(), true /* lastInSlice */); | 208 client_->writeData(data.data(), data.length(), true /* lastInSlice */); |
| 176 return; | 209 return; |
| 177 } | 210 } |
| 178 | 211 |
| 179 const TimeTicks now = TimeTicks::Now(); | 212 const TimeTicks now = TimeTicks::Now(); |
| 180 const bool last_in_slice = now > slice_origin_timestamp_ + timeslice_; | 213 const bool last_in_slice = now > slice_origin_timestamp_ + timeslice_; |
| 181 DVLOG_IF(1, last_in_slice) << "Slice finished @ " << now; | 214 DVLOG_IF(1, last_in_slice) << "Slice finished @ " << now; |
| 182 if (last_in_slice) | 215 if (last_in_slice) |
| 183 slice_origin_timestamp_ = now; | 216 slice_origin_timestamp_ = now; |
| 184 client_->writeData(data.data(), data.length(), last_in_slice); | 217 client_->writeData(data.data(), data.length(), last_in_slice); |
| 185 } | 218 } |
| 186 | 219 |
| 187 void MediaRecorderHandler::OnVideoFrameForTesting( | 220 void MediaRecorderHandler::OnVideoFrameForTesting( |
| 188 const scoped_refptr<media::VideoFrame>& frame, | 221 const scoped_refptr<media::VideoFrame>& frame, |
| 189 const TimeTicks& timestamp) { | 222 const TimeTicks& timestamp) { |
| 190 for (auto* recorder : video_recorders_) | 223 for (auto* recorder : video_recorders_) |
| 191 recorder->OnVideoFrameForTesting(frame, timestamp); | 224 recorder->OnVideoFrameForTesting(frame, timestamp); |
| 192 } | 225 } |
| 193 | 226 |
| 227 void MediaRecorderHandler::OnAudioBusForTesting( | |
| 228 const media::AudioBus& audio_bus, | |
| 229 const base::TimeTicks& timestamp) { | |
| 230 for (auto* recorder : audio_recorders_) | |
| 231 recorder->OnData(audio_bus, timestamp); | |
| 232 } | |
| 233 | |
| 234 void MediaRecorderHandler::SetAudioFormatForTesting( | |
| 235 const media::AudioParameters& params) { | |
| 236 for (auto* recorder : audio_recorders_) | |
| 237 recorder->OnSetFormat(params); | |
| 238 } | |
| 239 | |
| 194 } // namespace content | 240 } // namespace content |
| OLD | NEW |