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

Unified 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: Rebased 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 side-by-side diff with in-line comments
Download patch
Index: content/renderer/media/video_track_adapter.cc
diff --git a/content/renderer/media/video_track_adapter.cc b/content/renderer/media/video_track_adapter.cc
new file mode 100644
index 0000000000000000000000000000000000000000..46223f8cfad2cb89211e484e9532fb7da4e1156a
--- /dev/null
+++ b/content/renderer/media/video_track_adapter.cc
@@ -0,0 +1,335 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/media/video_track_adapter.h"
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/debug/trace_event.h"
+#include "base/location.h"
+#include "media/base/video_util.h"
+
+namespace content {
+
+namespace {
+
+// Empty method used for keeping a reference to the original media::VideoFrame
+// in VideoFrameResolutionAdapter::DeliverFrame if cropping is needed.
+// The reference to |frame| is kept in the closure that calls this method.
+void ReleaseOriginalFrame(
+ const scoped_refptr<media::VideoFrame>& frame) {
+}
+
+void ResetCallbackOnMainRenderThread(
+ scoped_ptr<VideoCaptureDeliverFrameCB> callback) {
+ // |callback| will be deleted when this exits.
+}
+
+} // anonymous namespace
+
+// VideoFrameResolutionAdapter is created on and lives on
+// on the IO-thread. It does the resolution adaptation and delivers frames to
+// all registered tracks on the IO-thread.
+// All method calls must be on the IO-thread.
+class VideoTrackAdapter::VideoFrameResolutionAdapter
+ : public base::RefCountedThreadSafe<VideoFrameResolutionAdapter> {
+ public:
+ VideoFrameResolutionAdapter(
+ scoped_refptr<base::SingleThreadTaskRunner> render_message_loop,
+ int max_width,
+ int max_height,
+ double min_aspect_ratio,
+ double max_aspect_ratio);
+
+ // Add |callback| to receive video frames on the IO-thread.
+ // |callback| will however be released on the main render thread.
+ void AddCallback(const MediaStreamVideoTrack* track,
+ const VideoCaptureDeliverFrameCB& callback);
+
+ // Removes |callback| associated with |track| from receiving video frames if
+ // |track| has been added. It is ok to call RemoveCallback even if the |track|
+ // has not been added. The |callback| is released on the main render thread.
+ void RemoveCallback(const MediaStreamVideoTrack* track);
+
+ void DeliverFrame(const scoped_refptr<media::VideoFrame>& frame,
+ const media::VideoCaptureFormat& format);
+
+ // Returns true if all arguments match with the output of this adapter.
+ bool ConstraintsMatch(int max_width,
+ int max_height,
+ double min_aspect_ratio,
+ double max_aspect_ratio) const;
+
+ bool IsEmpty() const;
+
+ private:
+ virtual ~VideoFrameResolutionAdapter();
+ friend class base::RefCountedThreadSafe<VideoFrameResolutionAdapter>;
+
+ virtual void DoDeliverFrame(
+ const scoped_refptr<media::VideoFrame>& frame,
+ const media::VideoCaptureFormat& format);
+
+ // Bound to the IO-thread.
+ base::ThreadChecker io_thread_checker_;
+
+ // The task runner where we will release VideoCaptureDeliverFrameCB
+ // registered in AddCallback.
+ scoped_refptr<base::SingleThreadTaskRunner> renderer_task_runner_;
+
+ gfx::Size max_frame_size_;
+ double min_aspect_ratio_;
+ double max_aspect_ratio_;
+
+ typedef std::pair<const void*, VideoCaptureDeliverFrameCB>
+ VideoIdCallbackPair;
+ std::vector<VideoIdCallbackPair> callbacks_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameResolutionAdapter);
+};
+
+VideoTrackAdapter::
+VideoFrameResolutionAdapter::VideoFrameResolutionAdapter(
+ scoped_refptr<base::SingleThreadTaskRunner> render_message_loop,
+ int max_width,
+ int max_height,
+ double min_aspect_ratio,
+ double max_aspect_ratio)
+ : renderer_task_runner_(render_message_loop),
+ max_frame_size_(max_width, max_height),
+ min_aspect_ratio_(min_aspect_ratio),
+ max_aspect_ratio_(max_aspect_ratio) {
+ DCHECK(renderer_task_runner_);
+ DCHECK(io_thread_checker_.CalledOnValidThread());
+ DCHECK_GE(max_aspect_ratio_, min_aspect_ratio_);
+ CHECK_NE(0, max_aspect_ratio_);
+ DVLOG(3) << "VideoFrameResolutionAdapter("
+ << "{ max_width =" << max_width << "}, "
+ << "{ max_height =" << max_height << "}, "
+ << "{ min_aspect_ratio =" << min_aspect_ratio << "}, "
+ << "{ max_aspect_ratio_ =" << max_aspect_ratio_ << "}) ";
+}
+
+VideoTrackAdapter::
+VideoFrameResolutionAdapter::~VideoFrameResolutionAdapter() {
+ DCHECK(io_thread_checker_.CalledOnValidThread());
+ DCHECK(callbacks_.empty());
+}
+
+void VideoTrackAdapter::VideoFrameResolutionAdapter::DeliverFrame(
+ const scoped_refptr<media::VideoFrame>& frame,
+ const media::VideoCaptureFormat& format) {
+ DCHECK(io_thread_checker_.CalledOnValidThread());
+ // TODO(perkj): Allow cropping / scaling of textures once
+ // http://crbug/362521 is fixed.
+ if (frame->format() == media::VideoFrame::NATIVE_TEXTURE) {
+ DoDeliverFrame(frame, format);
+ return;
+ }
+ scoped_refptr<media::VideoFrame> video_frame(frame);
+ double input_ratio =
+ static_cast<double>(frame->natural_size().width()) /
+ frame->natural_size().height();
+
+ // If |frame| has larger width or height than requested, or the aspect ratio
+ // does not match the requested, we want to create a wrapped version of this
+ // frame with a size that fulfills the constraints.
+ if (frame->natural_size().width() > max_frame_size_.width() ||
+ frame->natural_size().height() > max_frame_size_.height() ||
+ input_ratio > max_aspect_ratio_ ||
+ input_ratio < min_aspect_ratio_) {
+ int desired_width = std::min(max_frame_size_.width(),
+ frame->natural_size().width());
+ int desired_height = std::min(max_frame_size_.height(),
+ frame->natural_size().height());
+
+ double resulting_ratio =
+ static_cast<double>(desired_width) / desired_height;
+ double requested_ratio = resulting_ratio;
+
+ if (requested_ratio > max_aspect_ratio_)
+ requested_ratio = max_aspect_ratio_;
+ else if (requested_ratio < min_aspect_ratio_)
+ requested_ratio = min_aspect_ratio_;
+
+ if (resulting_ratio < requested_ratio) {
+ desired_height = static_cast<int>((desired_height * resulting_ratio) /
+ requested_ratio);
+ // Make sure we scale to an even height to avoid rounding errors
+ desired_height = (desired_height + 1) & ~1;
+ } else if (resulting_ratio > requested_ratio) {
+ desired_width = static_cast<int>((desired_width * requested_ratio) /
+ resulting_ratio);
+ // Make sure we scale to an even width to avoid rounding errors.
+ desired_width = (desired_width + 1) & ~1;
+ }
+
+ gfx::Size desired_size(desired_width, desired_height);
+
+ // Get the largest centered rectangle with the same aspect ratio of
+ // |desired_size| that fits entirely inside of |frame->visible_rect()|.
+ // This will be the rect we need to crop the original frame to.
+ // From this rect, the original frame can be scaled down to |desired_size|.
+ gfx::Rect region_in_frame =
+ media::ComputeLetterboxRegion(frame->visible_rect(), desired_size);
+
+ video_frame = media::VideoFrame::WrapVideoFrame(
+ frame,
+ region_in_frame,
+ desired_size,
+ base::Bind(&ReleaseOriginalFrame, frame));
+
+ DVLOG(3) << "desired size " << desired_size.ToString()
+ << " output natural size "
+ << video_frame->natural_size().ToString()
+ << " output visible rect "
+ << video_frame->visible_rect().ToString();
+ }
+ DoDeliverFrame(video_frame, format);
+}
+
+void VideoTrackAdapter::
+VideoFrameResolutionAdapter::DoDeliverFrame(
+ const scoped_refptr<media::VideoFrame>& frame,
+ const media::VideoCaptureFormat& format) {
+ DCHECK(io_thread_checker_.CalledOnValidThread());
+ for (std::vector<VideoIdCallbackPair>::const_iterator it = callbacks_.begin();
+ it != callbacks_.end(); ++it) {
+ it->second.Run(frame, format);
+ }
+}
+
+void VideoTrackAdapter::VideoFrameResolutionAdapter::AddCallback(
+ const MediaStreamVideoTrack* track,
+ const VideoCaptureDeliverFrameCB& callback) {
+ DCHECK(io_thread_checker_.CalledOnValidThread());
+ callbacks_.push_back(std::make_pair(track, callback));
+}
+
+void VideoTrackAdapter::VideoFrameResolutionAdapter::RemoveCallback(
+ const MediaStreamVideoTrack* track) {
+ DCHECK(io_thread_checker_.CalledOnValidThread());
+ std::vector<VideoIdCallbackPair>::iterator it = callbacks_.begin();
+ for (; it != callbacks_.end(); ++it) {
+ if (it->first == track) {
+ // Make sure the VideoCaptureDeliverFrameCB is released on the main
+ // render thread since it was added on the main render thread in
+ // VideoTrackAdapter::AddTrack.
+ scoped_ptr<VideoCaptureDeliverFrameCB> callback(
+ new VideoCaptureDeliverFrameCB(it->second));
+ callbacks_.erase(it);
+ renderer_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ResetCallbackOnMainRenderThread,
+ base::Passed(&callback)));
+
+ return;
+ }
+ }
+}
+
+bool VideoTrackAdapter::VideoFrameResolutionAdapter::ConstraintsMatch(
+ int max_width,
+ int max_height,
+ double min_aspect_ratio,
+ double max_aspect_ratio) const {
+ DCHECK(io_thread_checker_.CalledOnValidThread());
+ return max_frame_size_.width() == max_width &&
+ max_frame_size_.height() == max_height &&
+ min_aspect_ratio_ == min_aspect_ratio &&
+ max_aspect_ratio_ == max_aspect_ratio;
+}
+
+bool VideoTrackAdapter::VideoFrameResolutionAdapter::IsEmpty() const {
+ DCHECK(io_thread_checker_.CalledOnValidThread());
+ return callbacks_.empty();
+}
+
+VideoTrackAdapter::VideoTrackAdapter(
+ const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
+ : io_message_loop_(io_message_loop),
+ renderer_task_runner_(base::MessageLoopProxy::current()) {
+ DCHECK(io_message_loop_);
+}
+
+VideoTrackAdapter::~VideoTrackAdapter() {
+ DCHECK(adapters_.empty());
+}
+
+void VideoTrackAdapter::AddTrack(const MediaStreamVideoTrack* track,
+ VideoCaptureDeliverFrameCB frame_callback,
+ int max_width,
+ int max_height,
+ double min_aspect_ratio,
+ double max_aspect_ratio) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ io_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoTrackAdapter::AddTrackOnIO,
+ this, track, frame_callback, max_width, max_height,
+ min_aspect_ratio, max_aspect_ratio));
+}
+
+void VideoTrackAdapter::AddTrackOnIO(
+ const MediaStreamVideoTrack* track,
+ VideoCaptureDeliverFrameCB frame_callback,
+ int max_width,
+ int max_height,
+ double min_aspect_ratio,
+ double max_aspect_ratio) {
+ DCHECK(io_message_loop_->BelongsToCurrentThread());
+ scoped_refptr<VideoFrameResolutionAdapter> adapter;
+ for (FrameAdapters::const_iterator it = adapters_.begin();
+ it != adapters_.end(); ++it) {
+ if ((*it)->ConstraintsMatch(max_width, max_height, min_aspect_ratio,
+ max_aspect_ratio)) {
+ adapter = it->get();
+ break;
+ }
+ }
+ if (!adapter) {
+ adapter = new VideoFrameResolutionAdapter(renderer_task_runner_,
+ max_width,
+ max_height,
+ min_aspect_ratio,
+ max_aspect_ratio);
+ adapters_.push_back(adapter);
+ }
+
+ adapter->AddCallback(track, frame_callback);
+}
+
+void VideoTrackAdapter::RemoveTrack(const MediaStreamVideoTrack* track) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ io_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoTrackAdapter::RemoveTrackOnIO, this, track));
+}
+
+void VideoTrackAdapter::RemoveTrackOnIO(const MediaStreamVideoTrack* track) {
+ DCHECK(io_message_loop_->BelongsToCurrentThread());
+ for (FrameAdapters::iterator it = adapters_.begin();
+ it != adapters_.end(); ++it) {
+ (*it)->RemoveCallback(track);
+ if ((*it)->IsEmpty()) {
+ adapters_.erase(it);
+ break;
+ }
+ }
+}
+
+void VideoTrackAdapter::DeliverFrameOnIO(
+ const scoped_refptr<media::VideoFrame>& frame,
+ const media::VideoCaptureFormat& format) {
+ DCHECK(io_message_loop_->BelongsToCurrentThread());
+ TRACE_EVENT0("video", "VideoTrackAdapter::DeliverFrameOnIO");
+ for (FrameAdapters::iterator it = adapters_.begin();
+ it != adapters_.end(); ++it) {
+ (*it)->DeliverFrame(frame, format);
+ }
+}
+
+} // namespace content
« 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