OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/renderer/media/video_track_adapter.h" | |
6 | |
7 #include <algorithm> | |
8 #include <limits> | |
9 #include <utility> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/debug/trace_event.h" | |
13 #include "base/location.h" | |
14 #include "media/base/video_util.h" | |
15 | |
16 namespace content { | |
17 | |
18 namespace { | |
19 | |
20 // Empty method used for keeping a reference to the original media::VideoFrame | |
21 // in VideoFrameResolutionAdapter::DeliverFrame if cropping is needed. | |
22 // The reference to |frame| is kept in the closure that calls this method. | |
23 void ReleaseOriginalFrame( | |
24 const scoped_refptr<media::VideoFrame>& frame) { | |
25 } | |
26 | |
27 void ResetCallbackOnMainRenderThread( | |
28 scoped_ptr<VideoCaptureDeliverFrameCB> callback) { | |
29 // |callback| will be deleted when this exits. | |
30 } | |
31 | |
32 } // anonymous namespace | |
33 | |
34 // VideoFrameResolutionAdapter is created on and lives on | |
35 // on the IO-thread. It does the resolution adaptation and delivers frames to | |
36 // all registered tracks on the IO-thread. | |
37 // All method calls must be on the IO-thread. | |
38 class VideoTrackAdapter::VideoFrameResolutionAdapter | |
39 : public base::RefCountedThreadSafe<VideoFrameResolutionAdapter> { | |
40 public: | |
41 VideoFrameResolutionAdapter( | |
42 scoped_refptr<base::SingleThreadTaskRunner> render_message_loop, | |
43 int max_width, | |
44 int max_height, | |
45 double min_aspect_ratio, | |
46 double max_aspect_ratio); | |
47 | |
48 // Add |callback| to receive video frames on the IO-thread. | |
49 // |callback| will however be released on thee main render thread. | |
mcasas
2014/05/20 07:19:53
s/thee/the/
perkj_chrome
2014/05/22 07:06:05
Done.
| |
50 void AddCallback(const MediaStreamVideoTrack* track, | |
51 const VideoCaptureDeliverFrameCB& callback); | |
52 | |
53 // Removes |callback| associated with |track| from receiving video frames if | |
54 // |track| has been added. It is ok to call RemoveCallback even if the |track| | |
55 // has not been added. The |callback| is released on the main render thread. | |
56 void RemoveCallback(const MediaStreamVideoTrack* track); | |
57 | |
58 void DeliverFrame(const scoped_refptr<media::VideoFrame>& frame, | |
59 const media::VideoCaptureFormat& format); | |
60 | |
61 // Returns true if all arguments match with the output of this adapter. | |
62 bool ConstraintsMatch(int max_width, | |
63 int max_height, | |
64 double min_aspect_ratio, | |
65 double max_aspect_ratio) const; | |
66 | |
67 bool IsEmpty() const; | |
68 | |
69 private: | |
70 virtual ~VideoFrameResolutionAdapter(); | |
71 friend class base::RefCountedThreadSafe<VideoFrameResolutionAdapter>; | |
72 | |
73 virtual void DoDeliverFrame( | |
74 const scoped_refptr<media::VideoFrame>& frame, | |
75 const media::VideoCaptureFormat& format); | |
76 | |
77 // Bound to the IO-thread. | |
78 base::ThreadChecker io_thread_checker_; | |
79 | |
80 // The task runner where we will release VideoCaptureDeliverFrameCB | |
81 // registered in AddCallback. | |
82 scoped_refptr<base::SingleThreadTaskRunner> renderer_task_runner_; | |
83 | |
84 gfx::Size max_frame_size_; | |
85 double min_aspect_ratio_; | |
86 double max_aspect_ratio_; | |
87 | |
88 typedef std::pair<const void*, VideoCaptureDeliverFrameCB> | |
89 VideoIdCallbackPair; | |
90 std::vector<VideoIdCallbackPair> callbacks_; | |
91 | |
92 DISALLOW_COPY_AND_ASSIGN(VideoFrameResolutionAdapter); | |
93 }; | |
94 | |
95 VideoTrackAdapter:: | |
96 VideoFrameResolutionAdapter::VideoFrameResolutionAdapter( | |
97 scoped_refptr<base::SingleThreadTaskRunner> render_message_loop, | |
98 int max_width, | |
99 int max_height, | |
100 double min_aspect_ratio, | |
101 double max_aspect_ratio) | |
102 : renderer_task_runner_(render_message_loop), | |
103 max_frame_size_(max_width, max_height), | |
104 min_aspect_ratio_(min_aspect_ratio), | |
105 max_aspect_ratio_(max_aspect_ratio) { | |
106 DCHECK(renderer_task_runner_); | |
107 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
108 DCHECK_GE(max_aspect_ratio_, min_aspect_ratio_); | |
109 CHECK_NE(0, max_aspect_ratio_); | |
110 DVLOG(3) << "VideoFrameResolutionAdapter(" | |
111 << "{ max_width =" << max_width << "}, " | |
112 << "{ max_height =" << max_height << "}, " | |
113 << "{ min_aspect_ratio =" << min_aspect_ratio << "}, " | |
114 << "{ max_aspect_ratio_ =" << max_aspect_ratio_ << "}) "; | |
115 } | |
116 | |
117 VideoTrackAdapter:: | |
118 VideoFrameResolutionAdapter::~VideoFrameResolutionAdapter() { | |
119 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
120 DCHECK(callbacks_.empty()); | |
121 } | |
122 | |
123 void VideoTrackAdapter::VideoFrameResolutionAdapter::DeliverFrame( | |
124 const scoped_refptr<media::VideoFrame>& frame, | |
125 const media::VideoCaptureFormat& format) { | |
126 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
127 // TODO(perkj): Allow cropping / scaling of textures once | |
128 // http://crbug/362521 is fixed. | |
129 if (frame->format() == media::VideoFrame::NATIVE_TEXTURE) { | |
130 DoDeliverFrame(frame, format); | |
131 return; | |
132 } | |
133 scoped_refptr<media::VideoFrame> video_frame(frame); | |
134 double input_ratio = | |
135 static_cast<double>(frame->natural_size().width()) / | |
136 frame->natural_size().height(); | |
137 | |
138 // If |frame| has larger width or height than requested, or the aspect ratio | |
139 // does not match the requested, we want to create a wrapped version of this | |
140 // frame with a size that fulfills the constraints. | |
141 if (frame->natural_size().width() > max_frame_size_.width() || | |
142 frame->natural_size().height() > max_frame_size_.height() || | |
143 input_ratio > max_aspect_ratio_ || | |
144 input_ratio < min_aspect_ratio_) { | |
145 int desired_width = std::min(max_frame_size_.width(), | |
146 frame->natural_size().width()); | |
147 int desired_height = std::min(max_frame_size_.height(), | |
148 frame->natural_size().height()); | |
149 | |
150 double resulting_ratio = | |
151 static_cast<double>(desired_width) / desired_height; | |
152 double requested_ratio = resulting_ratio; | |
153 | |
154 if (requested_ratio > max_aspect_ratio_) | |
155 requested_ratio = max_aspect_ratio_; | |
156 else if (requested_ratio < min_aspect_ratio_) | |
157 requested_ratio = min_aspect_ratio_; | |
158 | |
159 if (resulting_ratio < requested_ratio) { | |
160 desired_height = static_cast<int>((desired_height * resulting_ratio) / | |
161 requested_ratio); | |
162 // Make sure we scale to an even height to avoid rounding errors | |
163 desired_height = (desired_height + 1) & ~1; | |
164 } else if (resulting_ratio > requested_ratio) { | |
165 desired_width = static_cast<int>((desired_width * requested_ratio) / | |
166 resulting_ratio); | |
167 // Make sure we scale to an even width to avoid rounding errors. | |
168 desired_width = (desired_width + 1) & ~1; | |
169 } | |
170 | |
171 gfx::Size desired_size(desired_width, desired_height); | |
172 | |
173 // Get the largest centered rectangle with the same aspect ratio of | |
174 // |desired_size| that fits entirely inside of |frame->visible_rect()|. | |
175 // This will be the rect we need to crop the original frame to. | |
176 // From this rect, the original frame can be scaled down to |desired_size|. | |
177 gfx::Rect region_in_frame = | |
178 media::ComputeLetterboxRegion(frame->visible_rect(), desired_size); | |
179 | |
180 video_frame = media::VideoFrame::WrapVideoFrame( | |
181 frame, | |
182 region_in_frame, | |
183 desired_size, | |
184 base::Bind(&ReleaseOriginalFrame, frame)); | |
185 | |
186 DVLOG(3) << "desired size " << desired_size.ToString() | |
187 << " output natural size " | |
188 << video_frame->natural_size().ToString() | |
189 << " output visible rect " | |
190 << video_frame->visible_rect().ToString(); | |
191 } | |
192 DoDeliverFrame(video_frame, format); | |
193 } | |
194 | |
195 void VideoTrackAdapter:: | |
196 VideoFrameResolutionAdapter::DoDeliverFrame( | |
197 const scoped_refptr<media::VideoFrame>& frame, | |
198 const media::VideoCaptureFormat& format) { | |
199 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
200 for (std::vector<VideoIdCallbackPair>::const_iterator it = callbacks_.begin(); | |
201 it != callbacks_.end(); ++it) { | |
202 it->second.Run(frame, format); | |
203 } | |
204 } | |
205 | |
206 void VideoTrackAdapter::VideoFrameResolutionAdapter::AddCallback( | |
207 const MediaStreamVideoTrack* track, | |
208 const VideoCaptureDeliverFrameCB& callback) { | |
209 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
210 callbacks_.push_back(std::make_pair(track, callback)); | |
211 } | |
212 | |
213 void VideoTrackAdapter::VideoFrameResolutionAdapter::RemoveCallback( | |
214 const MediaStreamVideoTrack* track) { | |
215 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
216 std::vector<VideoIdCallbackPair>::iterator it = callbacks_.begin(); | |
217 for (; it != callbacks_.end(); ++it) { | |
218 if (it->first == track) { | |
219 // Make sure the VideoCaptureDeliverFrameCB is released on the main | |
220 // render thread since it was added on the main render thread in | |
221 // VideoTrackAdapter::AddTrack. | |
222 scoped_ptr<VideoCaptureDeliverFrameCB> callback( | |
223 new VideoCaptureDeliverFrameCB(it->second)); | |
224 callbacks_.erase(it); | |
225 renderer_task_runner_->PostTask( | |
226 FROM_HERE, base::Bind(&ResetCallbackOnMainRenderThread, | |
227 base::Passed(&callback))); | |
mcasas
2014/05/20 07:36:09
PS-nit: consider "RelaseSoon()"
https://code.goog
perkj_chrome
2014/05/22 07:06:05
It should be DeleteSoon since this is not referenc
| |
228 | |
229 return; | |
230 } | |
231 } | |
232 } | |
233 | |
234 bool VideoTrackAdapter::VideoFrameResolutionAdapter::ConstraintsMatch( | |
235 int max_width, | |
236 int max_height, | |
237 double min_aspect_ratio, | |
238 double max_aspect_ratio) const { | |
239 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
240 return max_frame_size_.width() == max_width && | |
241 max_frame_size_.height() == max_height && | |
242 min_aspect_ratio_ == min_aspect_ratio && | |
243 max_aspect_ratio_ == max_aspect_ratio; | |
244 } | |
245 | |
246 bool VideoTrackAdapter::VideoFrameResolutionAdapter::IsEmpty() const { | |
247 DCHECK(io_thread_checker_.CalledOnValidThread()); | |
248 return callbacks_.empty(); | |
249 } | |
250 | |
251 VideoTrackAdapter::VideoTrackAdapter( | |
252 const scoped_refptr<base::MessageLoopProxy>& io_message_loop) | |
253 : io_message_loop_(io_message_loop), | |
254 renderer_task_runner_(base::MessageLoopProxy::current()) { | |
255 DCHECK(io_message_loop_); | |
256 } | |
257 | |
258 VideoTrackAdapter::~VideoTrackAdapter() { | |
259 DCHECK(adapters_.empty()); | |
260 } | |
261 | |
262 void VideoTrackAdapter::AddTrack(const MediaStreamVideoTrack* track, | |
263 VideoCaptureDeliverFrameCB frame_callback, | |
264 int max_width, | |
265 int max_height, | |
266 double min_aspect_ratio, | |
267 double max_aspect_ratio) { | |
268 DCHECK(thread_checker_.CalledOnValidThread()); | |
269 io_message_loop_->PostTask( | |
270 FROM_HERE, | |
271 base::Bind(&VideoTrackAdapter::AddTrackOnIO, | |
272 this, track, frame_callback, max_width, max_height, | |
273 min_aspect_ratio, max_aspect_ratio)); | |
274 } | |
275 | |
276 void VideoTrackAdapter::AddTrackOnIO( | |
277 const MediaStreamVideoTrack* track, | |
278 VideoCaptureDeliverFrameCB frame_callback, | |
279 int max_width, | |
280 int max_height, | |
281 double min_aspect_ratio, | |
282 double max_aspect_ratio) { | |
283 DCHECK(io_message_loop_->BelongsToCurrentThread()); | |
284 scoped_refptr<VideoFrameResolutionAdapter> adapter; | |
285 for (FrameAdapters::const_iterator it = adapters_.begin(); | |
286 it != adapters_.end(); ++it) { | |
287 if ((*it)->ConstraintsMatch(max_width, max_height, min_aspect_ratio, | |
288 max_aspect_ratio)) { | |
289 adapter = it->get(); | |
290 break; | |
291 } | |
292 } | |
293 if (!adapter) { | |
294 adapter = new VideoFrameResolutionAdapter(renderer_task_runner_, | |
295 max_width, | |
296 max_height, | |
297 min_aspect_ratio, | |
298 max_aspect_ratio); | |
299 adapters_.push_back(adapter); | |
300 } | |
301 | |
302 adapter->AddCallback(track, frame_callback); | |
303 } | |
304 | |
305 void VideoTrackAdapter::RemoveTrack(const MediaStreamVideoTrack* track) { | |
306 DCHECK(thread_checker_.CalledOnValidThread()); | |
307 io_message_loop_->PostTask( | |
308 FROM_HERE, | |
309 base::Bind(&VideoTrackAdapter::RemoveTrackOnIO, this, track)); | |
310 } | |
311 | |
312 void VideoTrackAdapter::RemoveTrackOnIO(const MediaStreamVideoTrack* track) { | |
313 DCHECK(io_message_loop_->BelongsToCurrentThread()); | |
314 for (FrameAdapters::iterator it = adapters_.begin(); | |
315 it != adapters_.end(); ++it) { | |
316 (*it)->RemoveCallback(track); | |
317 if ((*it)->IsEmpty()) { | |
318 adapters_.erase(it); | |
319 break; | |
320 } | |
321 } | |
322 } | |
323 | |
324 void VideoTrackAdapter::DeliverFrameOnIO( | |
325 const scoped_refptr<media::VideoFrame>& frame, | |
326 const media::VideoCaptureFormat& format) { | |
327 DCHECK(io_message_loop_->BelongsToCurrentThread()); | |
328 TRACE_EVENT0("video", "VideoTrackAdapter::DeliverFrameOnIO"); | |
329 for (FrameAdapters::iterator it = adapters_.begin(); | |
330 it != adapters_.end(); ++it) { | |
331 (*it)->DeliverFrame(frame, format); | |
332 } | |
333 } | |
334 | |
335 } // namespace content | |
OLD | NEW |