Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Side by Side Diff: content/renderer/media/video_track_adapter.cc

Issue 246433006: Change MediaStreamVideoSource to output different resolutions to different tracks depending on the … (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixed missed comments. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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.
tommi (sloooow) - chröme 2014/05/14 09:43:38 indent
perkj_chrome 2014/05/14 13:29:44 Done.
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::MessageLoopProxy> 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.
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 message loop where we will release VideoCaptureDeliverFrameCB
81 // registered in AddCallback.
82 scoped_refptr<base::MessageLoopProxy> render_message_loop_;
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::MessageLoopProxy> render_message_loop,
98 int max_width,
99 int max_height,
100 double min_aspect_ratio,
101 double max_aspect_ratio)
102 : render_message_loop_(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(render_message_loop_);
tommi (sloooow) - chröme 2014/05/14 09:43:38 w00t! dcheck party! (it's a good thing)
perkj_chrome 2014/05/14 13:29:44 Done.
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 // Make sure we scale to an even height to avoid rounding errors
161 // (+ 1 & ~1).
162 desired_height = static_cast<int>((desired_height * resulting_ratio) /
163 requested_ratio) + 1 & ~1;
164 } else if (resulting_ratio > requested_ratio) {
165 // Make sure we scale to an even width to avoid rounding errors.
166 desired_width = static_cast<int>((desired_width * requested_ratio) /
167 resulting_ratio) + 1 & ~1;
168 }
169
170 gfx::Size desired_size(desired_width, desired_height);
171
172 // Get the largest centered rectangle with the same aspect ratio of
173 // |desired_size| that fits entirely inside of |frame->visible_rect()|.
174 // This will be the rect we need to crop the original frame to.
175 // From this rect, the original frame can be scaled down to |desired_size|.
176 gfx::Rect region_in_frame =
177 media::ComputeLetterboxRegion(frame->visible_rect(), desired_size);
178
179 video_frame = media::VideoFrame::WrapVideoFrame(
180 frame,
181 region_in_frame,
182 desired_size,
183 base::Bind(&ReleaseOriginalFrame, frame));
184
185 DVLOG(3) << "desired size " << desired_size.ToString()
186 << " output natural size "
187 << video_frame->natural_size().ToString()
188 << " output visible rect "
189 << video_frame->visible_rect().ToString();
190 }
191 DoDeliverFrame(video_frame, format);
192 }
193
194 void VideoTrackAdapter::
195 VideoFrameResolutionAdapter::DoDeliverFrame(
196 const scoped_refptr<media::VideoFrame>& frame,
197 const media::VideoCaptureFormat& format) {
198 DCHECK(io_thread_checker_.CalledOnValidThread());
199 for (std::vector<VideoIdCallbackPair>::const_iterator it = callbacks_.begin();
200 it != callbacks_.end(); ++it) {
201 it->second.Run(frame, format);
202 }
203 }
204
205 void VideoTrackAdapter::VideoFrameResolutionAdapter::AddCallback(
206 const MediaStreamVideoTrack* track,
207 const VideoCaptureDeliverFrameCB& callback) {
208 DCHECK(io_thread_checker_.CalledOnValidThread());
209 callbacks_.push_back(std::make_pair(track, callback));
210 }
211
212 void VideoTrackAdapter::VideoFrameResolutionAdapter::RemoveCallback(
213 const MediaStreamVideoTrack* track) {
214 DCHECK(io_thread_checker_.CalledOnValidThread());
215 std::vector<VideoIdCallbackPair>::iterator it = callbacks_.begin();
216 for (; it != callbacks_.end(); ++it) {
217 if (it->first == track) {
218 // Callback is copied to heap and then deleted on the target thread.
219 // The following code ensures that the callback is not referenced on
220 // the stack.
221 scoped_ptr<VideoCaptureDeliverFrameCB> callback;
222 {
223 callback.reset(new VideoCaptureDeliverFrameCB(it->second));
tommi (sloooow) - chröme 2014/05/14 09:43:38 add a comment for why this is done inside a scope.
perkj_chrome 2014/05/14 13:29:44 You are right. Since the object is on the heap
224 callbacks_.erase(it);
225 }
226 render_message_loop_->PostTask(
227 FROM_HERE, base::Bind(&ResetCallbackOnMainRenderThread,
228 base::Passed(&callback)));
229
230 return;
231 }
232 }
233 }
234
235 bool VideoTrackAdapter::VideoFrameResolutionAdapter::ConstraintsMatch(
236 int max_width,
237 int max_height,
238 double min_aspect_ratio,
239 double max_aspect_ratio) const {
240 DCHECK(io_thread_checker_.CalledOnValidThread());
241 return max_frame_size_.width() == max_width &&
242 max_frame_size_.height() == max_height &&
243 min_aspect_ratio_ == min_aspect_ratio &&
244 max_aspect_ratio_ == max_aspect_ratio;
245 }
246
247 bool VideoTrackAdapter::VideoFrameResolutionAdapter::IsEmpty() const {
248 DCHECK(io_thread_checker_.CalledOnValidThread());
249 return callbacks_.empty();
250 }
251
252 VideoTrackAdapter::VideoTrackAdapter(
253 const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
254 : io_message_loop_(io_message_loop) {
255 DCHECK(io_message_loop_);
256 io_message_loop_->PostTask(
257 FROM_HERE,
258 base::Bind(&VideoTrackAdapter::SetRenderMessageLoopOnIo,
259 this, base::MessageLoopProxy::current()));
260 }
261
262 VideoTrackAdapter::~VideoTrackAdapter() {
263 DCHECK(adapters_.empty());
264 }
265
266 void VideoTrackAdapter::SetRenderMessageLoopOnIo(
267 const scoped_refptr<base::MessageLoopProxy>& renderer_message_loop) {
268 DCHECK(io_message_loop_->BelongsToCurrentThread());
269 DCHECK(renderer_message_loop);
270 renderer_message_loop_ = renderer_message_loop;
271 }
272
273 void VideoTrackAdapter::AddTrack(const MediaStreamVideoTrack* track,
274 VideoCaptureDeliverFrameCB frame_callback,
275 int max_width,
276 int max_height,
277 double min_aspect_ratio,
278 double max_aspect_ratio) {
279 DCHECK(thread_checker_.CalledOnValidThread());
280 io_message_loop_->PostTask(
281 FROM_HERE,
282 base::Bind(&VideoTrackAdapter::AddTrackOnIO,
283 this, track, frame_callback, max_width, max_height,
284 min_aspect_ratio, max_aspect_ratio));
285 }
286
287 void VideoTrackAdapter::AddTrackOnIO(
288 const MediaStreamVideoTrack* track,
289 VideoCaptureDeliverFrameCB frame_callback,
290 int max_width,
291 int max_height,
292 double min_aspect_ratio,
293 double max_aspect_ratio) {
294 DCHECK(io_message_loop_->BelongsToCurrentThread());
295 scoped_refptr<VideoFrameResolutionAdapter> adapter;
296 for (FrameAdapters::const_iterator it = adapters_.begin();
297 it != adapters_.end(); ++it) {
298 if ((*it)->ConstraintsMatch(max_width, max_height, min_aspect_ratio,
299 max_aspect_ratio)) {
300 adapter = it->get();
301 break;
302 }
303 }
304 if (!adapter) {
305 adapter = new VideoFrameResolutionAdapter(renderer_message_loop_,
306 max_width,
307 max_height,
308 min_aspect_ratio,
309 max_aspect_ratio);
310 adapters_.push_back(adapter);
311 }
312
313 adapter->AddCallback(track, frame_callback);
314 }
315
316 void VideoTrackAdapter::RemoveTrack(const MediaStreamVideoTrack* track) {
317 DCHECK(thread_checker_.CalledOnValidThread());
318 io_message_loop_->PostTask(
319 FROM_HERE,
320 base::Bind(&VideoTrackAdapter::RemoveTrackOnIO, this, track));
321 }
322
323 void VideoTrackAdapter::RemoveTrackOnIO(const MediaStreamVideoTrack* track) {
324 DCHECK(io_message_loop_->BelongsToCurrentThread());
325 for (FrameAdapters::iterator it = adapters_.begin();
326 it != adapters_.end(); ++it) {
327 (*it)->RemoveCallback(track);
328 if ((*it)->IsEmpty()) {
329 adapters_.erase(it);
330 break;
331 }
332 }
333 }
334
335 void VideoTrackAdapter::DeliverFrameOnIO(
336 const scoped_refptr<media::VideoFrame>& frame,
337 const media::VideoCaptureFormat& format) {
338 DCHECK(io_message_loop_->BelongsToCurrentThread());
339 TRACE_EVENT0("video", "VideoTrackAdapter::DeliverFrameOnIO");
340 for (FrameAdapters::iterator it = adapters_.begin();
341 it != adapters_.end(); ++it) {
342 (*it)->DeliverFrame(frame, format);
343 }
344 }
345
346 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698