| 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 <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/location.h" | 10 #include "base/location.h" |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 return media::kCodecVP8; | 40 return media::kCodecVP8; |
| 41 case VideoTrackRecorder::CodecId::VP9: | 41 case VideoTrackRecorder::CodecId::VP9: |
| 42 return media::kCodecVP9; | 42 return media::kCodecVP9; |
| 43 case VideoTrackRecorder::CodecId::H264: | 43 case VideoTrackRecorder::CodecId::H264: |
| 44 return media::kCodecH264; | 44 return media::kCodecH264; |
| 45 } | 45 } |
| 46 NOTREACHED() << "Unsupported codec"; | 46 NOTREACHED() << "Unsupported codec"; |
| 47 return media::kUnknownVideoCodec; | 47 return media::kUnknownVideoCodec; |
| 48 } | 48 } |
| 49 | 49 |
| 50 // Extracts the MIME subtype from an incoming |content_type|, or empty. |
| 51 std::string getSubtypeFromContentType(const std::string& content_type) { |
| 52 base::StringTokenizer web_type_tokens(content_type, "/"); |
| 53 if (!web_type_tokens.GetNext() || !web_type_tokens.GetNext()) |
| 54 return std::string(); |
| 55 return web_type_tokens.token(); |
| 56 } |
| 57 |
| 58 bool findCodecInList(const std::string& codec, |
| 59 const char* const* codecs, |
| 60 size_t codecs_count) { |
| 61 auto* const* found = std::find_if( |
| 62 &codecs[0], &codecs[codecs_count], [&codec](const char* name) { |
| 63 return base::EqualsCaseInsensitiveASCII(codec, name); |
| 64 }); |
| 65 return found != &codecs[codecs_count]; |
| 66 } |
| 67 |
| 50 } // anonymous namespace | 68 } // anonymous namespace |
| 51 | 69 |
| 52 MediaRecorderHandler::MediaRecorderHandler() | 70 MediaRecorderHandler::MediaRecorderHandler() |
| 53 : video_bits_per_second_(0), | 71 : video_bits_per_second_(0), |
| 54 audio_bits_per_second_(0), | 72 audio_bits_per_second_(0), |
| 73 use_container_(true), |
| 55 codec_id_(VideoTrackRecorder::CodecId::VP8), | 74 codec_id_(VideoTrackRecorder::CodecId::VP8), |
| 56 recording_(false), | 75 recording_(false), |
| 57 client_(nullptr), | 76 client_(nullptr), |
| 58 weak_factory_(this) {} | 77 weak_factory_(this) {} |
| 59 | 78 |
| 60 MediaRecorderHandler::~MediaRecorderHandler() { | 79 MediaRecorderHandler::~MediaRecorderHandler() { |
| 61 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 80 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 62 // Send a |last_in_slice| to our |client_|. | 81 // Send a |last_in_slice| to our |client_|. |
| 63 if (client_) | 82 if (client_) |
| 64 client_->writeData(nullptr, 0u, true); | 83 client_->writeData(nullptr, 0u, true); |
| 65 } | 84 } |
| 66 | 85 |
| 67 bool MediaRecorderHandler::canSupportMimeType( | 86 bool MediaRecorderHandler::canSupportMimeType( |
| 68 const blink::WebString& web_type, | 87 const blink::WebString& web_type, |
| 69 const blink::WebString& web_codecs) { | 88 const blink::WebString& web_codecs) { |
| 70 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 89 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 71 // An empty |web_type| means MediaRecorderHandler can choose its preferred | 90 // An empty |web_type| means MediaRecorderHandler can choose its preferred |
| 72 // codecs. | 91 // codecs. |
| 73 if (web_type.isEmpty()) | 92 if (web_type.isEmpty()) |
| 74 return true; | 93 return true; |
| 75 | 94 |
| 76 const std::string type(web_type.utf8()); | 95 const std::string type(web_type.utf8()); |
| 77 const bool video = base::EqualsCaseInsensitiveASCII(type, "video/webm"); | 96 const bool video = |
| 97 base::StartsWith(type, "video/", base::CompareCase::INSENSITIVE_ASCII); |
| 78 const bool audio = | 98 const bool audio = |
| 79 video ? false : base::EqualsCaseInsensitiveASCII(type, "audio/webm"); | 99 video ? false : base::StartsWith(type, "audio/", |
| 100 base::CompareCase::INSENSITIVE_ASCII); |
| 80 if (!video && !audio) | 101 if (!video && !audio) |
| 81 return false; | 102 return false; |
| 82 | 103 |
| 83 // Both |video| and |audio| support empty |codecs|; |type| == "video" supports | 104 // Both |video| and |audio| support empty |codecs|; |type| == "video" supports |
| 84 // vp8, vp9 or opus; |type| = "audio", supports only opus. | 105 // vp8, vp9, H264 or opus; |type| = "audio", supports only opus. |
| 85 // http://www.webmproject.org/docs/container Sec:"HTML5 Video Type Parameters" | 106 // http://www.webmproject.org/docs/container Sec:"HTML5 Video Type Parameters" |
| 86 static const char* const kVideoCodecs[] = { "vp8", "vp9", "h264", "opus" }; | 107 static const char* const kVideoCodecs[] = { "vp8", "vp9", "h264", "opus" }; |
| 87 static const char* const kAudioCodecs[] = { "opus" }; | 108 static const char* const kAudioCodecs[] = { "opus" }; |
| 88 const char* const* codecs = video ? &kVideoCodecs[0] : &kAudioCodecs[0]; | 109 const char* const* codecs = video ? &kVideoCodecs[0] : &kAudioCodecs[0]; |
| 89 const int codecs_count = | 110 const int codecs_count = |
| 90 video ? arraysize(kVideoCodecs) : arraysize(kAudioCodecs); | 111 video ? arraysize(kVideoCodecs) : arraysize(kAudioCodecs); |
| 91 | 112 |
| 113 // Check for the container or the codec following video/ or audio/. "webm" |
| 114 // container is supported,; so are any of the |codecs| as non-contained. |
| 115 const std::string mime_subtype = getSubtypeFromContentType(type); |
| 116 if (!base::EqualsCaseInsensitiveASCII(mime_subtype, "webm")) |
| 117 return findCodecInList(mime_subtype, codecs, codecs_count); |
| 118 |
| 92 std::vector<std::string> codecs_list; | 119 std::vector<std::string> codecs_list; |
| 93 media::ParseCodecString(web_codecs.utf8(), &codecs_list, true /* strip */); | 120 media::ParseCodecString(web_codecs.utf8(), &codecs_list, true /* strip */); |
| 94 for (const auto& codec : codecs_list) { | 121 for (const auto& codec : codecs_list) { |
| 95 auto* const* found = std::find_if( | 122 if (!findCodecInList(codec, codecs, codecs_count)) |
| 96 &codecs[0], &codecs[codecs_count], [&codec](const char* name) { | |
| 97 return base::EqualsCaseInsensitiveASCII(codec, name); | |
| 98 }); | |
| 99 if (found == &codecs[codecs_count]) | |
| 100 return false; | 123 return false; |
| 101 } | 124 } |
| 102 return true; | 125 return true; |
| 103 } | 126 } |
| 104 | 127 |
| 105 bool MediaRecorderHandler::initialize( | 128 bool MediaRecorderHandler::initialize( |
| 106 blink::WebMediaRecorderHandlerClient* client, | 129 blink::WebMediaRecorderHandlerClient* client, |
| 107 const blink::WebMediaStream& media_stream, | 130 const blink::WebMediaStream& media_stream, |
| 108 const blink::WebString& type, | 131 const blink::WebString& type, |
| 109 const blink::WebString& codecs, | 132 const blink::WebString& codecs, |
| 110 int32_t audio_bits_per_second, | 133 int32_t audio_bits_per_second, |
| 111 int32_t video_bits_per_second) { | 134 int32_t video_bits_per_second) { |
| 112 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 135 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 113 // Save histogram data so we can see how much MediaStream Recorder is used. | 136 // Save histogram data so we can see how much MediaStream Recorder is used. |
| 114 // The histogram counts the number of calls to the JS API. | 137 // The histogram counts the number of calls to the JS API. |
| 115 UpdateWebRTCMethodCount(WEBKIT_MEDIA_STREAM_RECORDER); | 138 UpdateWebRTCMethodCount(WEBKIT_MEDIA_STREAM_RECORDER); |
| 116 | 139 |
| 117 if (!canSupportMimeType(type, codecs)) { | 140 if (!canSupportMimeType(type, codecs)) { |
| 118 DLOG(ERROR) << "Can't support " << type.utf8() | 141 DLOG(ERROR) << "Can't support " << type.utf8() |
| 119 << ";codecs=" << codecs.utf8(); | 142 << ";codecs=" << codecs.utf8(); |
| 120 return false; | 143 return false; |
| 121 } | 144 } |
| 122 | 145 |
| 146 const std::string mime_subtype = getSubtypeFromContentType(type.utf8()); |
| 147 use_container_ = |
| 148 type.isEmpty() || base::EqualsCaseInsensitiveASCII(mime_subtype, "webm"); |
| 149 |
| 150 const std::string& codecs_str = |
| 151 use_container_ ? ToLowerASCII(codecs.utf8()) : ToLowerASCII(mime_subtype); |
| 152 |
| 123 // Once established that we support the codec(s), hunt then individually. | 153 // Once established that we support the codec(s), hunt then individually. |
| 124 const std::string& codecs_str = ToLowerASCII(codecs.utf8()); | |
| 125 if (codecs_str.find("vp8") != std::string::npos) | 154 if (codecs_str.find("vp8") != std::string::npos) |
| 126 codec_id_ = VideoTrackRecorder::CodecId::VP8; | 155 codec_id_ = VideoTrackRecorder::CodecId::VP8; |
| 127 else if (codecs_str.find("vp9") != std::string::npos) | 156 else if (codecs_str.find("vp9") != std::string::npos) |
| 128 codec_id_ = VideoTrackRecorder::CodecId::VP9; | 157 codec_id_ = VideoTrackRecorder::CodecId::VP9; |
| 129 #if BUILDFLAG(RTC_USE_H264) | 158 #if BUILDFLAG(RTC_USE_H264) |
| 130 else if (codecs_str.find("h264") != std::string::npos) | 159 else if (codecs_str.find("h264") != std::string::npos) |
| 131 codec_id_ = VideoTrackRecorder::CodecId::H264; | 160 codec_id_ = VideoTrackRecorder::CodecId::H264; |
| 132 #endif | 161 #endif |
| 133 | 162 |
| 134 media_stream_ = media_stream; | 163 media_stream_ = media_stream; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 173 !audio_tracks.isEmpty() && MediaStreamAudioTrack::From(audio_tracks[0]) && | 202 !audio_tracks.isEmpty() && MediaStreamAudioTrack::From(audio_tracks[0]) && |
| 174 audio_tracks[0].isEnabled() && | 203 audio_tracks[0].isEnabled() && |
| 175 audio_tracks[0].source().getReadyState() != | 204 audio_tracks[0].source().getReadyState() != |
| 176 blink::WebMediaStreamSource::ReadyStateEnded; | 205 blink::WebMediaStreamSource::ReadyStateEnded; |
| 177 | 206 |
| 178 if (!use_video_tracks && !use_audio_tracks) { | 207 if (!use_video_tracks && !use_audio_tracks) { |
| 179 LOG(WARNING) << __func__ << ": no tracks to be recorded."; | 208 LOG(WARNING) << __func__ << ": no tracks to be recorded."; |
| 180 return false; | 209 return false; |
| 181 } | 210 } |
| 182 | 211 |
| 183 webm_muxer_.reset(new media::WebmMuxer( | 212 if (use_container_) { |
| 184 CodecIdToMediaVideoCodec(codec_id_), use_video_tracks, use_audio_tracks, | 213 webm_muxer_.reset(new media::WebmMuxer( |
| 185 base::Bind(&MediaRecorderHandler::WriteData, | 214 CodecIdToMediaVideoCodec(codec_id_), use_video_tracks, use_audio_tracks, |
| 186 weak_factory_.GetWeakPtr()))); | 215 base::Bind(&MediaRecorderHandler::WriteData, |
| 216 weak_factory_.GetWeakPtr()))); |
| 217 } |
| 187 | 218 |
| 188 if (use_video_tracks) { | 219 if (use_video_tracks) { |
| 189 // TODO(mcasas): The muxer API supports only one video track. Extend it to | 220 // TODO(mcasas): The muxer API supports only one video track. Extend it to |
| 190 // several video tracks, see http://crbug.com/528523. | 221 // several video tracks, see http://crbug.com/528523. |
| 191 LOG_IF(WARNING, video_tracks.size() > 1u) | 222 LOG_IF(WARNING, video_tracks.size() > 1u) |
| 192 << "Recording multiple video tracks is not implemented. " | 223 << "Recording multiple video tracks is not implemented. " |
| 193 << "Only recording first video track."; | 224 << "Only recording first video track."; |
| 194 const blink::WebMediaStreamTrack& video_track = video_tracks[0]; | 225 const blink::WebMediaStreamTrack& video_track = video_tracks[0]; |
| 195 if (video_track.isNull()) | 226 if (video_track.isNull()) |
| 196 return false; | 227 return false; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 237 } | 268 } |
| 238 | 269 |
| 239 void MediaRecorderHandler::pause() { | 270 void MediaRecorderHandler::pause() { |
| 240 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 271 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 241 DCHECK(recording_); | 272 DCHECK(recording_); |
| 242 recording_ = false; | 273 recording_ = false; |
| 243 for (auto* video_recorder : video_recorders_) | 274 for (auto* video_recorder : video_recorders_) |
| 244 video_recorder->Pause(); | 275 video_recorder->Pause(); |
| 245 for (auto* audio_recorder : audio_recorders_) | 276 for (auto* audio_recorder : audio_recorders_) |
| 246 audio_recorder->Pause(); | 277 audio_recorder->Pause(); |
| 247 webm_muxer_->Pause(); | 278 if (webm_muxer_) |
| 279 webm_muxer_->Pause(); |
| 248 } | 280 } |
| 249 | 281 |
| 250 void MediaRecorderHandler::resume() { | 282 void MediaRecorderHandler::resume() { |
| 251 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 283 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 252 DCHECK(!recording_); | 284 DCHECK(!recording_); |
| 253 recording_ = true; | 285 recording_ = true; |
| 254 for (auto* video_recorder : video_recorders_) | 286 for (auto* video_recorder : video_recorders_) |
| 255 video_recorder->Resume(); | 287 video_recorder->Resume(); |
| 256 for (auto* audio_recorder : audio_recorders_) | 288 for (auto* audio_recorder : audio_recorders_) |
| 257 audio_recorder->Resume(); | 289 audio_recorder->Resume(); |
| 258 webm_muxer_->Resume(); | 290 if (webm_muxer_) |
| 291 webm_muxer_->Resume(); |
| 259 } | 292 } |
| 260 | 293 |
| 261 void MediaRecorderHandler::OnEncodedVideo( | 294 void MediaRecorderHandler::OnEncodedVideo( |
| 262 const scoped_refptr<media::VideoFrame>& video_frame, | 295 const scoped_refptr<media::VideoFrame>& video_frame, |
| 263 std::unique_ptr<std::string> encoded_data, | 296 std::unique_ptr<std::string> encoded_data, |
| 264 TimeTicks timestamp, | 297 TimeTicks timestamp, |
| 265 bool is_key_frame) { | 298 bool is_key_frame) { |
| 266 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 299 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 267 if (!webm_muxer_) | 300 if (webm_muxer_) { |
| 268 return; | 301 return webm_muxer_->OnEncodedVideo(video_frame, std::move(encoded_data), |
| 269 webm_muxer_->OnEncodedVideo(video_frame, std::move(encoded_data), timestamp, | 302 timestamp, is_key_frame); |
| 270 is_key_frame); | 303 } |
| 304 DCHECK(!use_container_); |
| 305 WriteData(base::StringPiece(*encoded_data.get())); |
| 271 } | 306 } |
| 272 | 307 |
| 273 void MediaRecorderHandler::OnEncodedAudio( | 308 void MediaRecorderHandler::OnEncodedAudio( |
| 274 const media::AudioParameters& params, | 309 const media::AudioParameters& params, |
| 275 std::unique_ptr<std::string> encoded_data, | 310 std::unique_ptr<std::string> encoded_data, |
| 276 base::TimeTicks timestamp) { | 311 base::TimeTicks timestamp) { |
| 277 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 312 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 278 if (webm_muxer_) | 313 if (webm_muxer_) |
| 279 webm_muxer_->OnEncodedAudio(params, std::move(encoded_data), timestamp); | 314 webm_muxer_->OnEncodedAudio(params, std::move(encoded_data), timestamp); |
| 280 } | 315 } |
| (...skipping 28 matching lines...) Expand all Loading... |
| 309 recorder->OnData(audio_bus, timestamp); | 344 recorder->OnData(audio_bus, timestamp); |
| 310 } | 345 } |
| 311 | 346 |
| 312 void MediaRecorderHandler::SetAudioFormatForTesting( | 347 void MediaRecorderHandler::SetAudioFormatForTesting( |
| 313 const media::AudioParameters& params) { | 348 const media::AudioParameters& params) { |
| 314 for (auto* recorder : audio_recorders_) | 349 for (auto* recorder : audio_recorders_) |
| 315 recorder->OnSetFormat(params); | 350 recorder->OnSetFormat(params); |
| 316 } | 351 } |
| 317 | 352 |
| 318 } // namespace content | 353 } // namespace content |
| OLD | NEW |