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 "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/sys_info.h" | 9 #include "base/sys_info.h" |
10 #include "base/threading/thread.h" | 10 #include "base/threading/thread.h" |
11 #include "base/time/time.h" | 11 #include "base/time/time.h" |
12 #include "base/trace_event/trace_event.h" | 12 #include "base/trace_event/trace_event.h" |
13 #include "content/child/child_process.h" | |
14 #include "media/base/bind_to_current_loop.h" | |
15 #include "media/base/video_frame.h" | 13 #include "media/base/video_frame.h" |
16 | 14 |
17 extern "C" { | 15 extern "C" { |
18 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide | 16 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide |
19 // backwards compatibility for legacy applications using the library. | 17 // backwards compatibility for legacy applications using the library. |
20 #define VPX_CODEC_DISABLE_COMPAT 1 | 18 #define VPX_CODEC_DISABLE_COMPAT 1 |
21 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" | 19 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" |
22 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h" | 20 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h" |
23 } | 21 } |
24 | 22 |
25 using media::VideoFrame; | 23 using media::VideoFrame; |
26 using media::VideoFrameMetadata; | 24 using media::VideoFrameMetadata; |
27 | 25 |
28 namespace content { | 26 namespace content { |
29 | 27 |
30 namespace { | 28 namespace { |
| 29 |
31 const vpx_codec_flags_t kNoFlags = 0; | 30 const vpx_codec_flags_t kNoFlags = 0; |
32 | 31 |
| 32 // Originally from remoting/codec/scoped_vpx_codec.h. |
| 33 // TODO(mcasas): Refactor into a common location. |
| 34 struct VpxCodecDeleter { |
| 35 void operator()(vpx_codec_ctx_t* codec) { |
| 36 if (!codec) |
| 37 return; |
| 38 vpx_codec_err_t ret = vpx_codec_destroy(codec); |
| 39 CHECK_EQ(ret, VPX_CODEC_OK); |
| 40 delete codec; |
| 41 } |
| 42 }; |
| 43 |
| 44 typedef scoped_ptr<vpx_codec_ctx_t, VpxCodecDeleter> ScopedVpxCodecCtxPtr; |
| 45 |
33 void OnFrameEncodeCompleted( | 46 void OnFrameEncodeCompleted( |
34 const content::VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_cb, | 47 const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_cb, |
35 const scoped_refptr<VideoFrame>& frame, | 48 const scoped_refptr<VideoFrame>& frame, |
36 scoped_ptr<std::string> data, | 49 scoped_ptr<std::string> data, |
37 base::TimeTicks capture_timestamp, | 50 base::TimeTicks capture_timestamp, |
38 bool keyframe) { | 51 bool keyframe) { |
39 DVLOG(1) << (keyframe ? "" : "non ") << "keyframe " | 52 DVLOG(1) << (keyframe ? "" : "non ") << "keyframe "<< data->length() << "B, " |
40 << capture_timestamp << " ms - " << data->length() << "B "; | 53 << capture_timestamp << " ms"; |
41 on_encoded_video_cb.Run(frame, base::StringPiece(*data), capture_timestamp, | 54 on_encoded_video_cb.Run(frame, base::StringPiece(*data), capture_timestamp, |
42 keyframe); | 55 keyframe); |
43 } | 56 } |
44 | 57 |
45 } // anonymous namespace | 58 } // anonymous namespace |
46 | 59 |
47 // Inner class encapsulating all libvpx interactions and the encoding+delivery | 60 // Inner class encapsulating all libvpx interactions and the encoding+delivery |
48 // of received frames. This class is: | 61 // of received frames. Limitation: Only VP8 is supported for the time being. |
49 // - created and destroyed on its parent's thread (usually the main render | 62 // This class must be ref-counted because the MediaStreamVideoTrack will hold a |
50 // thread), | 63 // reference to it, via the callback MediaStreamVideoSink passes along, and it's |
51 // - receives VideoFrames and Run()s the callbacks on another thread (supposedly | 64 // unknown when exactly it will release that reference. This class: |
52 // the render IO thread), which is cached on first frame arrival, | 65 // - is created and destroyed on its parent's thread (usually the main Render |
| 66 // thread); |
| 67 // - receives VideoFrames and Run()s the callbacks on |origin_task_runner_|, |
| 68 // which is cached on first frame arrival, and is supposed to be the render IO |
| 69 // thread, but this is not enforced; |
53 // - uses an internal |encoding_thread_| for libvpx interactions, notably for | 70 // - uses an internal |encoding_thread_| for libvpx interactions, notably for |
54 // encoding (which might take some time). | 71 // encoding (which might take some time). |
55 // Only VP8 is supported for the time being. | 72 class VideoTrackRecorder::VpxEncoder final |
56 class VideoTrackRecorder::VpxEncoder final { | 73 : public base::RefCountedThreadSafe<VpxEncoder> { |
57 public: | 74 public: |
| 75 static void ShutdownEncoder(scoped_ptr<base::Thread> encoding_thread, |
| 76 ScopedVpxCodecCtxPtr encoder); |
| 77 |
58 explicit VpxEncoder(const OnEncodedVideoCB& on_encoded_video_callback); | 78 explicit VpxEncoder(const OnEncodedVideoCB& on_encoded_video_callback); |
59 ~VpxEncoder(); | |
60 | 79 |
61 void StartFrameEncode(const scoped_refptr<VideoFrame>& frame, | 80 void StartFrameEncode(const scoped_refptr<VideoFrame>& frame, |
62 base::TimeTicks capture_timestamp); | 81 base::TimeTicks capture_timestamp); |
63 | 82 |
64 private: | 83 private: |
| 84 friend class base::RefCountedThreadSafe<VpxEncoder>; |
| 85 ~VpxEncoder(); |
| 86 |
65 void EncodeOnEncodingThread(const scoped_refptr<VideoFrame>& frame, | 87 void EncodeOnEncodingThread(const scoped_refptr<VideoFrame>& frame, |
66 base::TimeTicks capture_timestamp); | 88 base::TimeTicks capture_timestamp); |
67 | 89 |
68 void ConfigureVp8Encoding(const gfx::Size& size); | 90 void ConfigureVp8Encoding(const gfx::Size& size); |
69 | 91 |
70 // Returns true if |codec_config_| has been filled in at least once. | 92 // Returns true if |codec_config_| has been filled in at least once. |
71 bool IsInitialized() const; | 93 bool IsInitialized() const; |
72 | 94 |
73 // Estimate the frame duration from |frame| and |last_frame_timestamp_|. | 95 // Estimate the frame duration from |frame| and |last_frame_timestamp_|. |
74 base::TimeDelta CalculateFrameDuration( | 96 base::TimeDelta CalculateFrameDuration( |
75 const scoped_refptr<VideoFrame>& frame); | 97 const scoped_refptr<VideoFrame>& frame); |
76 | 98 |
77 // Used to check that we are destroyed on the same thread we were created. | 99 // Used to shutdown properly on the same thread we were created. |
78 base::ThreadChecker main_render_thread_checker_; | 100 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; |
79 | 101 |
80 // Task runner where frames to encode and reply callbacks must happen. | 102 // Task runner where frames to encode and reply callbacks must happen. |
81 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_; | 103 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_; |
82 | 104 |
83 // This callback should be exercised on IO thread. | 105 // This callback should be exercised on IO thread. |
84 const OnEncodedVideoCB on_encoded_video_callback_; | 106 const OnEncodedVideoCB on_encoded_video_callback_; |
85 | 107 |
86 // Thread for encoding. Active as long as VpxEncoder exists. All variables | 108 // Thread for encoding. Active for the lifetime of VpxEncoder. All variables |
87 // below this are used in this thread. | 109 // below this are used in this thread. |
88 base::Thread encoding_thread_; | 110 scoped_ptr<base::Thread> encoding_thread_; |
89 // VP8 internal objects: configuration, encoder and Vpx Image wrapper. | 111 // VP8 internal objects: configuration and encoder. |
90 vpx_codec_enc_cfg_t codec_config_; | 112 vpx_codec_enc_cfg_t codec_config_; |
91 vpx_codec_ctx_t encoder_; | 113 // |encoder_| is a special scoped pointer to guarantee proper destruction. |
| 114 // Again, it should only be accessed on |encoding_thread_|. |
| 115 ScopedVpxCodecCtxPtr encoder_; |
92 | 116 |
93 // The |VideoFrame::timestamp()| of the last encoded frame. This is used to | 117 // The |VideoFrame::timestamp()| of the last encoded frame. This is used to |
94 // predict the duration of the next frame. | 118 // predict the duration of the next frame. |
95 base::TimeDelta last_frame_timestamp_; | 119 base::TimeDelta last_frame_timestamp_; |
96 | 120 |
97 DISALLOW_COPY_AND_ASSIGN(VpxEncoder); | 121 DISALLOW_COPY_AND_ASSIGN(VpxEncoder); |
98 }; | 122 }; |
99 | 123 |
| 124 // static |
| 125 void VideoTrackRecorder::VpxEncoder::ShutdownEncoder( |
| 126 scoped_ptr<base::Thread> encoding_thread, |
| 127 ScopedVpxCodecCtxPtr encoder) { |
| 128 DCHECK(encoding_thread->IsRunning()); |
| 129 encoding_thread->Stop(); |
| 130 // Both |encoding_thread| and |encoder| will be destroyed at end-of-scope. |
| 131 } |
| 132 |
100 VideoTrackRecorder::VpxEncoder::VpxEncoder( | 133 VideoTrackRecorder::VpxEncoder::VpxEncoder( |
101 const OnEncodedVideoCB& on_encoded_video_callback) | 134 const OnEncodedVideoCB& on_encoded_video_callback) |
102 : on_encoded_video_callback_(on_encoded_video_callback), | 135 : main_task_runner_(base::MessageLoop::current()->task_runner()), |
103 encoding_thread_("EncodingThread") { | 136 on_encoded_video_callback_(on_encoded_video_callback), |
| 137 encoding_thread_(new base::Thread("EncodingThread")) { |
104 DCHECK(!on_encoded_video_callback_.is_null()); | 138 DCHECK(!on_encoded_video_callback_.is_null()); |
105 | 139 |
106 codec_config_.g_timebase.den = 0; // Not initialized. | 140 codec_config_.g_timebase.den = 0; // Not initialized. |
107 | 141 |
108 DCHECK(!encoding_thread_.IsRunning()); | 142 DCHECK(!encoding_thread_->IsRunning()); |
109 encoding_thread_.Start(); | 143 encoding_thread_->Start(); |
110 } | 144 } |
111 | 145 |
112 VideoTrackRecorder::VpxEncoder::~VpxEncoder() { | 146 VideoTrackRecorder::VpxEncoder::~VpxEncoder() { |
113 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 147 main_task_runner_->PostTask(FROM_HERE, |
114 DCHECK(encoding_thread_.IsRunning()); | 148 base::Bind(&VpxEncoder::ShutdownEncoder, |
115 encoding_thread_.Stop(); | 149 base::Passed(&encoding_thread_), |
116 vpx_codec_destroy(&encoder_); | 150 base::Passed(&encoder_))); |
117 } | 151 } |
118 | 152 |
119 void VideoTrackRecorder::VpxEncoder::StartFrameEncode( | 153 void VideoTrackRecorder::VpxEncoder::StartFrameEncode( |
120 const scoped_refptr<VideoFrame>& frame, | 154 const scoped_refptr<VideoFrame>& frame, |
121 base::TimeTicks capture_timestamp) { | 155 base::TimeTicks capture_timestamp) { |
122 // Cache the thread sending frames on first frame arrival. | 156 // Cache the thread sending frames on first frame arrival. |
123 if (!origin_task_runner_.get()) | 157 if (!origin_task_runner_.get()) |
124 origin_task_runner_ = base::MessageLoop::current()->task_runner(); | 158 origin_task_runner_ = base::MessageLoop::current()->task_runner(); |
125 DCHECK(origin_task_runner_->BelongsToCurrentThread()); | 159 DCHECK(origin_task_runner_->BelongsToCurrentThread()); |
126 | 160 |
127 encoding_thread_.task_runner()->PostTask( | 161 encoding_thread_->task_runner()->PostTask( |
128 FROM_HERE, base::Bind(&VpxEncoder::EncodeOnEncodingThread, | 162 FROM_HERE, base::Bind(&VpxEncoder::EncodeOnEncodingThread, |
129 base::Unretained(this), frame, capture_timestamp)); | 163 this, frame, capture_timestamp)); |
130 } | 164 } |
131 | 165 |
132 void VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread( | 166 void VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread( |
133 const scoped_refptr<VideoFrame>& frame, | 167 const scoped_refptr<VideoFrame>& frame, |
134 base::TimeTicks capture_timestamp) { | 168 base::TimeTicks capture_timestamp) { |
135 TRACE_EVENT0("video", | 169 TRACE_EVENT0("video", |
136 "VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread"); | 170 "VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread"); |
137 DCHECK(encoding_thread_.task_runner()->BelongsToCurrentThread()); | 171 DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); |
138 | 172 |
139 const gfx::Size frame_size = frame->visible_rect().size(); | 173 const gfx::Size frame_size = frame->visible_rect().size(); |
140 if (!IsInitialized() || | 174 if (!IsInitialized() || |
141 gfx::Size(codec_config_.g_w, codec_config_.g_h) != frame_size) { | 175 gfx::Size(codec_config_.g_w, codec_config_.g_h) != frame_size) { |
142 ConfigureVp8Encoding(frame_size); | 176 ConfigureVp8Encoding(frame_size); |
143 } | 177 } |
144 | 178 |
145 vpx_image_t vpx_image; | 179 vpx_image_t vpx_image; |
146 vpx_image_t* const result = vpx_img_wrap(&vpx_image, | 180 vpx_image_t* const result = vpx_img_wrap(&vpx_image, |
147 VPX_IMG_FMT_I420, | 181 VPX_IMG_FMT_I420, |
148 frame_size.width(), | 182 frame_size.width(), |
149 frame_size.height(), | 183 frame_size.height(), |
150 1 /* align */, | 184 1 /* align */, |
151 frame->data(VideoFrame::kYPlane)); | 185 frame->data(VideoFrame::kYPlane)); |
152 DCHECK_EQ(result, &vpx_image); | 186 DCHECK_EQ(result, &vpx_image); |
153 vpx_image.planes[VPX_PLANE_Y] = frame->visible_data(VideoFrame::kYPlane); | 187 vpx_image.planes[VPX_PLANE_Y] = frame->visible_data(VideoFrame::kYPlane); |
154 vpx_image.planes[VPX_PLANE_U] = frame->visible_data(VideoFrame::kUPlane); | 188 vpx_image.planes[VPX_PLANE_U] = frame->visible_data(VideoFrame::kUPlane); |
155 vpx_image.planes[VPX_PLANE_V] = frame->visible_data(VideoFrame::kVPlane); | 189 vpx_image.planes[VPX_PLANE_V] = frame->visible_data(VideoFrame::kVPlane); |
156 vpx_image.stride[VPX_PLANE_Y] = frame->stride(VideoFrame::kYPlane); | 190 vpx_image.stride[VPX_PLANE_Y] = frame->stride(VideoFrame::kYPlane); |
157 vpx_image.stride[VPX_PLANE_U] = frame->stride(VideoFrame::kUPlane); | 191 vpx_image.stride[VPX_PLANE_U] = frame->stride(VideoFrame::kUPlane); |
158 vpx_image.stride[VPX_PLANE_V] = frame->stride(VideoFrame::kVPlane); | 192 vpx_image.stride[VPX_PLANE_V] = frame->stride(VideoFrame::kVPlane); |
159 | 193 |
160 const base::TimeDelta duration = CalculateFrameDuration(frame); | 194 const base::TimeDelta duration = CalculateFrameDuration(frame); |
161 // Encode the frame. The presentation time stamp argument here is fixed to | 195 // Encode the frame. The presentation time stamp argument here is fixed to |
162 // zero to force the encoder to base its single-frame bandwidth calculations | 196 // zero to force the encoder to base its single-frame bandwidth calculations |
163 // entirely on |predicted_frame_duration|. | 197 // entirely on |predicted_frame_duration|. |
164 const vpx_codec_err_t ret = vpx_codec_encode(&encoder_, | 198 const vpx_codec_err_t ret = vpx_codec_encode(encoder_.get(), |
165 &vpx_image, | 199 &vpx_image, |
166 0 /* pts */, | 200 0 /* pts */, |
167 duration.InMicroseconds(), | 201 duration.InMicroseconds(), |
168 kNoFlags, | 202 kNoFlags, |
169 VPX_DL_REALTIME); | 203 VPX_DL_REALTIME); |
170 DCHECK_EQ(ret, VPX_CODEC_OK) << vpx_codec_err_to_string(ret) << ", #" | 204 DCHECK_EQ(ret, VPX_CODEC_OK) << vpx_codec_err_to_string(ret) << ", #" |
171 << vpx_codec_error(&encoder_) << " -" | 205 << vpx_codec_error(encoder_.get()) << " -" |
172 << vpx_codec_error_detail(&encoder_); | 206 << vpx_codec_error_detail(encoder_.get()); |
173 | 207 |
174 scoped_ptr<std::string> data(new std::string); | 208 scoped_ptr<std::string> data(new std::string); |
175 bool keyframe = false; | 209 bool keyframe = false; |
176 vpx_codec_iter_t iter = NULL; | 210 vpx_codec_iter_t iter = NULL; |
177 const vpx_codec_cx_pkt_t* pkt = NULL; | 211 const vpx_codec_cx_pkt_t* pkt = NULL; |
178 while ((pkt = vpx_codec_get_cx_data(&encoder_, &iter)) != NULL) { | 212 while ((pkt = vpx_codec_get_cx_data(encoder_.get(), &iter)) != NULL) { |
179 if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) | 213 if (pkt->kind != VPX_CODEC_CX_FRAME_PKT) |
180 continue; | 214 continue; |
181 data->assign(static_cast<char*>(pkt->data.frame.buf), pkt->data.frame.sz); | 215 data->assign(static_cast<char*>(pkt->data.frame.buf), pkt->data.frame.sz); |
182 keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0; | 216 keyframe = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0; |
183 break; | 217 break; |
184 } | 218 } |
185 origin_task_runner_->PostTask(FROM_HERE, | 219 origin_task_runner_->PostTask(FROM_HERE, |
186 base::Bind(&OnFrameEncodeCompleted, | 220 base::Bind(OnFrameEncodeCompleted, |
187 on_encoded_video_callback_, | 221 on_encoded_video_callback_, |
188 frame, | 222 frame, |
189 base::Passed(&data), | 223 base::Passed(&data), |
190 capture_timestamp, | 224 capture_timestamp, |
191 keyframe)); | 225 keyframe)); |
192 } | 226 } |
193 | 227 |
194 void VideoTrackRecorder::VpxEncoder::ConfigureVp8Encoding( | 228 void VideoTrackRecorder::VpxEncoder::ConfigureVp8Encoding( |
195 const gfx::Size& size) { | 229 const gfx::Size& size) { |
196 if (IsInitialized()) { | 230 if (IsInitialized()) { |
197 // TODO(mcasas) VP8 quirk/optimisation: If the new |size| is strictly less- | 231 // TODO(mcasas) VP8 quirk/optimisation: If the new |size| is strictly less- |
198 // than-or-equal than the old size, in terms of area, the existing encoder | 232 // than-or-equal than the old size, in terms of area, the existing encoder |
199 // instance could be reused after changing |codec_config_.{g_w,g_h}|. | 233 // instance could be reused after changing |codec_config_.{g_w,g_h}|. |
200 DVLOG(1) << "Destroying/Re-Creating encoder for new frame size: " | 234 DVLOG(1) << "Destroying/Re-Creating encoder for new frame size: " |
201 << gfx::Size(codec_config_.g_w, codec_config_.g_h).ToString() | 235 << gfx::Size(codec_config_.g_w, codec_config_.g_h).ToString() |
202 << " --> " << size.ToString(); | 236 << " --> " << size.ToString(); |
203 vpx_codec_destroy(&encoder_); | 237 encoder_.reset(); |
204 } | 238 } |
205 | 239 |
206 const vpx_codec_iface_t* interface = vpx_codec_vp8_cx(); | 240 const vpx_codec_iface_t* interface = vpx_codec_vp8_cx(); |
207 const vpx_codec_err_t result = vpx_codec_enc_config_default(interface, | 241 const vpx_codec_err_t result = vpx_codec_enc_config_default(interface, |
208 &codec_config_, | 242 &codec_config_, |
209 0 /* reserved */); | 243 0 /* reserved */); |
210 DCHECK_EQ(VPX_CODEC_OK, result); | 244 DCHECK_EQ(VPX_CODEC_OK, result); |
211 | 245 |
212 // Adjust default bit rate to account for the actual size. | 246 // Adjust default bit rate to account for the actual size. |
213 DCHECK_EQ(320u, codec_config_.g_w); | 247 DCHECK_EQ(320u, codec_config_.g_w); |
(...skipping 25 matching lines...) Expand all Loading... |
239 | 273 |
240 // Do not saturate CPU utilization just for encoding. On a lower-end system | 274 // Do not saturate CPU utilization just for encoding. On a lower-end system |
241 // with only 1 or 2 cores, use only one thread for encoding. On systems with | 275 // with only 1 or 2 cores, use only one thread for encoding. On systems with |
242 // more cores, allow half of the cores to be used for encoding. | 276 // more cores, allow half of the cores to be used for encoding. |
243 codec_config_.g_threads = | 277 codec_config_.g_threads = |
244 std::min(8, (base::SysInfo::NumberOfProcessors() + 1) / 2); | 278 std::min(8, (base::SysInfo::NumberOfProcessors() + 1) / 2); |
245 | 279 |
246 // Number of frames to consume before producing output. | 280 // Number of frames to consume before producing output. |
247 codec_config_.g_lag_in_frames = 0; | 281 codec_config_.g_lag_in_frames = 0; |
248 | 282 |
249 const vpx_codec_err_t ret = vpx_codec_enc_init(&encoder_, interface, | 283 DCHECK(!encoder_); |
| 284 encoder_.reset(new vpx_codec_ctx_t); |
| 285 const vpx_codec_err_t ret = vpx_codec_enc_init(encoder_.get(), interface, |
250 &codec_config_, kNoFlags); | 286 &codec_config_, kNoFlags); |
251 DCHECK_EQ(VPX_CODEC_OK, ret); | 287 DCHECK_EQ(VPX_CODEC_OK, ret); |
252 } | 288 } |
253 | 289 |
254 bool VideoTrackRecorder::VpxEncoder::IsInitialized() const { | 290 bool VideoTrackRecorder::VpxEncoder::IsInitialized() const { |
255 DCHECK(encoding_thread_.task_runner()->BelongsToCurrentThread()); | 291 DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); |
256 return codec_config_.g_timebase.den != 0; | 292 return codec_config_.g_timebase.den != 0; |
257 } | 293 } |
258 | 294 |
259 base::TimeDelta VideoTrackRecorder::VpxEncoder::CalculateFrameDuration( | 295 base::TimeDelta VideoTrackRecorder::VpxEncoder::CalculateFrameDuration( |
260 const scoped_refptr<VideoFrame>& frame) { | 296 const scoped_refptr<VideoFrame>& frame) { |
261 DCHECK(encoding_thread_.task_runner()->BelongsToCurrentThread()); | 297 DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); |
262 | 298 |
263 using base::TimeDelta; | 299 using base::TimeDelta; |
264 TimeDelta predicted_frame_duration; | 300 TimeDelta predicted_frame_duration; |
265 if (!frame->metadata()->GetTimeDelta(VideoFrameMetadata::FRAME_DURATION, | 301 if (!frame->metadata()->GetTimeDelta(VideoFrameMetadata::FRAME_DURATION, |
266 &predicted_frame_duration) || | 302 &predicted_frame_duration) || |
267 predicted_frame_duration <= TimeDelta()) { | 303 predicted_frame_duration <= TimeDelta()) { |
268 // The source of the video frame did not provide the frame duration. Use | 304 // The source of the video frame did not provide the frame duration. Use |
269 // the actual amount of time between the current and previous frame as a | 305 // the actual amount of time between the current and previous frame as a |
270 // prediction for the next frame's duration. | 306 // prediction for the next frame's duration. |
271 // TODO(mcasas): This duration estimation could lead to artifacts if the | 307 // TODO(mcasas): This duration estimation could lead to artifacts if the |
272 // cadence of the received stream is compromised (e.g. camera freeze, pause, | 308 // cadence of the received stream is compromised (e.g. camera freeze, pause, |
273 // remote packet loss). Investigate using GetFrameRate() in this case. | 309 // remote packet loss). Investigate using GetFrameRate() in this case. |
274 predicted_frame_duration = frame->timestamp() - last_frame_timestamp_; | 310 predicted_frame_duration = frame->timestamp() - last_frame_timestamp_; |
275 } | 311 } |
276 last_frame_timestamp_ = frame->timestamp(); | 312 last_frame_timestamp_ = frame->timestamp(); |
277 // Make sure |predicted_frame_duration| is in a safe range of values. | 313 // Make sure |predicted_frame_duration| is in a safe range of values. |
278 const TimeDelta kMaxFrameDuration = TimeDelta::FromSecondsD(1.0 / 8); | 314 const TimeDelta kMaxFrameDuration = TimeDelta::FromSecondsD(1.0 / 8); |
279 const TimeDelta kMinFrameDuration = TimeDelta::FromMilliseconds(1); | 315 const TimeDelta kMinFrameDuration = TimeDelta::FromMilliseconds(1); |
280 return std::min(kMaxFrameDuration, std::max(predicted_frame_duration, | 316 return std::min(kMaxFrameDuration, std::max(predicted_frame_duration, |
281 kMinFrameDuration)); | 317 kMinFrameDuration)); |
282 } | 318 } |
283 | 319 |
284 VideoTrackRecorder::VideoTrackRecorder( | 320 VideoTrackRecorder::VideoTrackRecorder( |
285 const blink::WebMediaStreamTrack& track, | 321 const blink::WebMediaStreamTrack& track, |
286 const OnEncodedVideoCB& on_encoded_video_callback) | 322 const OnEncodedVideoCB& on_encoded_video_callback) |
287 : track_(track), | 323 : track_(track), |
288 encoder_(new VpxEncoder(on_encoded_video_callback)), | 324 encoder_(new VpxEncoder(on_encoded_video_callback)) { |
289 weak_factory_(this) { | |
290 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 325 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
291 DCHECK(!track_.isNull()); | 326 DCHECK(!track_.isNull()); |
292 DCHECK(track.extraData()); | 327 DCHECK(track.extraData()); |
| 328 |
| 329 // StartFrameEncode() will be called on Render IO thread. |
293 AddToVideoTrack(this, | 330 AddToVideoTrack(this, |
294 media::BindToCurrentLoop( | 331 base::Bind(&VideoTrackRecorder::VpxEncoder::StartFrameEncode, |
295 base::Bind(&VideoTrackRecorder::OnVideoFrame, | 332 encoder_), |
296 weak_factory_.GetWeakPtr())), | |
297 track_); | 333 track_); |
298 } | 334 } |
299 | 335 |
300 VideoTrackRecorder::~VideoTrackRecorder() { | 336 VideoTrackRecorder::~VideoTrackRecorder() { |
301 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 337 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
302 RemoveFromVideoTrack(this, track_); | 338 RemoveFromVideoTrack(this, track_); |
303 weak_factory_.InvalidateWeakPtrs(); | |
304 track_.reset(); | 339 track_.reset(); |
305 } | 340 } |
306 | 341 |
307 void VideoTrackRecorder::OnVideoFrame(const scoped_refptr<VideoFrame>& frame, | 342 void VideoTrackRecorder::OnVideoFrameForTesting( |
308 base::TimeTicks timestamp) { | 343 const scoped_refptr<media::VideoFrame>& frame, |
309 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 344 base::TimeTicks timestamp) { |
310 encoder_->StartFrameEncode(frame, timestamp); | 345 encoder_->StartFrameEncode(frame, timestamp); |
311 } | 346 } |
312 | 347 |
313 } // namespace content | 348 } // namespace content |
OLD | NEW |