blob: e48d28645ad92733b4fe8f7d7dd197a33d11a755 [file] [log] [blame]
// Copyright 2018 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.
#ifndef COMPONENTS_VIZ_SERVICE_FRAME_SINKS_VIDEO_CAPTURE_VIDEO_CAPTURE_OVERLAY_H_
#define COMPONENTS_VIZ_SERVICE_FRAME_SINKS_VIDEO_CAPTURE_VIDEO_CAPTURE_OVERLAY_H_
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/sequence_checker.h"
#include "components/viz/service/viz_service_export.h"
#include "media/base/video_types.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/viz/privileged/interfaces/compositing/frame_sink_video_capture.mojom.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/color_transform.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
namespace media {
class VideoFrame;
}
namespace viz {
// An overlay image to be blitted onto video frames. A mojo client sets the
// image, position, and size of the overlay in the video frame; and then this
// VideoCaptureOverlay scales the image and maps its color space to match that
// of the video frame before the blitting.
//
// As an optimization, the client's bitmap image is transformed (scaled, color
// space converted, and pre-multiplied by alpha), and then this cached Sprite is
// re-used for blitting to all successive video frames until some change
// requires a different transformation. MakeRenderer() produces a Renderer
// callback that holds a reference to an existing Sprite, or will create a new
// one if necessary. The Renderer callback can then be run at any point in the
// future, unaffected by later image, size, or color space settings changes.
//
// The blit algorithm uses naive linear blending. Thus, the use of non-linear
// color spaces will cause loses in color accuracy.
class VIZ_SERVICE_EXPORT VideoCaptureOverlay
: public mojom::FrameSinkVideoCaptureOverlay {
public:
// Interface for notifying the frame source when changes to the overlay's
// state occur.
class VIZ_SERVICE_EXPORT FrameSource {
public:
// Returns the current size of the source, or empty if unknown.
virtual gfx::Size GetSourceSize() = 0;
// Notifies the FrameSource that the given source |rect| needs to be
// re-captured soon. One or more calls to this method will be followed-up
// with a call to RequestRefreshFrame().
virtual void InvalidateRect(const gfx::Rect& rect) = 0;
// Notifies the FrameSource that another frame should be captured and have
// its VideoCaptureOverlay re-rendered soon to reflect an updated overlay
// image and/or position.
virtual void RequestRefreshFrame() = 0;
// Notifies the FrameSource that the VideoCaptureOverlay has lost its mojo
// binding.
virtual void OnOverlayConnectionLost(VideoCaptureOverlay* overlay) = 0;
protected:
virtual ~FrameSource();
};
// A OnceCallback that, when run, renders the overlay on a VideoFrame.
using OnceRenderer = base::OnceCallback<void(media::VideoFrame*)>;
// |frame_source| must outlive this instance.
VideoCaptureOverlay(FrameSource* frame_source,
mojom::FrameSinkVideoCaptureOverlayRequest request);
~VideoCaptureOverlay() final;
// mojom::FrameSinkVideoCaptureOverlay implementation:
void SetImageAndBounds(const SkBitmap& image, const gfx::RectF& bounds) final;
void SetBounds(const gfx::RectF& bounds) final;
// Returns a OnceCallback that, when run, renders this VideoCaptureOverlay on
// a VideoFrame. The overlay's position and size are computed based on the
// given content |region_in_frame|. Returns a null OnceCallback if there is
// nothing to render at this time.
OnceRenderer MakeRenderer(const gfx::Rect& region_in_frame,
const media::VideoPixelFormat frame_format);
// Returns a OnceCallback that renders all of the given |overlays| in
// order. The remaining arguments are the same as in MakeRenderer(). This is a
// convenience that produces a single callback, so that client code need not
// deal with collections of callbacks. Returns a null OnceCallback if there is
// nothing to render at this time.
static OnceRenderer MakeCombinedRenderer(
const std::vector<VideoCaptureOverlay*>& overlays,
const gfx::Rect& region_in_frame,
const media::VideoPixelFormat frame_format);
private:
// Transforms the overlay SkBitmap image by scaling and converting its color
// space, and then blitting it onto a VideoFrame. The transformation is lazy:
// Meaning, the transformation is executed upon the first call to Blit(), and
// the result is cached for re-use for later Blit() calls. The transformation
// is re-executed if the color space of the VideoFrame changes (rarely).
class Sprite : public base::RefCounted<Sprite> {
public:
Sprite(const SkBitmap& image,
const gfx::Size& size,
const media::VideoPixelFormat format);
const gfx::Size& size() const { return size_; }
media::VideoPixelFormat format() const { return format_; }
void Blit(const gfx::Point& position,
const gfx::Rect& blit_rect,
media::VideoFrame* frame);
private:
friend class base::RefCounted<Sprite>;
~Sprite();
void TransformImage();
// As Sprites can be long-lived and hidden from external code within
// callbacks, ensure that all Blit() calls are in-sequence.
SEQUENCE_CHECKER(sequence_checker_);
// Starts-out as the original, unscaled overlay image. The first call to
// TransformImage() replaces it with a scaled one.
SkBitmap image_;
// The size, format, and color space of the cached transformed image.
const gfx::Size size_;
const media::VideoPixelFormat format_;
gfx::ColorSpace color_space_;
// The transformed source image data. For blitting to ARGB format video
// frames, the source image data will consist of 4 elements per pixel pixel
// (A, R, G, B). For blitting to the I420 format, the source image data is
// not interlaved: Instead, there are 5 planes of data (one minus alpha, Y,
// subsampled one minus alpha, U, V). For both formats, the color components
// are premultiplied for more-efficient Blit()'s.
std::unique_ptr<float[]> transformed_image_;
DISALLOW_COPY_AND_ASSIGN(Sprite);
};
// Computes the region of the source that, if changed, would require
// re-rendering the overlay.
gfx::Rect ComputeSourceMutationRect() const;
FrameSource* const frame_source_;
mojo::Binding<mojom::FrameSinkVideoCaptureOverlay> binding_;
// The currently-set overlay image.
SkBitmap image_;
// If empty, the overlay is currently hidden. Otherwise, this consists of
// coordinates where the range [0.0,1.0) indicates the relative position+size
// within the bounds of the video frame's content region (e.g., 0.0 refers to
// the top or left edge; 1.0 to just after the bottom or right edge).
gfx::RectF bounds_;
// The current Sprite. This is set to null whenever a settings change requires
// a new Sprite to be generated from the |image_|.
scoped_refptr<Sprite> sprite_;
DISALLOW_COPY_AND_ASSIGN(VideoCaptureOverlay);
};
} // namespace viz
#endif // COMPONENTS_VIZ_SERVICE_FRAME_SINKS_VIDEO_CAPTURE_VIDEO_CAPTURE_OVERLAY_H_