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 |