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

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: Fix race in ctor of VideoTrackAdapter. 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.
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
OLDNEW
« no previous file with comments | « content/renderer/media/video_track_adapter.h ('k') | content/renderer/media/webrtc/media_stream_remote_video_source.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698