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/video_track_recorder.h" | 5 #include "content/renderer/media/video_track_recorder.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/macros.h" | 11 #include "base/macros.h" |
12 #include "base/sys_info.h" | 12 #include "base/sys_info.h" |
13 #include "base/threading/thread.h" | 13 #include "base/threading/thread.h" |
14 #include "base/time/time.h" | 14 #include "base/time/time.h" |
15 #include "base/trace_event/trace_event.h" | 15 #include "base/trace_event/trace_event.h" |
16 #include "media/base/video_frame.h" | 16 #include "media/base/video_frame.h" |
17 #include "media/base/video_util.h" | 17 #include "media/base/video_util.h" |
18 #include "ui/gfx/geometry/size.h" | 18 #include "ui/gfx/geometry/size.h" |
19 | 19 |
20 extern "C" { | 20 extern "C" { |
21 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide | 21 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide |
22 // backwards compatibility for legacy applications using the library. | 22 // backwards compatibility for legacy applications using the library. |
23 #define VPX_CODEC_DISABLE_COMPAT 1 | 23 #define VPX_CODEC_DISABLE_COMPAT 1 |
24 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" | 24 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" |
25 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h" | 25 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h" |
26 } | 26 } |
27 | 27 |
| 28 #include "third_party/openh264/src/codec/api/svc/codec_api.h" |
| 29 #include "third_party/openh264/src/codec/api/svc/codec_app_def.h" |
| 30 #include "third_party/openh264/src/codec/api/svc/codec_def.h" |
| 31 |
28 using media::VideoFrame; | 32 using media::VideoFrame; |
29 using media::VideoFrameMetadata; | 33 using media::VideoFrameMetadata; |
30 | 34 |
31 namespace content { | 35 namespace content { |
32 | 36 |
33 namespace { | 37 namespace { |
34 | 38 |
35 const vpx_codec_flags_t kNoFlags = 0; | 39 const vpx_codec_flags_t kNoFlags = 0; |
36 | 40 |
37 // Originally from remoting/codec/scoped_vpx_codec.h. | 41 // Originally from remoting/codec/scoped_vpx_codec.h. |
(...skipping 16 matching lines...) Expand all Loading... |
54 std::unique_ptr<std::string> data, | 58 std::unique_ptr<std::string> data, |
55 base::TimeTicks capture_timestamp, | 59 base::TimeTicks capture_timestamp, |
56 bool keyframe) { | 60 bool keyframe) { |
57 DVLOG(1) << (keyframe ? "" : "non ") << "keyframe "<< data->length() << "B, " | 61 DVLOG(1) << (keyframe ? "" : "non ") << "keyframe "<< data->length() << "B, " |
58 << capture_timestamp << " ms"; | 62 << capture_timestamp << " ms"; |
59 on_encoded_video_cb.Run(frame, std::move(data), capture_timestamp, keyframe); | 63 on_encoded_video_cb.Run(frame, std::move(data), capture_timestamp, keyframe); |
60 } | 64 } |
61 | 65 |
62 } // anonymous namespace | 66 } // anonymous namespace |
63 | 67 |
| 68 class VideoTrackRecorder::Encoder : public base::RefCountedThreadSafe<Encoder> { |
| 69 public: |
| 70 virtual void StartFrameEncode(const scoped_refptr<VideoFrame>& frame, |
| 71 base::TimeTicks capture_timestamp) = 0; |
| 72 |
| 73 virtual void set_paused(bool paused) = 0; |
| 74 |
| 75 protected: |
| 76 friend class base::RefCountedThreadSafe<Encoder>; |
| 77 virtual ~Encoder() {} |
| 78 }; |
| 79 |
| 80 namespace { |
| 81 |
64 // Inner class encapsulating all libvpx interactions and the encoding+delivery | 82 // Inner class encapsulating all libvpx interactions and the encoding+delivery |
65 // of received frames. Limitation: Only VP8 is supported for the time being. | 83 // of received frames. Limitation: Only VP8 is supported for the time being. |
66 // This class must be ref-counted because the MediaStreamVideoTrack will hold a | 84 // This class must be ref-counted because the MediaStreamVideoTrack will hold a |
67 // reference to it, via the callback MediaStreamVideoSink passes along, and it's | 85 // reference to it, via the callback MediaStreamVideoSink passes along, and it's |
68 // unknown when exactly it will release that reference. This class: | 86 // unknown when exactly it will release that reference. This class: |
69 // - is created and destroyed on its parent's thread (usually the main Render | 87 // - is created and destroyed on its parent's thread (usually the main Render |
70 // thread); | 88 // thread); |
71 // - receives VideoFrames and Run()s the callbacks on |origin_task_runner_|, | 89 // - receives VideoFrames and Run()s the callbacks on |origin_task_runner_|, |
72 // which is cached on first frame arrival, and is supposed to be the render IO | 90 // which is cached on first frame arrival, and is supposed to be the render IO |
73 // thread, but this is not enforced; | 91 // thread, but this is not enforced; |
74 // - uses an internal |encoding_thread_| for libvpx interactions, notably for | 92 // - uses an internal |encoding_thread_| for libvpx interactions, notably for |
75 // encoding (which might take some time). | 93 // encoding (which might take some time). |
76 class VideoTrackRecorder::VpxEncoder final | 94 class VpxEncoder final : public VideoTrackRecorder::Encoder { |
77 : public base::RefCountedThreadSafe<VpxEncoder> { | |
78 public: | 95 public: |
79 static void ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread, | 96 static void ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread, |
80 ScopedVpxCodecCtxPtr encoder); | 97 ScopedVpxCodecCtxPtr encoder); |
81 | 98 |
82 VpxEncoder(bool use_vp9, | 99 VpxEncoder( |
83 const OnEncodedVideoCB& on_encoded_video_callback, | 100 bool use_vp9, |
84 int32_t bits_per_second); | 101 const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback, |
| 102 int32_t bits_per_second); |
85 | 103 |
86 void StartFrameEncode(const scoped_refptr<VideoFrame>& frame, | 104 void StartFrameEncode(const scoped_refptr<VideoFrame>& frame, |
87 base::TimeTicks capture_timestamp); | 105 base::TimeTicks capture_timestamp) override; |
88 | 106 |
89 void set_paused(bool paused) { paused_ = paused; } | 107 void set_paused(bool paused) override { paused_ = paused; } |
90 | 108 |
91 private: | 109 private: |
92 friend class base::RefCountedThreadSafe<VpxEncoder>; | 110 ~VpxEncoder() override; |
93 ~VpxEncoder(); | |
94 | 111 |
95 void EncodeOnEncodingThread(const scoped_refptr<VideoFrame>& frame, | 112 void EncodeOnEncodingThread(const scoped_refptr<VideoFrame>& frame, |
96 base::TimeTicks capture_timestamp); | 113 base::TimeTicks capture_timestamp); |
97 | 114 |
98 void ConfigureEncoding(const gfx::Size& size); | 115 void ConfigureEncoding(const gfx::Size& size); |
99 | 116 |
100 // Returns true if |codec_config_| has been filled in at least once. | 117 // Returns true if |codec_config_| has been filled in at least once. |
101 bool IsInitialized() const; | 118 bool IsInitialized() const; |
102 | 119 |
103 // Estimate the frame duration from |frame| and |last_frame_timestamp_|. | 120 // Estimate the frame duration from |frame| and |last_frame_timestamp_|. |
104 base::TimeDelta CalculateFrameDuration( | 121 base::TimeDelta CalculateFrameDuration( |
105 const scoped_refptr<VideoFrame>& frame); | 122 const scoped_refptr<VideoFrame>& frame); |
106 | 123 |
107 // While |paused_|, frames are not encoded. | 124 // While |paused_|, frames are not encoded. |
108 bool paused_; | 125 bool paused_; |
109 | 126 |
110 // Force usage of VP9 for encoding, instead of VP8 which is the default. | 127 // Force usage of VP9 for encoding, instead of VP8 which is the default. |
111 const bool use_vp9_; | 128 const bool use_vp9_; |
112 | 129 |
113 // This callback should be exercised on IO thread. | 130 // This callback should be exercised on IO thread. |
114 const OnEncodedVideoCB on_encoded_video_callback_; | 131 const VideoTrackRecorder::OnEncodedVideoCB on_encoded_video_callback_; |
115 | 132 |
116 // Target bitrate or video encoding. If 0, a standard bitrate is used. | 133 // Target bitrate or video encoding. If 0, a standard bitrate is used. |
117 const int32_t bits_per_second_; | 134 const int32_t bits_per_second_; |
118 | 135 |
119 // Used to shutdown properly on the same thread we were created. | 136 // Used to shutdown properly on the same thread we were created. |
120 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; | 137 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; |
121 | 138 |
122 // Task runner where frames to encode and reply callbacks must happen. | 139 // Task runner where frames to encode and reply callbacks must happen. |
123 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_; | 140 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_; |
124 | 141 |
125 // Thread for encoding. Active for the lifetime of VpxEncoder. All variables | 142 // Thread for encoding. Active for the lifetime of VpxEncoder. All variables |
126 // below this are used in this thread. | 143 // below this are used in this thread. |
127 std::unique_ptr<base::Thread> encoding_thread_; | 144 std::unique_ptr<base::Thread> encoding_thread_; |
128 // VP8 internal objects: configuration and encoder. | 145 // VP8 internal objects: configuration and encoder. |
129 vpx_codec_enc_cfg_t codec_config_; | 146 vpx_codec_enc_cfg_t codec_config_; |
130 // |encoder_| is a special scoped pointer to guarantee proper destruction. | 147 // |encoder_| is a special scoped pointer to guarantee proper destruction. |
131 // Again, it should only be accessed on |encoding_thread_|. | 148 // Again, it should only be accessed on |encoding_thread_|. |
132 ScopedVpxCodecCtxPtr encoder_; | 149 ScopedVpxCodecCtxPtr encoder_; |
133 | 150 |
| 151 |
134 // The |VideoFrame::timestamp()| of the last encoded frame. This is used to | 152 // The |VideoFrame::timestamp()| of the last encoded frame. This is used to |
135 // predict the duration of the next frame. | 153 // predict the duration of the next frame. |
136 base::TimeDelta last_frame_timestamp_; | 154 base::TimeDelta last_frame_timestamp_; |
137 | 155 |
138 DISALLOW_COPY_AND_ASSIGN(VpxEncoder); | 156 DISALLOW_COPY_AND_ASSIGN(VpxEncoder); |
139 }; | 157 }; |
140 | 158 |
| 159 class H264Encoder final : public VideoTrackRecorder::Encoder { |
| 160 public: |
| 161 static void ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread, |
| 162 scoped_ptr<ISVCEncoder> encoder); |
| 163 |
| 164 H264Encoder( |
| 165 const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback, |
| 166 int32_t bits_per_second); |
| 167 |
| 168 void StartFrameEncode(const scoped_refptr<VideoFrame>& frame, |
| 169 base::TimeTicks capture_timestamp) override; |
| 170 |
| 171 void set_paused(bool paused) override { paused_ = paused; } |
| 172 |
| 173 private: |
| 174 ~H264Encoder() override; |
| 175 |
| 176 void EncodeOnEncodingThread(const scoped_refptr<VideoFrame>& frame, |
| 177 base::TimeTicks capture_timestamp); |
| 178 |
| 179 void ConfigureEncoding(const gfx::Size& size); |
| 180 |
| 181 // While |paused_|, frames are not encoded. |
| 182 bool paused_; |
| 183 |
| 184 // This callback should be exercised on IO thread. |
| 185 const VideoTrackRecorder::OnEncodedVideoCB on_encoded_video_callback_; |
| 186 |
| 187 // Target bitrate or video encoding. If 0, a standard bitrate is used. |
| 188 const int32_t bits_per_second_; |
| 189 |
| 190 // Used to shutdown properly on the same thread we were created. |
| 191 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; |
| 192 |
| 193 // Task runner where frames to encode and reply callbacks must happen. |
| 194 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_; |
| 195 |
| 196 // Thread for encoding. Active for the lifetime of VpxEncoder. All variables |
| 197 // below this are used in this thread. |
| 198 std::unique_ptr<base::Thread> encoding_thread_; |
| 199 |
| 200 scoped_ptr<ISVCEncoder> openh264_encoder_; |
| 201 gfx::Size configured_size_; |
| 202 |
| 203 // The |VideoFrame::timestamp()| of the first received frame. |
| 204 base::TimeTicks first_frame_timestamp_; |
| 205 |
| 206 DISALLOW_COPY_AND_ASSIGN(H264Encoder); |
| 207 }; |
| 208 |
141 // static | 209 // static |
142 void VideoTrackRecorder::VpxEncoder::ShutdownEncoder( | 210 void H264Encoder::ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread, |
143 std::unique_ptr<base::Thread> encoding_thread, | 211 scoped_ptr<ISVCEncoder> encoder) { |
144 ScopedVpxCodecCtxPtr encoder) { | 212 DVLOG(1) << __FUNCTION__; |
| 213 DCHECK(encoding_thread->IsRunning()); |
| 214 encoding_thread->Stop(); |
| 215 |
| 216 if (encoder) { |
| 217 const int uninit_ret = encoder->Uninitialize(); |
| 218 DLOG_IF(ERROR, uninit_ret != 0) << "OpenH264 Uninitialize()"; |
| 219 |
| 220 WelsDestroySVCEncoder(encoder.release()); |
| 221 } |
| 222 } |
| 223 |
| 224 H264Encoder::H264Encoder( |
| 225 const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback, |
| 226 int32_t bits_per_second) |
| 227 : paused_(false), |
| 228 on_encoded_video_callback_(on_encoded_video_callback), |
| 229 bits_per_second_(bits_per_second), |
| 230 main_task_runner_(base::MessageLoop::current()->task_runner()), |
| 231 encoding_thread_(new base::Thread("EncodingThread")) { |
| 232 DCHECK(!on_encoded_video_callback_.is_null()); |
| 233 |
| 234 DCHECK(!encoding_thread_->IsRunning()); |
| 235 encoding_thread_->Start(); |
| 236 } |
| 237 |
| 238 void H264Encoder::StartFrameEncode(const scoped_refptr<VideoFrame>& frame, |
| 239 base::TimeTicks capture_timestamp) { |
| 240 DVLOG(1) << __FUNCTION__; |
| 241 // Cache the thread sending frames on first frame arrival. |
| 242 if (!origin_task_runner_.get()) |
| 243 origin_task_runner_ = base::MessageLoop::current()->task_runner(); |
| 244 DCHECK(origin_task_runner_->BelongsToCurrentThread()); |
| 245 if (paused_) |
| 246 return; |
| 247 encoding_thread_->task_runner()->PostTask( |
| 248 FROM_HERE, base::Bind(&H264Encoder::EncodeOnEncodingThread, |
| 249 this, frame, capture_timestamp)); |
| 250 } |
| 251 |
| 252 |
| 253 void H264Encoder::EncodeOnEncodingThread( |
| 254 const scoped_refptr<VideoFrame>& video_frame, |
| 255 base::TimeTicks capture_timestamp) { |
| 256 DVLOG(1) << __FUNCTION__; |
| 257 DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); |
| 258 |
| 259 if (!(video_frame->format() == media::PIXEL_FORMAT_I420 || |
| 260 video_frame->format() == media::PIXEL_FORMAT_YV12 || |
| 261 video_frame->format() == media::PIXEL_FORMAT_YV12A)) { |
| 262 NOTREACHED(); |
| 263 return; |
| 264 } |
| 265 scoped_refptr<media::VideoFrame> frame = video_frame; |
| 266 // Drop alpha channel since we do not support it yet. |
| 267 if (frame->format() == media::PIXEL_FORMAT_YV12A) |
| 268 frame = media::WrapAsI420VideoFrame(video_frame); |
| 269 |
| 270 const gfx::Size frame_size = frame->visible_rect().size(); |
| 271 if (!openh264_encoder_ || configured_size_ != frame_size) { |
| 272 ConfigureEncoding(frame_size); |
| 273 first_frame_timestamp_ = capture_timestamp; |
| 274 } |
| 275 |
| 276 DCHECK(openh264_encoder_); |
| 277 |
| 278 // EncodeFrame input. |
| 279 SSourcePicture picture = {}; |
| 280 picture.iPicWidth = frame_size.width(); |
| 281 picture.iPicHeight = frame_size.height(); |
| 282 picture.iColorFormat = EVideoFormatType::videoFormatI420; |
| 283 picture.uiTimeStamp = |
| 284 (capture_timestamp - first_frame_timestamp_).InMilliseconds(); |
| 285 picture.iStride[0] = video_frame->stride(VideoFrame::kYPlane); |
| 286 picture.iStride[1] = video_frame->stride(VideoFrame::kUPlane); |
| 287 picture.iStride[2] = video_frame->stride(VideoFrame::kVPlane); |
| 288 picture.pData[0] = video_frame->data(VideoFrame::kYPlane); |
| 289 picture.pData[1] = video_frame->data(VideoFrame::kUPlane); |
| 290 picture.pData[2] = video_frame->data(VideoFrame::kVPlane); |
| 291 |
| 292 // EncodeFrame output. |
| 293 SFrameBSInfo info; |
| 294 memset(&info, 0, sizeof(SFrameBSInfo)); |
| 295 |
| 296 // Encode! |
| 297 int enc_ret = openh264_encoder_->EncodeFrame(&picture, &info); |
| 298 if (enc_ret != cmResultSuccess) { |
| 299 DLOG(ERROR) << "OpenH264 encoding failed, EncodeFrame says " << enc_ret; |
| 300 NOTREACHED(); |
| 301 return; |
| 302 } |
| 303 |
| 304 std::unique_ptr<std::string> data(new std::string); |
| 305 const uint8_t kNALStartCode[4] = {0, 0, 0, 1}; |
| 306 for (int layer = 0; layer < info.iLayerNum; ++layer) { |
| 307 const SLayerBSInfo& layerInfo = info.sLayerInfo[layer]; |
| 308 // Iterate NAL units making up this layer, noting fragments. |
| 309 size_t layer_len = 0; |
| 310 for (int nal = 0; nal < layerInfo.iNalCount; ++nal) { |
| 311 |
| 312 DCHECK_GE(layerInfo.pNalLengthInByte[nal], 4); |
| 313 DCHECK_EQ(kNALStartCode[0], layerInfo.pBsBuf[layer_len+0]); |
| 314 DCHECK_EQ(kNALStartCode[1], layerInfo.pBsBuf[layer_len+1]); |
| 315 DCHECK_EQ(kNALStartCode[2], layerInfo.pBsBuf[layer_len+2]); |
| 316 DCHECK_EQ(kNALStartCode[3], layerInfo.pBsBuf[layer_len+3]); |
| 317 |
| 318 layer_len += layerInfo.pNalLengthInByte[nal]; |
| 319 } |
| 320 // Copy the entire layer's data (including start codes). |
| 321 data->append(reinterpret_cast<char*>(layerInfo.pBsBuf), layer_len); |
| 322 } |
| 323 |
| 324 const bool is_key_frame = info.eFrameType == videoFrameTypeIDR; |
| 325 |
| 326 origin_task_runner_->PostTask( |
| 327 FROM_HERE, |
| 328 base::Bind(OnFrameEncodeCompleted, on_encoded_video_callback_, frame, |
| 329 base::Passed(&data), capture_timestamp, is_key_frame)); |
| 330 } |
| 331 |
| 332 void H264Encoder::ConfigureEncoding(const gfx::Size& size) { |
| 333 DVLOG(1) << __FUNCTION__; |
| 334 configured_size_ = size; |
| 335 |
| 336 ISVCEncoder* temp_encoder = nullptr; |
| 337 int result = WelsCreateSVCEncoder(&temp_encoder); |
| 338 if (result != 0) { |
| 339 DLOG(ERROR) << "Failed to create OpenH264 encoder"; |
| 340 NOTREACHED(); |
| 341 return; |
| 342 } |
| 343 openh264_encoder_.reset(temp_encoder); |
| 344 |
| 345 #if DCHECK_IS_ON() |
| 346 int trace_level = WELS_LOG_DETAIL; |
| 347 openh264_encoder_->SetOption(ENCODER_OPTION_TRACE_LEVEL, &trace_level); |
| 348 #endif |
| 349 |
| 350 SEncParamExt init_params; |
| 351 openh264_encoder_->GetDefaultParams(&init_params); |
| 352 init_params.iUsageType = CAMERA_VIDEO_REAL_TIME; |
| 353 |
| 354 init_params.iPicWidth = size.width(); |
| 355 init_params.iPicHeight = size.height(); |
| 356 if (bits_per_second_ > 0) { |
| 357 init_params.iRCMode = RC_BITRATE_MODE; |
| 358 init_params.iTargetBitrate = bits_per_second_; |
| 359 } else { |
| 360 init_params.iRCMode = RC_OFF_MODE; |
| 361 } |
| 362 |
| 363 // Do not saturate CPU utilization just for encoding. On a lower-end system |
| 364 // with only 1 or 2 cores, use only one thread for encoding. On systems with |
| 365 // more cores, allow half of the cores to be used for encoding. |
| 366 init_params.iMultipleThreadIdc = |
| 367 std::min(8, (base::SysInfo::NumberOfProcessors() + 1) / 2); |
| 368 |
| 369 // The base spatial layer 0 is the only one we use. |
| 370 DCHECK_EQ(1, init_params.iSpatialLayerNum); |
| 371 init_params.sSpatialLayers[0].iVideoWidth = init_params.iPicWidth; |
| 372 init_params.sSpatialLayers[0].iVideoHeight = init_params.iPicHeight; |
| 373 init_params.sSpatialLayers[0].iSpatialBitrate = init_params.iTargetBitrate; |
| 374 |
| 375 result = openh264_encoder_->InitializeExt(&init_params); |
| 376 if (result != cmResultSuccess) { |
| 377 DLOG(ERROR) << "Failed to initialize OpenH264 encoder"; |
| 378 NOTREACHED(); |
| 379 return; |
| 380 } |
| 381 |
| 382 int video_format = EVideoFormatType::videoFormatI420; |
| 383 openh264_encoder_->SetOption(ENCODER_OPTION_DATAFORMAT, |
| 384 &video_format); |
| 385 } |
| 386 |
| 387 H264Encoder::~H264Encoder() { |
| 388 main_task_runner_->PostTask(FROM_HERE, |
| 389 base::Bind(&H264Encoder::ShutdownEncoder, |
| 390 base::Passed(&encoding_thread_), |
| 391 base::Passed(&openh264_encoder_))); |
| 392 } |
| 393 |
| 394 // |
| 395 // |
| 396 // static |
| 397 void VpxEncoder::ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread, |
| 398 ScopedVpxCodecCtxPtr encoder) { |
145 DCHECK(encoding_thread->IsRunning()); | 399 DCHECK(encoding_thread->IsRunning()); |
146 encoding_thread->Stop(); | 400 encoding_thread->Stop(); |
147 // Both |encoding_thread| and |encoder| will be destroyed at end-of-scope. | 401 // Both |encoding_thread| and |encoder| will be destroyed at end-of-scope. |
148 } | 402 } |
149 | 403 |
150 VideoTrackRecorder::VpxEncoder::VpxEncoder( | 404 VpxEncoder::VpxEncoder( |
151 bool use_vp9, | 405 bool use_vp9, |
152 const OnEncodedVideoCB& on_encoded_video_callback, | 406 const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback, |
153 int32_t bits_per_second) | 407 int32_t bits_per_second) |
154 : paused_(false), | 408 : paused_(false), |
155 use_vp9_(use_vp9), | 409 use_vp9_(use_vp9), |
156 on_encoded_video_callback_(on_encoded_video_callback), | 410 on_encoded_video_callback_(on_encoded_video_callback), |
157 bits_per_second_(bits_per_second), | 411 bits_per_second_(bits_per_second), |
158 main_task_runner_(base::MessageLoop::current()->task_runner()), | 412 main_task_runner_(base::MessageLoop::current()->task_runner()), |
159 encoding_thread_(new base::Thread("EncodingThread")) { | 413 encoding_thread_(new base::Thread("EncodingThread")) { |
160 DCHECK(!on_encoded_video_callback_.is_null()); | 414 DCHECK(!on_encoded_video_callback_.is_null()); |
161 | 415 |
162 codec_config_.g_timebase.den = 0; // Not initialized. | 416 codec_config_.g_timebase.den = 0; // Not initialized. |
163 | 417 |
164 DCHECK(!encoding_thread_->IsRunning()); | 418 DCHECK(!encoding_thread_->IsRunning()); |
165 encoding_thread_->Start(); | 419 encoding_thread_->Start(); |
166 } | 420 } |
167 | 421 |
168 VideoTrackRecorder::VpxEncoder::~VpxEncoder() { | 422 VpxEncoder::~VpxEncoder() { |
169 main_task_runner_->PostTask(FROM_HERE, | 423 main_task_runner_->PostTask(FROM_HERE, |
170 base::Bind(&VpxEncoder::ShutdownEncoder, | 424 base::Bind(&VpxEncoder::ShutdownEncoder, |
171 base::Passed(&encoding_thread_), | 425 base::Passed(&encoding_thread_), |
172 base::Passed(&encoder_))); | 426 base::Passed(&encoder_))); |
173 } | 427 } |
174 | 428 |
175 void VideoTrackRecorder::VpxEncoder::StartFrameEncode( | 429 void VpxEncoder::StartFrameEncode(const scoped_refptr<VideoFrame>& frame, |
176 const scoped_refptr<VideoFrame>& frame, | 430 base::TimeTicks capture_timestamp) { |
177 base::TimeTicks capture_timestamp) { | |
178 // Cache the thread sending frames on first frame arrival. | 431 // Cache the thread sending frames on first frame arrival. |
179 if (!origin_task_runner_.get()) | 432 if (!origin_task_runner_.get()) |
180 origin_task_runner_ = base::MessageLoop::current()->task_runner(); | 433 origin_task_runner_ = base::MessageLoop::current()->task_runner(); |
181 DCHECK(origin_task_runner_->BelongsToCurrentThread()); | 434 DCHECK(origin_task_runner_->BelongsToCurrentThread()); |
182 if (paused_) | 435 if (paused_) |
183 return; | 436 return; |
184 encoding_thread_->task_runner()->PostTask( | 437 encoding_thread_->task_runner()->PostTask( |
185 FROM_HERE, base::Bind(&VpxEncoder::EncodeOnEncodingThread, | 438 FROM_HERE, base::Bind(&VpxEncoder::EncodeOnEncodingThread, |
186 this, frame, capture_timestamp)); | 439 this, frame, capture_timestamp)); |
187 } | 440 } |
188 | 441 |
189 void VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread( | 442 void VpxEncoder::EncodeOnEncodingThread( |
190 const scoped_refptr<VideoFrame>& video_frame, | 443 const scoped_refptr<VideoFrame>& video_frame, |
191 base::TimeTicks capture_timestamp) { | 444 base::TimeTicks capture_timestamp) { |
192 TRACE_EVENT0("video", | 445 TRACE_EVENT0("video", |
193 "VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread"); | 446 "VpxEncoder::EncodeOnEncodingThread"); |
194 DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); | 447 DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); |
195 | 448 |
196 if (!(video_frame->format() == media::PIXEL_FORMAT_I420 || | 449 if (!(video_frame->format() == media::PIXEL_FORMAT_I420 || |
197 video_frame->format() == media::PIXEL_FORMAT_YV12 || | 450 video_frame->format() == media::PIXEL_FORMAT_YV12 || |
198 video_frame->format() == media::PIXEL_FORMAT_YV12A)) { | 451 video_frame->format() == media::PIXEL_FORMAT_YV12A)) { |
199 NOTREACHED(); | 452 NOTREACHED(); |
200 return; | 453 return; |
201 } | 454 } |
202 scoped_refptr<media::VideoFrame> frame = video_frame; | 455 scoped_refptr<media::VideoFrame> frame = video_frame; |
203 // Drop alpha channel since we do not support it yet. | 456 // Drop alpha channel since we do not support it yet. |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
252 } | 505 } |
253 origin_task_runner_->PostTask(FROM_HERE, | 506 origin_task_runner_->PostTask(FROM_HERE, |
254 base::Bind(OnFrameEncodeCompleted, | 507 base::Bind(OnFrameEncodeCompleted, |
255 on_encoded_video_callback_, | 508 on_encoded_video_callback_, |
256 frame, | 509 frame, |
257 base::Passed(&data), | 510 base::Passed(&data), |
258 capture_timestamp, | 511 capture_timestamp, |
259 keyframe)); | 512 keyframe)); |
260 } | 513 } |
261 | 514 |
262 void VideoTrackRecorder::VpxEncoder::ConfigureEncoding(const gfx::Size& size) { | 515 void VpxEncoder::ConfigureEncoding(const gfx::Size& size) { |
263 if (IsInitialized()) { | 516 if (IsInitialized()) { |
264 // TODO(mcasas) VP8 quirk/optimisation: If the new |size| is strictly less- | 517 // TODO(mcasas) VP8 quirk/optimisation: If the new |size| is strictly less- |
265 // than-or-equal than the old size, in terms of area, the existing encoder | 518 // than-or-equal than the old size, in terms of area, the existing encoder |
266 // instance could be reused after changing |codec_config_.{g_w,g_h}|. | 519 // instance could be reused after changing |codec_config_.{g_w,g_h}|. |
267 DVLOG(1) << "Destroying/Re-Creating encoder for new frame size: " | 520 DVLOG(1) << "Destroying/Re-Creating encoder for new frame size: " |
268 << gfx::Size(codec_config_.g_w, codec_config_.g_h).ToString() | 521 << gfx::Size(codec_config_.g_w, codec_config_.g_h).ToString() |
269 << " --> " << size.ToString() << (use_vp9_ ? " vp9" : " vp8"); | 522 << " --> " << size.ToString() << (use_vp9_ ? " vp9" : " vp8"); |
270 encoder_.reset(); | 523 encoder_.reset(); |
271 } | 524 } |
272 | 525 |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
344 // the expense of quality up to a maximum value of 8 for VP9, by tuning the | 597 // the expense of quality up to a maximum value of 8 for VP9, by tuning the |
345 // target time spent encoding the frame. Go from 8 to 5 (values for real | 598 // target time spent encoding the frame. Go from 8 to 5 (values for real |
346 // time encoding) depending on the amount of cores available in the system. | 599 // time encoding) depending on the amount of cores available in the system. |
347 const int kCpuUsed = | 600 const int kCpuUsed = |
348 std::max(5, 8 - base::SysInfo::NumberOfProcessors() / 2); | 601 std::max(5, 8 - base::SysInfo::NumberOfProcessors() / 2); |
349 result = vpx_codec_control(encoder_.get(), VP8E_SET_CPUUSED, kCpuUsed); | 602 result = vpx_codec_control(encoder_.get(), VP8E_SET_CPUUSED, kCpuUsed); |
350 DLOG_IF(WARNING, VPX_CODEC_OK != result) << "VP8E_SET_CPUUSED failed"; | 603 DLOG_IF(WARNING, VPX_CODEC_OK != result) << "VP8E_SET_CPUUSED failed"; |
351 } | 604 } |
352 } | 605 } |
353 | 606 |
354 bool VideoTrackRecorder::VpxEncoder::IsInitialized() const { | 607 bool VpxEncoder::IsInitialized() const { |
355 DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); | 608 DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); |
356 return codec_config_.g_timebase.den != 0; | 609 return codec_config_.g_timebase.den != 0; |
357 } | 610 } |
358 | 611 |
359 base::TimeDelta VideoTrackRecorder::VpxEncoder::CalculateFrameDuration( | 612 base::TimeDelta VpxEncoder::CalculateFrameDuration( |
360 const scoped_refptr<VideoFrame>& frame) { | 613 const scoped_refptr<VideoFrame>& frame) { |
361 DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); | 614 DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); |
362 | 615 |
363 using base::TimeDelta; | 616 using base::TimeDelta; |
364 TimeDelta predicted_frame_duration; | 617 TimeDelta predicted_frame_duration; |
365 if (!frame->metadata()->GetTimeDelta(VideoFrameMetadata::FRAME_DURATION, | 618 if (!frame->metadata()->GetTimeDelta(VideoFrameMetadata::FRAME_DURATION, |
366 &predicted_frame_duration) || | 619 &predicted_frame_duration) || |
367 predicted_frame_duration <= TimeDelta()) { | 620 predicted_frame_duration <= TimeDelta()) { |
368 // The source of the video frame did not provide the frame duration. Use | 621 // The source of the video frame did not provide the frame duration. Use |
369 // the actual amount of time between the current and previous frame as a | 622 // the actual amount of time between the current and previous frame as a |
370 // prediction for the next frame's duration. | 623 // prediction for the next frame's duration. |
371 // TODO(mcasas): This duration estimation could lead to artifacts if the | 624 // TODO(mcasas): This duration estimation could lead to artifacts if the |
372 // cadence of the received stream is compromised (e.g. camera freeze, pause, | 625 // cadence of the received stream is compromised (e.g. camera freeze, pause, |
373 // remote packet loss). Investigate using GetFrameRate() in this case. | 626 // remote packet loss). Investigate using GetFrameRate() in this case. |
374 predicted_frame_duration = frame->timestamp() - last_frame_timestamp_; | 627 predicted_frame_duration = frame->timestamp() - last_frame_timestamp_; |
375 } | 628 } |
376 last_frame_timestamp_ = frame->timestamp(); | 629 last_frame_timestamp_ = frame->timestamp(); |
377 // Make sure |predicted_frame_duration| is in a safe range of values. | 630 // Make sure |predicted_frame_duration| is in a safe range of values. |
378 const TimeDelta kMaxFrameDuration = TimeDelta::FromSecondsD(1.0 / 8); | 631 const TimeDelta kMaxFrameDuration = TimeDelta::FromSecondsD(1.0 / 8); |
379 const TimeDelta kMinFrameDuration = TimeDelta::FromMilliseconds(1); | 632 const TimeDelta kMinFrameDuration = TimeDelta::FromMilliseconds(1); |
380 return std::min(kMaxFrameDuration, std::max(predicted_frame_duration, | 633 return std::min(kMaxFrameDuration, std::max(predicted_frame_duration, |
381 kMinFrameDuration)); | 634 kMinFrameDuration)); |
382 } | 635 } |
383 | 636 |
| 637 } // anonymous namespace |
| 638 |
384 VideoTrackRecorder::VideoTrackRecorder( | 639 VideoTrackRecorder::VideoTrackRecorder( |
385 bool use_vp9, | 640 CodecId codec, |
386 const blink::WebMediaStreamTrack& track, | 641 const blink::WebMediaStreamTrack& track, |
387 const OnEncodedVideoCB& on_encoded_video_callback, | 642 const OnEncodedVideoCB& on_encoded_video_callback, |
388 int32_t bits_per_second) | 643 int32_t bits_per_second) |
389 : track_(track), | 644 : track_(track) { |
390 encoder_( | |
391 new VpxEncoder(use_vp9, on_encoded_video_callback, bits_per_second)) { | |
392 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 645 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
393 DCHECK(!track_.isNull()); | 646 DCHECK(!track_.isNull()); |
394 DCHECK(track_.getExtraData()); | 647 DCHECK(track_.getExtraData()); |
395 | 648 |
| 649 switch (codec) { |
| 650 case CodecId::H264: |
| 651 encoder_ = new H264Encoder(on_encoded_video_callback, bits_per_second); |
| 652 break; |
| 653 case CodecId::VP8: |
| 654 case CodecId::VP9: |
| 655 encoder_ = new VpxEncoder(codec == CodecId::VP9, |
| 656 on_encoded_video_callback, bits_per_second); |
| 657 break; |
| 658 default: |
| 659 NOTREACHED(); |
| 660 } |
| 661 |
396 // StartFrameEncode() will be called on Render IO thread. | 662 // StartFrameEncode() will be called on Render IO thread. |
397 MediaStreamVideoSink::ConnectToTrack( | 663 MediaStreamVideoSink::ConnectToTrack( |
398 track_, | 664 track_, |
399 base::Bind(&VideoTrackRecorder::VpxEncoder::StartFrameEncode, encoder_)); | 665 base::Bind(&VideoTrackRecorder::Encoder::StartFrameEncode, encoder_)); |
400 } | 666 } |
401 | 667 |
402 VideoTrackRecorder::~VideoTrackRecorder() { | 668 VideoTrackRecorder::~VideoTrackRecorder() { |
403 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 669 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
404 MediaStreamVideoSink::DisconnectFromTrack(); | 670 MediaStreamVideoSink::DisconnectFromTrack(); |
405 track_.reset(); | 671 track_.reset(); |
406 } | 672 } |
407 | 673 |
408 void VideoTrackRecorder::Pause() { | 674 void VideoTrackRecorder::Pause() { |
409 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 675 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
410 DCHECK(encoder_); | 676 DCHECK(encoder_); |
411 encoder_->set_paused(true); | 677 encoder_->set_paused(true); |
412 } | 678 } |
413 | 679 |
414 void VideoTrackRecorder::Resume() { | 680 void VideoTrackRecorder::Resume() { |
415 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 681 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
416 DCHECK(encoder_); | 682 DCHECK(encoder_); |
417 encoder_->set_paused(false); | 683 encoder_->set_paused(false); |
418 } | 684 } |
419 | 685 |
420 void VideoTrackRecorder::OnVideoFrameForTesting( | 686 void VideoTrackRecorder::OnVideoFrameForTesting( |
421 const scoped_refptr<media::VideoFrame>& frame, | 687 const scoped_refptr<media::VideoFrame>& frame, |
422 base::TimeTicks timestamp) { | 688 base::TimeTicks timestamp) { |
423 encoder_->StartFrameEncode(frame, timestamp); | 689 encoder_->StartFrameEncode(frame, timestamp); |
424 } | 690 } |
425 | 691 |
426 } // namespace content | 692 } // namespace content |
OLD | NEW |