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

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

Powered by Google App Engine
This is Rietveld 408576698