Commit 63c640c5 authored by Vasiliy Telezhnikov's avatar Vasiliy Telezhnikov Committed by Commit Bot

Get coded size for MediaCodec path

This CL modifies MediaCodec path to get use real coded_size and
visible_rect in VideoFrame instead of relying on texture matrix
transform.

To achieve this we utilize similar approach as in YCbCrHelper.
Helper is renamed to FrameInfoHelper. During CreateVideoFrame
VideoFrameFactoryImpl will check if it has cached FrameInfo for
current visible size that comes from media codec. If not it
will post task to FrameInfoHelper to get it. To avoid
unnecessary frame renders FrameInfoHelper also does similar
caching. If info is not cached it will Render frame early to
get the info.

To be able render frame before creation CodecImage and
SharedImageVideo the render logic is extracted from CodecImage
to CodecOutputBufferRenderer.

Bug: 1076564
Change-Id: I5baac124f46d78a89ae4300d5a01a9b9ab04af2e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2196922Reviewed-by: default avatarJonathan Backer <backer@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Commit-Queue: Vasiliy Telezhnikov <vasilyt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#769028}
parent 3ea0a099
...@@ -103,7 +103,7 @@ void SharedImageVideo::OnContextLost() { ...@@ -103,7 +103,7 @@ void SharedImageVideo::OnContextLost() {
} }
base::Optional<VulkanYCbCrInfo> SharedImageVideo::GetYcbcrInfo( base::Optional<VulkanYCbCrInfo> SharedImageVideo::GetYcbcrInfo(
StreamTextureSharedImageInterface* stream_texture_sii, TextureOwner* texture_owner,
scoped_refptr<SharedContextState> context_state) { scoped_refptr<SharedContextState> context_state) {
// For non-vulkan context, return null. // For non-vulkan context, return null.
if (!context_state->GrContextIsVulkan()) if (!context_state->GrContextIsVulkan())
...@@ -111,7 +111,7 @@ base::Optional<VulkanYCbCrInfo> SharedImageVideo::GetYcbcrInfo( ...@@ -111,7 +111,7 @@ base::Optional<VulkanYCbCrInfo> SharedImageVideo::GetYcbcrInfo(
// GetAHardwareBuffer() renders the latest image and gets AHardwareBuffer // GetAHardwareBuffer() renders the latest image and gets AHardwareBuffer
// from it. // from it.
auto scoped_hardware_buffer = stream_texture_sii->GetAHardwareBuffer(); auto scoped_hardware_buffer = texture_owner->GetAHardwareBuffer();
if (!scoped_hardware_buffer) { if (!scoped_hardware_buffer) {
return base::nullopt; return base::nullopt;
} }
......
...@@ -54,7 +54,7 @@ class GPU_GLES2_EXPORT SharedImageVideo ...@@ -54,7 +54,7 @@ class GPU_GLES2_EXPORT SharedImageVideo
// Returns ycbcr information. This is only valid in vulkan context and // Returns ycbcr information. This is only valid in vulkan context and
// nullopt for other context. // nullopt for other context.
static base::Optional<VulkanYCbCrInfo> GetYcbcrInfo( static base::Optional<VulkanYCbCrInfo> GetYcbcrInfo(
StreamTextureSharedImageInterface* stream_texture_sii, TextureOwner* texture_owner,
scoped_refptr<SharedContextState> context_state); scoped_refptr<SharedContextState> context_state);
protected: protected:
......
...@@ -17,6 +17,20 @@ class TextureBase; ...@@ -17,6 +17,20 @@ class TextureBase;
class GPU_GLES2_EXPORT StreamTextureSharedImageInterface class GPU_GLES2_EXPORT StreamTextureSharedImageInterface
: public gles2::GLStreamTextureImage { : public gles2::GLStreamTextureImage {
public: public:
enum class BindingsMode {
// Ensures that the TextureOwner's texture is bound to the latest image, if
// it requires explicit binding.
kEnsureTexImageBound,
// Updates the current image but does not bind it. If updating the image
// implicitly binds the texture, the current bindings will be restored.
kRestoreIfBound,
// Updates the current image but does not bind it. If updating the image
// implicitly binds the texture, the current bindings will not be restored.
kDontRestoreIfBound
};
// Release the underlying resources. This should be called when the image is // Release the underlying resources. This should be called when the image is
// not longer valid or the context is lost. // not longer valid or the context is lost.
virtual void ReleaseResources() = 0; virtual void ReleaseResources() = 0;
...@@ -41,20 +55,6 @@ class GPU_GLES2_EXPORT StreamTextureSharedImageInterface ...@@ -41,20 +55,6 @@ class GPU_GLES2_EXPORT StreamTextureSharedImageInterface
protected: protected:
~StreamTextureSharedImageInterface() override = default; ~StreamTextureSharedImageInterface() override = default;
enum class BindingsMode {
// Ensures that the TextureOwner's texture is bound to the latest image, if
// it requires explicit binding.
kEnsureTexImageBound,
// Updates the current image but does not bind it. If updating the image
// implicitly binds the texture, the current bindings will be restored.
kRestoreIfBound,
// Updates the current image but does not bind it. If updating the image
// implicitly binds the texture, the current bindings will not be restored.
kDontRestoreIfBound
};
}; };
} // namespace gpu } // namespace gpu
......
...@@ -239,7 +239,8 @@ void StreamTexture::OnFrameAvailable() { ...@@ -239,7 +239,8 @@ void StreamTexture::OnFrameAvailable() {
visible_rect_ = visible_rect; visible_rect_ = visible_rect;
auto mailbox = CreateSharedImage(coded_size); auto mailbox = CreateSharedImage(coded_size);
auto ycbcr_info = SharedImageVideo::GetYcbcrInfo(this, context_state_); auto ycbcr_info =
SharedImageVideo::GetYcbcrInfo(texture_owner_.get(), context_state_);
channel_->Send(new GpuStreamTextureMsg_FrameWithInfoAvailable( channel_->Send(new GpuStreamTextureMsg_FrameWithInfoAvailable(
route_id_, mailbox, coded_size, visible_rect, ycbcr_info)); route_id_, mailbox, coded_size, visible_rect, ycbcr_info));
......
...@@ -109,6 +109,8 @@ component("gpu") { ...@@ -109,6 +109,8 @@ component("gpu") {
"android/codec_image.h", "android/codec_image.h",
"android/codec_image_group.cc", "android/codec_image_group.cc",
"android/codec_image_group.h", "android/codec_image_group.h",
"android/codec_output_buffer_renderer.cc",
"android/codec_output_buffer_renderer.h",
"android/codec_surface_bundle.cc", "android/codec_surface_bundle.cc",
"android/codec_surface_bundle.h", "android/codec_surface_bundle.h",
"android/codec_wrapper.cc", "android/codec_wrapper.cc",
...@@ -117,6 +119,8 @@ component("gpu") { ...@@ -117,6 +119,8 @@ component("gpu") {
"android/device_info.h", "android/device_info.h",
"android/direct_shared_image_video_provider.cc", "android/direct_shared_image_video_provider.cc",
"android/direct_shared_image_video_provider.h", "android/direct_shared_image_video_provider.h",
"android/frame_info_helper.cc",
"android/frame_info_helper.h",
"android/maybe_render_early_manager.cc", "android/maybe_render_early_manager.cc",
"android/maybe_render_early_manager.h", "android/maybe_render_early_manager.h",
"android/media_codec_video_decoder.cc", "android/media_codec_video_decoder.cc",
...@@ -133,8 +137,6 @@ component("gpu") { ...@@ -133,8 +137,6 @@ component("gpu") {
"android/video_frame_factory.h", "android/video_frame_factory.h",
"android/video_frame_factory_impl.cc", "android/video_frame_factory_impl.cc",
"android/video_frame_factory_impl.h", "android/video_frame_factory_impl.h",
"android/ycbcr_helper.cc",
"android/ycbcr_helper.h",
] ]
libs += [ "android" ] libs += [ "android" ]
deps += [ deps += [
......
...@@ -16,31 +16,6 @@ ...@@ -16,31 +16,6 @@
#include "ui/gl/scoped_make_current.h" #include "ui/gl/scoped_make_current.h"
namespace media { namespace media {
namespace {
// Makes |texture_owner|'s context current if it isn't already.
std::unique_ptr<ui::ScopedMakeCurrent> MakeCurrentIfNeeded(
gpu::TextureOwner* texture_owner) {
gl::GLContext* context = texture_owner->GetContext();
// Note: this works for virtual contexts too, because IsCurrent() returns true
// if their shared platform context is current, regardless of which virtual
// context is current.
if (context->IsCurrent(nullptr))
return nullptr;
auto scoped_current = std::make_unique<ui::ScopedMakeCurrent>(
context, texture_owner->GetSurface());
// Log an error if ScopedMakeCurrent failed for debugging
// https://crbug.com/878042.
// TODO(ericrk): Remove this once debugging is completed.
if (!context->IsCurrent(nullptr)) {
LOG(ERROR) << "Failed to make context current in CodecImage. Subsequent "
"UpdateTexImage may fail.";
}
return scoped_current;
}
} // namespace
CodecImage::CodecImage() = default; CodecImage::CodecImage() = default;
...@@ -49,12 +24,11 @@ CodecImage::~CodecImage() { ...@@ -49,12 +24,11 @@ CodecImage::~CodecImage() {
} }
void CodecImage::Initialize( void CodecImage::Initialize(
std::unique_ptr<CodecOutputBuffer> output_buffer, std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator, scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb) { PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb) {
DCHECK(output_buffer); DCHECK(output_buffer_renderer);
phase_ = Phase::kInCodec; output_buffer_renderer_ = std::move(output_buffer_renderer);
output_buffer_ = std::move(output_buffer);
codec_buffer_wait_coordinator_ = std::move(codec_buffer_wait_coordinator); codec_buffer_wait_coordinator_ = std::move(codec_buffer_wait_coordinator);
promotion_hint_cb_ = std::move(promotion_hint_cb); promotion_hint_cb_ = std::move(promotion_hint_cb);
} }
...@@ -80,7 +54,8 @@ gfx::Size CodecImage::GetSize() { ...@@ -80,7 +54,8 @@ gfx::Size CodecImage::GetSize() {
// Return a nonzero size, to avoid GL errors, even if we dropped the codec // Return a nonzero size, to avoid GL errors, even if we dropped the codec
// buffer already. Note that if we dropped it, there's no data in the // buffer already. Note that if we dropped it, there's no data in the
// texture anyway, so the old size doesn't matter. // texture anyway, so the old size doesn't matter.
return output_buffer_ ? output_buffer_->size() : gfx::Size(1, 1); return output_buffer_renderer_ ? output_buffer_renderer_->size()
: gfx::Size(1, 1);
} }
unsigned CodecImage::GetInternalFormat() { unsigned CodecImage::GetInternalFormat() {
...@@ -119,7 +94,11 @@ bool CodecImage::CopyTexImage(unsigned target) { ...@@ -119,7 +94,11 @@ bool CodecImage::CopyTexImage(unsigned target) {
codec_buffer_wait_coordinator_->texture_owner()->GetTextureId())) codec_buffer_wait_coordinator_->texture_owner()->GetTextureId()))
return false; return false;
RenderToTextureOwnerFrontBuffer(BindingsMode::kEnsureTexImageBound); if (!output_buffer_renderer_)
return true;
output_buffer_renderer_->RenderToTextureOwnerFrontBuffer(
BindingsMode::kEnsureTexImageBound);
return true; return true;
} }
...@@ -183,23 +162,13 @@ void CodecImage::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, ...@@ -183,23 +162,13 @@ void CodecImage::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
const std::string& dump_name) {} const std::string& dump_name) {}
void CodecImage::GetTextureMatrix(float matrix[16]) { void CodecImage::GetTextureMatrix(float matrix[16]) {
// Default to identity. static constexpr float kIdentity[16]{
static constexpr float kYInvertedIdentity[16]{ 1, 0, 0, 0, //
1, 0, 0, 0, // 0, 1, 0, 0, //
0, -1, 0, 0, // 0, 0, 1, 0, //
0, 0, 1, 0, // 0, 0, 0, 1 //
0, 1, 0, 1 //
}; };
memcpy(matrix, kYInvertedIdentity, sizeof(kYInvertedIdentity)); memcpy(matrix, kIdentity, sizeof(kIdentity));
if (!codec_buffer_wait_coordinator_)
return;
// The matrix is available after we render to the front buffer. If that fails
// we'll return the matrix from the previous frame, which is more likely to be
// correct than the identity matrix anyway.
RenderToTextureOwnerFrontBuffer(BindingsMode::kDontRestoreIfBound);
codec_buffer_wait_coordinator_->texture_owner()->GetTransformMatrix(matrix);
YInvertMatrix(matrix);
} }
void CodecImage::NotifyPromotionHint(bool promotion_hint, void CodecImage::NotifyPromotionHint(bool promotion_hint,
...@@ -222,8 +191,10 @@ void CodecImage::ReleaseResources() { ...@@ -222,8 +191,10 @@ void CodecImage::ReleaseResources() {
} }
bool CodecImage::IsUsingGpuMemory() const { bool CodecImage::IsUsingGpuMemory() const {
if (!output_buffer_renderer_)
return false;
// Only the images which are bound to texture accounts for gpu memory. // Only the images which are bound to texture accounts for gpu memory.
return was_tex_image_bound_; return output_buffer_renderer_->was_tex_image_bound();
} }
void CodecImage::UpdateAndBindTexImage() { void CodecImage::UpdateAndBindTexImage() {
...@@ -239,120 +210,33 @@ gpu::TextureBase* CodecImage::GetTextureBase() const { ...@@ -239,120 +210,33 @@ gpu::TextureBase* CodecImage::GetTextureBase() const {
} }
bool CodecImage::RenderToFrontBuffer() { bool CodecImage::RenderToFrontBuffer() {
// This code is used to trigger early rendering of the image before it is used if (!output_buffer_renderer_)
// for compositing, there is no need to bind the image. return false;
return codec_buffer_wait_coordinator_ return output_buffer_renderer_->RenderToFrontBuffer();
? RenderToTextureOwnerFrontBuffer(BindingsMode::kRestoreIfBound)
: RenderToOverlay();
} }
bool CodecImage::RenderToTextureOwnerBackBuffer(BlockingMode blocking_mode) { bool CodecImage::RenderToTextureOwnerBackBuffer(BlockingMode blocking_mode) {
DCHECK_NE(phase_, Phase::kInFrontBuffer); if (!output_buffer_renderer_)
if (phase_ == Phase::kInBackBuffer)
return true;
if (phase_ == Phase::kInvalidated)
return false;
// Normally, we should have a wait coordinator if we're called. However, if
// the renderer is torn down (either VideoFrameSubmitter or the whole process)
// before we get returns back from viz, then we can be notified that we're
// no longer in use (erroneously) when the VideoFrame is destroyed. So, if
// we don't have a wait coordinator, then just fail.
if (!codec_buffer_wait_coordinator_)
return false; return false;
// Wait for a previous frame available so we don't confuse it with the one return output_buffer_renderer_->RenderToTextureOwnerBackBuffer(blocking_mode);
// we're about to release.
if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable()) {
if (blocking_mode == BlockingMode::kForbidBlocking)
return false;
codec_buffer_wait_coordinator_->WaitForFrameAvailable();
}
if (!output_buffer_->ReleaseToSurface()) {
phase_ = Phase::kInvalidated;
return false;
}
phase_ = Phase::kInBackBuffer;
codec_buffer_wait_coordinator_->SetReleaseTimeToNow();
return true;
} }
bool CodecImage::RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode) { bool CodecImage::RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode) {
// Normally, we should have a wait coordinator if we're called. However, if if (!output_buffer_renderer_)
// the renderer is torn down (either VideoFrameSubmitter or the whole process)
// before we get returns back from viz, then we can be notified that we're
// no longer in use (erroneously) when the VideoFrame is destroyed. So, if
// we don't have a wait coordinator, then just fail.
if (!codec_buffer_wait_coordinator_)
return false; return false;
return output_buffer_renderer_->RenderToTextureOwnerFrontBuffer(
if (phase_ == Phase::kInFrontBuffer) { bindings_mode);
EnsureBoundIfNeeded(bindings_mode);
return true;
}
if (phase_ == Phase::kInvalidated)
return false;
// Render it to the back buffer if it's not already there.
if (!RenderToTextureOwnerBackBuffer())
return false;
// The image is now in the back buffer, so promote it to the front buffer.
phase_ = Phase::kInFrontBuffer;
if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable())
codec_buffer_wait_coordinator_->WaitForFrameAvailable();
std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current =
MakeCurrentIfNeeded(
codec_buffer_wait_coordinator_->texture_owner().get());
// If updating the image will implicitly update the texture bindings then
// restore if requested or the update needed a context switch.
bool should_restore_bindings =
codec_buffer_wait_coordinator_->texture_owner()
->binds_texture_on_update() &&
(bindings_mode == BindingsMode::kRestoreIfBound || !!scoped_make_current);
GLint bound_service_id = 0;
if (should_restore_bindings)
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
codec_buffer_wait_coordinator_->texture_owner()->UpdateTexImage();
EnsureBoundIfNeeded(bindings_mode);
if (should_restore_bindings)
glBindTexture(GL_TEXTURE_EXTERNAL_OES, bound_service_id);
return true;
}
void CodecImage::EnsureBoundIfNeeded(BindingsMode mode) {
DCHECK(codec_buffer_wait_coordinator_);
if (codec_buffer_wait_coordinator_->texture_owner()
->binds_texture_on_update()) {
was_tex_image_bound_ = true;
return;
}
if (mode != BindingsMode::kEnsureTexImageBound)
return;
codec_buffer_wait_coordinator_->texture_owner()->EnsureTexImageBound();
was_tex_image_bound_ = true;
} }
bool CodecImage::RenderToOverlay() { bool CodecImage::RenderToOverlay() {
if (phase_ == Phase::kInFrontBuffer) if (!output_buffer_renderer_)
return true;
if (phase_ == Phase::kInvalidated)
return false; return false;
return output_buffer_renderer_->RenderToOverlay();
if (!output_buffer_->ReleaseToSurface()) {
phase_ = Phase::kInvalidated;
return false;
}
phase_ = Phase::kInFrontBuffer;
return true;
} }
void CodecImage::ReleaseCodecBuffer() { void CodecImage::ReleaseCodecBuffer() {
output_buffer_ = nullptr; output_buffer_renderer_.reset();
phase_ = Phase::kInvalidated;
} }
std::unique_ptr<base::android::ScopedHardwareBufferFenceSync> std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
...@@ -369,9 +253,7 @@ CodecImage::GetAHardwareBuffer() { ...@@ -369,9 +253,7 @@ CodecImage::GetAHardwareBuffer() {
} }
gfx::Rect CodecImage::GetCropRect() { gfx::Rect CodecImage::GetCropRect() {
if (!codec_buffer_wait_coordinator_) return gfx::Rect();
return gfx::Rect();
return codec_buffer_wait_coordinator_->texture_owner()->GetCropRect();
} }
bool CodecImage::HasMutableState() const { bool CodecImage::HasMutableState() const {
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#include "gpu/command_buffer/service/gl_stream_texture_image.h" #include "gpu/command_buffer/service/gl_stream_texture_image.h"
#include "gpu/command_buffer/service/stream_texture_shared_image_interface.h" #include "gpu/command_buffer/service/stream_texture_shared_image_interface.h"
#include "media/gpu/android/codec_buffer_wait_coordinator.h" #include "media/gpu/android/codec_buffer_wait_coordinator.h"
#include "media/gpu/android/codec_wrapper.h" #include "media/gpu/android/codec_output_buffer_renderer.h"
#include "media/gpu/android/promotion_hint_aggregator.h" #include "media/gpu/android/promotion_hint_aggregator.h"
#include "media/gpu/media_gpu_export.h" #include "media/gpu/media_gpu_export.h"
...@@ -33,8 +33,7 @@ namespace media { ...@@ -33,8 +33,7 @@ namespace media {
class MEDIA_GPU_EXPORT CodecImage class MEDIA_GPU_EXPORT CodecImage
: public gpu::StreamTextureSharedImageInterface { : public gpu::StreamTextureSharedImageInterface {
public: public:
// Whether RenderToTextureOwnerBackBuffer may block or not. using BlockingMode = CodecOutputBufferRenderer::BlockingMode;
enum class BlockingMode { kForbidBlocking, kAllowBlocking };
// Callback to notify that a codec image is now unused in the sense of not // Callback to notify that a codec image is now unused in the sense of not
// being out for display. This lets us signal interested folks once a video // being out for display. This lets us signal interested folks once a video
...@@ -54,7 +53,7 @@ class MEDIA_GPU_EXPORT CodecImage ...@@ -54,7 +53,7 @@ class MEDIA_GPU_EXPORT CodecImage
// May be called on a random thread, but only if the CodecImage is otherwise // May be called on a random thread, but only if the CodecImage is otherwise
// not in use. // not in use.
void Initialize( void Initialize(
std::unique_ptr<CodecOutputBuffer> output_buffer, std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator, scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb); PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb);
...@@ -123,12 +122,11 @@ class MEDIA_GPU_EXPORT CodecImage ...@@ -123,12 +122,11 @@ class MEDIA_GPU_EXPORT CodecImage
// Whether the codec buffer has been rendered to the front buffer. // Whether the codec buffer has been rendered to the front buffer.
bool was_rendered_to_front_buffer() const { bool was_rendered_to_front_buffer() const {
return phase_ == Phase::kInFrontBuffer; return output_buffer_renderer_
? output_buffer_renderer_->was_rendered_to_front_buffer()
: false;
} }
// Whether the TextureOwner's texture is in the front buffer and bound to the
// latest image.
bool was_tex_image_bound() const { return was_tex_image_bound_; }
// Whether this image is backed by a texture owner. // Whether this image is backed by a texture owner.
// We want to check for texture_owner owned by // We want to check for texture_owner owned by
...@@ -164,7 +162,9 @@ class MEDIA_GPU_EXPORT CodecImage ...@@ -164,7 +162,9 @@ class MEDIA_GPU_EXPORT CodecImage
virtual void ReleaseCodecBuffer(); virtual void ReleaseCodecBuffer();
CodecOutputBuffer* get_codec_output_buffer_for_testing() const { CodecOutputBuffer* get_codec_output_buffer_for_testing() const {
return output_buffer_.get(); return output_buffer_renderer_
? output_buffer_renderer_->get_codec_output_buffer_for_testing()
: nullptr;
} }
protected: protected:
...@@ -173,22 +173,12 @@ class MEDIA_GPU_EXPORT CodecImage ...@@ -173,22 +173,12 @@ class MEDIA_GPU_EXPORT CodecImage
private: private:
FRIEND_TEST_ALL_PREFIXES(CodecImageTest, RenderAfterUnusedDoesntCrash); FRIEND_TEST_ALL_PREFIXES(CodecImageTest, RenderAfterUnusedDoesntCrash);
// The lifecycle phases of an image. std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer_;
// The only possible transitions are from left to right. Both
// kInFrontBuffer and kInvalidated are terminal.
enum class Phase { kInCodec, kInBackBuffer, kInFrontBuffer, kInvalidated };
// Renders this image to the texture owner front buffer by first rendering // Renders this image to the texture owner front buffer by first rendering
// it to the back buffer if it's not already there, and then waiting for the // it to the back buffer if it's not already there, and then waiting for the
// frame available event before calling UpdateTexImage(). // frame available event before calling UpdateTexImage().
bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode); bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode);
void EnsureBoundIfNeeded(BindingsMode mode);
// The phase of the image buffer's lifecycle.
Phase phase_ = Phase::kInvalidated;
// The buffer backing this image.
std::unique_ptr<CodecOutputBuffer> output_buffer_;
// The CodecBufferWaitCoordinator that |output_buffer_| will be rendered to. // The CodecBufferWaitCoordinator that |output_buffer_| will be rendered to.
// Or null, if this image is backed by an overlay. // Or null, if this image is backed by an overlay.
...@@ -202,8 +192,6 @@ class MEDIA_GPU_EXPORT CodecImage ...@@ -202,8 +192,6 @@ class MEDIA_GPU_EXPORT CodecImage
std::vector<UnusedCB> unused_cbs_; std::vector<UnusedCB> unused_cbs_;
bool was_tex_image_bound_ = false;
DISALLOW_COPY_AND_ASSIGN(CodecImage); DISALLOW_COPY_AND_ASSIGN(CodecImage);
}; };
......
...@@ -86,10 +86,15 @@ class CodecImageTest : public testing::Test { ...@@ -86,10 +86,15 @@ class CodecImageTest : public testing::Test {
CodecImage::UnusedCB unused_cb = base::DoNothing()) { CodecImage::UnusedCB unused_cb = base::DoNothing()) {
std::unique_ptr<CodecOutputBuffer> buffer; std::unique_ptr<CodecOutputBuffer> buffer;
wrapper_->DequeueOutputBuffer(nullptr, nullptr, &buffer); wrapper_->DequeueOutputBuffer(nullptr, nullptr, &buffer);
auto codec_buffer_wait_coordinator =
kind == kTextureOwner ? codec_buffer_wait_coordinator_ : nullptr;
auto buffer_renderer = std::make_unique<CodecOutputBufferRenderer>(
std::move(buffer), codec_buffer_wait_coordinator);
scoped_refptr<CodecImage> image = new CodecImage(); scoped_refptr<CodecImage> image = new CodecImage();
image->Initialize( image->Initialize(
std::move(buffer), std::move(buffer_renderer), codec_buffer_wait_coordinator,
kind == kTextureOwner ? codec_buffer_wait_coordinator_ : nullptr,
base::BindRepeating(&PromotionHintReceiver::OnPromotionHint, base::BindRepeating(&PromotionHintReceiver::OnPromotionHint,
base::Unretained(&promotion_hint_receiver_))); base::Unretained(&promotion_hint_receiver_)));
...@@ -216,48 +221,6 @@ TEST_F(CodecImageTestExplicitBind, CopyTexImageTriggersFrontBufferRendering) { ...@@ -216,48 +221,6 @@ TEST_F(CodecImageTestExplicitBind, CopyTexImageTriggersFrontBufferRendering) {
ASSERT_TRUE(i->was_rendered_to_front_buffer()); ASSERT_TRUE(i->was_rendered_to_front_buffer());
} }
TEST_F(CodecImageTest, GetTextureMatrixTriggersFrontBufferRendering) {
auto i = NewImage(kTextureOwner);
InSequence s;
EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
EXPECT_CALL(*codec_buffer_wait_coordinator_, WaitForFrameAvailable());
EXPECT_CALL(*codec_buffer_wait_coordinator_->texture_owner(),
UpdateTexImage());
EXPECT_CALL(*codec_buffer_wait_coordinator_->texture_owner(),
GetTransformMatrix(_));
float matrix[16];
i->GetTextureMatrix(matrix);
ASSERT_TRUE(i->was_rendered_to_front_buffer());
}
TEST_F(CodecImageTestExplicitBind,
GetTextureMatrixTriggersFrontBufferRendering) {
// GetTextureMatrix should not bind the image.
codec_buffer_wait_coordinator_->texture_owner()->expect_update_tex_image =
false;
auto i = NewImage(kTextureOwner);
InSequence s;
EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
EXPECT_CALL(*codec_buffer_wait_coordinator_, WaitForFrameAvailable());
EXPECT_CALL(*codec_buffer_wait_coordinator_->texture_owner(),
UpdateTexImage());
EXPECT_CALL(*codec_buffer_wait_coordinator_->texture_owner(),
GetTransformMatrix(_));
float matrix[16];
i->GetTextureMatrix(matrix);
ASSERT_TRUE(i->was_rendered_to_front_buffer());
}
TEST_F(CodecImageTest, GetTextureMatrixReturnsIdentityForOverlayImages) {
auto i = NewImage(kOverlay);
float matrix[16]{0};
i->GetTextureMatrix(matrix);
// See GetTextureMatrix() for the expected result.
ASSERT_EQ(matrix[0], 1);
ASSERT_EQ(matrix[5], -1);
}
TEST_F(CodecImageTest, ScheduleOverlayPlaneTriggersFrontBufferRendering) { TEST_F(CodecImageTest, ScheduleOverlayPlaneTriggersFrontBufferRendering) {
auto i = NewImage(kOverlay); auto i = NewImage(kOverlay);
EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true)); EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
...@@ -412,15 +375,6 @@ TEST_F(CodecImageTest, GetAHardwareBufferAfterRelease) { ...@@ -412,15 +375,6 @@ TEST_F(CodecImageTest, GetAHardwareBufferAfterRelease) {
EXPECT_FALSE(i->GetAHardwareBuffer()); EXPECT_FALSE(i->GetAHardwareBuffer());
} }
TEST_F(CodecImageTest, GetCropRect) {
auto i = NewImage(kTextureOwner);
EXPECT_EQ(
codec_buffer_wait_coordinator_->texture_owner()->get_crop_rect_count, 0);
i->GetCropRect();
EXPECT_EQ(
codec_buffer_wait_coordinator_->texture_owner()->get_crop_rect_count, 1);
}
TEST_F(CodecImageTest, RenderAfterUnusedDoesntCrash) { TEST_F(CodecImageTest, RenderAfterUnusedDoesntCrash) {
auto i = NewImage(kTextureOwner); auto i = NewImage(kTextureOwner);
i->NotifyUnused(); i->NotifyUnused();
......
// Copyright 2020 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 "media/gpu/android/codec_output_buffer_renderer.h"
#include <string.h>
#include "base/android/scoped_hardware_buffer_fence_sync.h"
#include "base/bind_helpers.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/scoped_make_current.h"
namespace media {
namespace {
// Makes |texture_owner|'s context current if it isn't already.
std::unique_ptr<ui::ScopedMakeCurrent> MakeCurrentIfNeeded(
gpu::TextureOwner* texture_owner) {
gl::GLContext* context = texture_owner->GetContext();
// Note: this works for virtual contexts too, because IsCurrent() returns true
// if their shared platform context is current, regardless of which virtual
// context is current.
if (context->IsCurrent(nullptr))
return nullptr;
auto scoped_current = std::make_unique<ui::ScopedMakeCurrent>(
context, texture_owner->GetSurface());
// Log an error if ScopedMakeCurrent failed for debugging
// https://crbug.com/878042.
// TODO(ericrk): Remove this once debugging is completed.
if (!context->IsCurrent(nullptr)) {
LOG(ERROR) << "Failed to make context current in CodecImage. Subsequent "
"UpdateTexImage may fail.";
}
return scoped_current;
}
} // namespace
CodecOutputBufferRenderer::CodecOutputBufferRenderer(
std::unique_ptr<CodecOutputBuffer> output_buffer,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator)
: output_buffer_(std::move(output_buffer)),
codec_buffer_wait_coordinator_(std::move(codec_buffer_wait_coordinator)) {
}
CodecOutputBufferRenderer::~CodecOutputBufferRenderer() = default;
bool CodecOutputBufferRenderer::RenderToTextureOwnerBackBuffer(
BlockingMode blocking_mode) {
DCHECK_NE(phase_, Phase::kInFrontBuffer);
if (phase_ == Phase::kInBackBuffer)
return true;
if (phase_ == Phase::kInvalidated)
return false;
// Normally, we should have a wait coordinator if we're called. However, if
// the renderer is torn down (either VideoFrameSubmitter or the whole process)
// before we get returns back from viz, then we can be notified that we're
// no longer in use (erroneously) when the VideoFrame is destroyed. So, if
// we don't have a wait coordinator, then just fail.
if (!codec_buffer_wait_coordinator_)
return false;
// Wait for a previous frame available so we don't confuse it with the one
// we're about to release.
if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable()) {
if (blocking_mode == BlockingMode::kForbidBlocking)
return false;
codec_buffer_wait_coordinator_->WaitForFrameAvailable();
}
if (!output_buffer_->ReleaseToSurface()) {
phase_ = Phase::kInvalidated;
return false;
}
phase_ = Phase::kInBackBuffer;
codec_buffer_wait_coordinator_->SetReleaseTimeToNow();
return true;
}
bool CodecOutputBufferRenderer::RenderToTextureOwnerFrontBuffer(
BindingsMode bindings_mode) {
// Normally, we should have a wait coordinator if we're called. However, if
// the renderer is torn down (either VideoFrameSubmitter or the whole process)
// before we get returns back from viz, then we can be notified that we're
// no longer in use (erroneously) when the VideoFrame is destroyed. So, if
// we don't have a wait coordinator, then just fail.
if (!codec_buffer_wait_coordinator_)
return false;
if (phase_ == Phase::kInFrontBuffer) {
EnsureBoundIfNeeded(bindings_mode);
return true;
}
if (phase_ == Phase::kInvalidated)
return false;
// Render it to the back buffer if it's not already there.
if (!RenderToTextureOwnerBackBuffer())
return false;
// The image is now in the back buffer, so promote it to the front buffer.
phase_ = Phase::kInFrontBuffer;
if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable())
codec_buffer_wait_coordinator_->WaitForFrameAvailable();
std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current =
MakeCurrentIfNeeded(
codec_buffer_wait_coordinator_->texture_owner().get());
// If updating the image will implicitly update the texture bindings then
// restore if requested or the update needed a context switch.
bool should_restore_bindings =
codec_buffer_wait_coordinator_->texture_owner()
->binds_texture_on_update() &&
(bindings_mode == BindingsMode::kRestoreIfBound || !!scoped_make_current);
GLint bound_service_id = 0;
if (should_restore_bindings)
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
codec_buffer_wait_coordinator_->texture_owner()->UpdateTexImage();
EnsureBoundIfNeeded(bindings_mode);
if (should_restore_bindings)
glBindTexture(GL_TEXTURE_EXTERNAL_OES, bound_service_id);
return true;
}
void CodecOutputBufferRenderer::EnsureBoundIfNeeded(BindingsMode mode) {
DCHECK(codec_buffer_wait_coordinator_);
if (codec_buffer_wait_coordinator_->texture_owner()
->binds_texture_on_update()) {
was_tex_image_bound_ = true;
return;
}
if (mode != BindingsMode::kEnsureTexImageBound)
return;
codec_buffer_wait_coordinator_->texture_owner()->EnsureTexImageBound();
was_tex_image_bound_ = true;
}
bool CodecOutputBufferRenderer::RenderToOverlay() {
if (phase_ == Phase::kInFrontBuffer)
return true;
if (phase_ == Phase::kInvalidated)
return false;
if (!output_buffer_->ReleaseToSurface()) {
phase_ = Phase::kInvalidated;
return false;
}
phase_ = Phase::kInFrontBuffer;
return true;
}
bool CodecOutputBufferRenderer::RenderToFrontBuffer() {
// This code is used to trigger early rendering of the image before it is used
// for compositing, there is no need to bind the image.
return codec_buffer_wait_coordinator_
? RenderToTextureOwnerFrontBuffer(BindingsMode::kRestoreIfBound)
: RenderToOverlay();
}
} // namespace media
// Copyright 2020 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 MEDIA_GPU_ANDROID_CODEC_OUTPUT_BUFFER_RENDERER_H_
#define MEDIA_GPU_ANDROID_CODEC_OUTPUT_BUFFER_RENDERER_H_
#include <stdint.h>
#include <memory>
#include "gpu/command_buffer/service/stream_texture_shared_image_interface.h"
#include "media/gpu/android/codec_buffer_wait_coordinator.h"
#include "media/gpu/android/codec_wrapper.h"
#include "media/gpu/media_gpu_export.h"
namespace media {
// A class that holds CodecOutputBuffer and renders it to TextureOwner or
// overlay as necessary. Unit tests for this class are part of CodecImage unit
// tests.
class MEDIA_GPU_EXPORT CodecOutputBufferRenderer {
public:
using BindingsMode = gpu::StreamTextureSharedImageInterface::BindingsMode;
// Whether RenderToTextureOwnerBackBuffer may block or not.
enum class BlockingMode { kForbidBlocking, kAllowBlocking };
CodecOutputBufferRenderer(
std::unique_ptr<CodecOutputBuffer> output_buffer,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator);
~CodecOutputBufferRenderer();
CodecOutputBufferRenderer(const CodecOutputBufferRenderer&) = delete;
CodecOutputBufferRenderer& operator=(const CodecOutputBufferRenderer&) =
delete;
// Renders this image to the overlay. Returns true if the buffer is in the
// overlay front buffer. Returns false if the buffer was invalidated.
bool RenderToOverlay();
// Renders this image to the texture owner front buffer by first rendering
// it to the back buffer if it's not already there, and then waiting for the
// frame available event before calling UpdateTexImage().
bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode);
// Renders this image to the front buffer of its backing surface.
// Returns true if the buffer is in the front buffer. Returns false if the
// buffer was invalidated. After an image is invalidated it's no longer
// possible to render it.
bool RenderToFrontBuffer();
// Renders this image to the back buffer of its texture owner. Only valid if
// is_texture_owner_backed(). Returns true if the buffer is in the back
// buffer. Returns false if the buffer was invalidated.
// |blocking_mode| indicates whether this should (a) wait for any previously
// pending rendered frame before rendering this one, or (b) fail if a wait
// is required.
bool RenderToTextureOwnerBackBuffer(
BlockingMode blocking_mode = BlockingMode::kAllowBlocking);
// Whether the codec buffer has been rendered to the front buffer.
bool was_rendered_to_front_buffer() const {
return phase_ == Phase::kInFrontBuffer;
}
gfx::Size size() const { return output_buffer_->size(); }
bool was_tex_image_bound() const { return was_tex_image_bound_; }
scoped_refptr<gpu::TextureOwner> texture_owner() const {
return codec_buffer_wait_coordinator_
? codec_buffer_wait_coordinator_->texture_owner()
: nullptr;
}
CodecOutputBuffer* get_codec_output_buffer_for_testing() const {
return output_buffer_.get();
}
private:
// The lifecycle phases of an buffer.
// The only possible transitions are from left to right. Both
// kInFrontBuffer and kInvalidated are terminal.
enum class Phase { kInCodec, kInBackBuffer, kInFrontBuffer, kInvalidated };
void EnsureBoundIfNeeded(BindingsMode mode);
// The phase of the image buffer's lifecycle.
Phase phase_ = Phase::kInCodec;
// The buffer backing this image.
std::unique_ptr<CodecOutputBuffer> output_buffer_;
// The CodecBufferWaitCoordinator that |output_buffer_| will be rendered to.
// Or null, if this image is backed by an overlay.
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator_;
bool was_tex_image_bound_ = false;
};
} // namespace media
#endif // MEDIA_GPU_ANDROID_CODEC_OUTPUT_BUFFER_RENDERER_H
// Copyright 2019 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 "media/gpu/android/frame_info_helper.h"
#include "gpu/command_buffer/service/shared_image_video.h"
#include "gpu/ipc/service/command_buffer_stub.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
namespace media {
FrameInfoHelper::FrameInfo::FrameInfo() = default;
FrameInfoHelper::FrameInfo::~FrameInfo() = default;
FrameInfoHelper::FrameInfo::FrameInfo(FrameInfo&&) = default;
FrameInfoHelper::FrameInfo::FrameInfo(const FrameInfoHelper::FrameInfo&) =
default;
FrameInfoHelper::FrameInfo& FrameInfoHelper::FrameInfo::operator=(
const FrameInfoHelper::FrameInfo&) = default;
// Concrete implementation of FrameInfoHelper that renders output buffers and
// gets the FrameInfo they need.
class FrameInfoHelperImpl : public FrameInfoHelper,
public gpu::CommandBufferStub::DestructionObserver {
public:
FrameInfoHelperImpl(SharedImageVideoProvider::GetStubCB get_stub_cb) {
stub_ = get_stub_cb.Run();
if (stub_)
stub_->AddDestructionObserver(this);
}
~FrameInfoHelperImpl() override {
if (stub_)
stub_->RemoveDestructionObserver(this);
}
void GetFrameInfo(
std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
base::OnceCallback<
void(std::unique_ptr<CodecOutputBufferRenderer>, FrameInfo, bool)> cb)
override {
if (!buffer_renderer) {
std::move(cb).Run(nullptr, FrameInfo(), false);
return;
}
auto texture_owner = buffer_renderer->texture_owner();
FrameInfo info;
// Indicates that the FrameInfo is reliable and can be cached by caller.
// It's true if we either return cached values or we attempted to render
// frame and succeeded.
bool success = true;
// We default to visible size if if we can't get real size
info.coded_size = buffer_renderer->size();
info.visible_rect = gfx::Rect(info.coded_size);
if (texture_owner) {
if (visible_size_ == buffer_renderer->size()) {
info = frame_info_;
} else if (buffer_renderer->RenderToTextureOwnerFrontBuffer(
CodecOutputBufferRenderer::BindingsMode::
kDontRestoreIfBound)) {
visible_size_ = buffer_renderer->size();
texture_owner->GetCodedSizeAndVisibleRect(
visible_size_, &frame_info_.coded_size, &frame_info_.visible_rect);
frame_info_.ycbcr_info = GetYCbCrInfo(texture_owner.get());
info = frame_info_;
} else {
// We attempted to render frame and failed, mark request as failed so
// caller won't cache best-guess values.
success = false;
}
}
std::move(cb).Run(std::move(buffer_renderer), frame_info_, success);
}
void OnWillDestroyStub(bool have_context) override {
DCHECK(stub_);
stub_ = nullptr;
}
private:
// Gets YCbCrInfo from last rendered frame.
base::Optional<gpu::VulkanYCbCrInfo> GetYCbCrInfo(
gpu::TextureOwner* texture_owner) {
gpu::ContextResult result;
if (!stub_)
return base::nullopt;
auto shared_context =
stub_->channel()->gpu_channel_manager()->GetSharedContextState(&result);
auto context_provider =
(result == gpu::ContextResult::kSuccess) ? shared_context : nullptr;
if (!context_provider)
return base::nullopt;
return gpu::SharedImageVideo::GetYcbcrInfo(texture_owner, context_provider);
}
gpu::CommandBufferStub* stub_ = nullptr;
FrameInfo frame_info_;
gfx::Size visible_size_;
};
// static
base::SequenceBound<FrameInfoHelper> FrameInfoHelper::Create(
scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
SharedImageVideoProvider::GetStubCB get_stub_cb) {
return base::SequenceBound<FrameInfoHelperImpl>(std::move(gpu_task_runner),
std::move(get_stub_cb));
}
} // namespace media
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef MEDIA_GPU_ANDROID_YCBCR_HELPER_H_ #ifndef MEDIA_GPU_ANDROID_FRAME_INFO_HELPER_H_
#define MEDIA_GPU_ANDROID_YCBCR_HELPER_H_ #define MEDIA_GPU_ANDROID_FRAME_INFO_HELPER_H_
#include "base/optional.h" #include "base/optional.h"
#include "base/threading/sequence_bound.h" #include "base/threading/sequence_bound.h"
...@@ -14,35 +14,50 @@ ...@@ -14,35 +14,50 @@
namespace media { namespace media {
// Helper class to fetch YCbCrInfo for Vulkan from a CodecImage. // Helper class to fetch YCbCrInfo for Vulkan from a CodecImage.
class MEDIA_GPU_EXPORT YCbCrHelper { class MEDIA_GPU_EXPORT FrameInfoHelper {
public: public:
using OptionalInfo = base::Optional<gpu::VulkanYCbCrInfo>; struct FrameInfo {
FrameInfo();
~FrameInfo();
static base::SequenceBound<YCbCrHelper> Create( FrameInfo(FrameInfo&&);
FrameInfo(const FrameInfo&);
FrameInfo& operator=(const FrameInfo&);
gfx::Size coded_size;
gfx::Rect visible_rect;
base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info;
};
static base::SequenceBound<FrameInfoHelper> Create(
scoped_refptr<base::SequencedTaskRunner> gpu_task_runner, scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
SharedImageVideoProvider::GetStubCB get_stub_cb); SharedImageVideoProvider::GetStubCB get_stub_cb);
virtual ~YCbCrHelper() = default; virtual ~FrameInfoHelper() = default;
// Call |cb| with the YCbCrInfo (or nullopt, if we can't get it). Will render // Call |cb| with the FrameInfo. Will render |buffer_renderer| to the front
// |codec_image_holder| to the front buffer if it hasn't successfully gotten // buffer if we don't have frame info cached. For Vulkan this also will
// the YCbCrInfo on a previous call. Otherwise, will return the cached // attempt to get YCbCrInfo and cache it. If all necessary info is cached the
// YCbCrInfo and leave |codec_image_holder| unmodified. Once we call |cb| // call will leave buffer_renderer intact and it can be rendered later.
// with a non-nullopt YCbCrInfo, we will always return that same value; there // Rendering can fail for reasons. This function will make best efforts to
// is no need to call us afterwards. // fill FrameInfo which can be used to create VideoFrame, but shouldn't be
// cached by caller. Last parameter in |cb| is bool that indicates that info
// is reliable.
// //
// While this API might seem to be out of its Vulkan mind, it's this // While this API might seem to be out of its Vulkan mind, it's this
// complicated to (a) prevent rendering frames out of order to the front // complicated to (a) prevent rendering frames out of order to the front
// buffer, and (b) make it easy to handle the fact that sometimes, we just // buffer, and (b) make it easy to handle the fact that sometimes, we just
// can't get a YCbCrInfo from a CodecImage due to timeouts. // can't get a YCbCrInfo from a CodecImage due to timeouts.
virtual void GetYCbCrInfo( virtual void GetFrameInfo(
scoped_refptr<CodecImageHolder> codec_image_holder, std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
base::OnceCallback<void(OptionalInfo ycbcr_info)> cb) = 0; base::OnceCallback<void(std::unique_ptr<CodecOutputBufferRenderer>,
FrameInfo,
bool)> cb) = 0;
protected: protected:
YCbCrHelper() = default; FrameInfoHelper() = default;
}; };
} // namespace media } // namespace media
#endif // MEDIA_GPU_ANDROID_YCBCR_HELPER_H_ #endif // MEDIA_GPU_ANDROID_FRAME_INFO_HELPER_H_
...@@ -16,10 +16,10 @@ ...@@ -16,10 +16,10 @@
#include "media/gpu/android/codec_buffer_wait_coordinator.h" #include "media/gpu/android/codec_buffer_wait_coordinator.h"
#include "media/gpu/android/codec_image.h" #include "media/gpu/android/codec_image.h"
#include "media/gpu/android/codec_wrapper.h" #include "media/gpu/android/codec_wrapper.h"
#include "media/gpu/android/frame_info_helper.h"
#include "media/gpu/android/maybe_render_early_manager.h" #include "media/gpu/android/maybe_render_early_manager.h"
#include "media/gpu/android/shared_image_video_provider.h" #include "media/gpu/android/shared_image_video_provider.h"
#include "media/gpu/android/video_frame_factory.h" #include "media/gpu/android/video_frame_factory.h"
#include "media/gpu/android/ycbcr_helper.h"
#include "media/gpu/media_gpu_export.h" #include "media/gpu/media_gpu_export.h"
#include "ui/gl/gl_bindings.h" #include "ui/gl/gl_bindings.h"
...@@ -41,13 +41,18 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory { ...@@ -41,13 +41,18 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory {
base::OnceCallback<void(gpu::Mailbox mailbox, base::OnceCallback<void(gpu::Mailbox mailbox,
VideoFrame::ReleaseMailboxCB release_cb)>; VideoFrame::ReleaseMailboxCB release_cb)>;
using ImageWithInfoReadyCB =
base::OnceCallback<void(std::unique_ptr<CodecOutputBufferRenderer>,
FrameInfoHelper::FrameInfo,
SharedImageVideoProvider::ImageRecord)>;
// |get_stub_cb| will be run on |gpu_task_runner|. // |get_stub_cb| will be run on |gpu_task_runner|.
VideoFrameFactoryImpl( VideoFrameFactoryImpl(
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
const gpu::GpuPreferences& gpu_preferences, const gpu::GpuPreferences& gpu_preferences,
std::unique_ptr<SharedImageVideoProvider> image_provider, std::unique_ptr<SharedImageVideoProvider> image_provider,
std::unique_ptr<MaybeRenderEarlyManager> mre_manager, std::unique_ptr<MaybeRenderEarlyManager> mre_manager,
base::SequenceBound<YCbCrHelper> ycbcr_helper); base::SequenceBound<FrameInfoHelper> frame_info_helper);
~VideoFrameFactoryImpl() override; ~VideoFrameFactoryImpl() override;
void Initialize(OverlayMode overlay_mode, InitCB init_cb) override; void Initialize(OverlayMode overlay_mode, InitCB init_cb) override;
...@@ -68,6 +73,8 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory { ...@@ -68,6 +73,8 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory {
} }
private: private:
void RequestImage(std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
ImageWithInfoReadyCB image_ready_cb);
// ImageReadyCB that will construct a VideoFrame, and forward it to // ImageReadyCB that will construct a VideoFrame, and forward it to
// |output_cb| if construction succeeds. This is static for two reasons. // |output_cb| if construction succeeds. This is static for two reasons.
// First, we want to snapshot the state of the world when the request is made, // First, we want to snapshot the state of the world when the request is made,
...@@ -83,33 +90,23 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory { ...@@ -83,33 +90,23 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory {
base::WeakPtr<VideoFrameFactoryImpl> thiz, base::WeakPtr<VideoFrameFactoryImpl> thiz,
OnceOutputCB output_cb, OnceOutputCB output_cb,
base::TimeDelta timestamp, base::TimeDelta timestamp,
gfx::Size coded_size,
gfx::Size natural_size, gfx::Size natural_size,
std::unique_ptr<CodecOutputBuffer> output_buffer,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator, scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb, PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb,
VideoPixelFormat pixel_format, VideoPixelFormat pixel_format,
OverlayMode overlay_mode, OverlayMode overlay_mode,
bool enable_threaded_texture_mailboxes, bool enable_threaded_texture_mailboxes,
scoped_refptr<base::SequencedTaskRunner> gpu_task_runner, scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
FrameInfoHelper::FrameInfo frame_info,
SharedImageVideoProvider::ImageRecord record); SharedImageVideoProvider::ImageRecord record);
// Callback to receive YCbCrInfo from |provider_| while creating a VideoFrame. void CreateVideoFrame_OnFrameInfoReady(
void CreateVideoFrame_OnYCbCrInfo(base::OnceClosure completion_cb, ImageWithInfoReadyCB image_ready_cb,
YCbCrHelper::OptionalInfo ycbcr_info);
// Really create the VideoFrame, once we've tried to get the YCbCrInfo if it's
// needed for it.
void CreateVideoFrame_Finish(
OnceOutputCB output_cb,
base::TimeDelta timestamp,
gfx::Size coded_size,
gfx::Size natural_size,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator, scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
VideoPixelFormat pixel_format, std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
OverlayMode overlay_mode, FrameInfoHelper::FrameInfo frame_info,
bool enable_threaded_texture_mailboxes, bool success);
SharedImageVideoProvider::ImageRecord record);
MaybeRenderEarlyManager* mre_manager() const { return mre_manager_.get(); } MaybeRenderEarlyManager* mre_manager() const { return mre_manager_.get(); }
...@@ -131,11 +128,12 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory { ...@@ -131,11 +128,12 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory {
std::unique_ptr<MaybeRenderEarlyManager> mre_manager_; std::unique_ptr<MaybeRenderEarlyManager> mre_manager_;
// Sampler conversion information which is used in vulkan context. // Caches FrameInfo and visible size it was cached for.
YCbCrHelper::OptionalInfo ycbcr_info_; gfx::Size visible_size_;
FrameInfoHelper::FrameInfo frame_info_;
// Optional helper to get the Vulkan YCbCrInfo. // Optional helper to get the Vulkan YCbCrInfo.
base::SequenceBound<YCbCrHelper> ycbcr_helper_; base::SequenceBound<FrameInfoHelper> frame_info_helper_;
// The current image spec that we'll use to request images. // The current image spec that we'll use to request images.
SharedImageVideoProvider::ImageSpec image_spec_; SharedImageVideoProvider::ImageSpec image_spec_;
......
...@@ -41,21 +41,49 @@ class MockMaybeRenderEarlyManager : public MaybeRenderEarlyManager { ...@@ -41,21 +41,49 @@ class MockMaybeRenderEarlyManager : public MaybeRenderEarlyManager {
MOCK_METHOD0(MaybeRenderEarly, void()); MOCK_METHOD0(MaybeRenderEarly, void());
}; };
class MockYCbCrHelper : public YCbCrHelper, public DestructionObservable { class MockFrameInfoHelper : public FrameInfoHelper,
public DestructionObservable {
public: public:
MockYCbCrHelper(MockYCbCrHelper** thiz) { *thiz = this; } MockFrameInfoHelper(MockFrameInfoHelper** thiz) { *thiz = this; }
void GetYCbCrInfo( void GetFrameInfo(
scoped_refptr<CodecImageHolder> codec_image_holder, std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
base::OnceCallback<void(OptionalInfo ycbcr_info)> cb) override { base::OnceCallback<
MockGetYCbCrInfo(codec_image_holder); void(std::unique_ptr<CodecOutputBufferRenderer>, FrameInfo, bool)> cb)
override {
MockGetFrameInfo(buffer_renderer.get());
cb_ = std::move(cb); cb_ = std::move(cb);
buffer_renderer_ = std::move(buffer_renderer);
if (run_callback_automatically_) {
RunWithYcbCrInfo(true);
base::RunLoop().RunUntilIdle();
}
} }
MOCK_METHOD1(MockGetYCbCrInfo, void RunWithYcbCrInfo(bool success) {
void(scoped_refptr<CodecImageHolder> codec_image_holder)); DCHECK(buffer_renderer_);
FrameInfo info;
info.coded_size = buffer_renderer_->size();
info.visible_rect = gfx::Rect(info.coded_size);
std::move(cb_).Run(std::move(buffer_renderer_), info, success);
}
base::OnceCallback<void(OptionalInfo ycbcr_info)> cb_; void set_run_callback_automatically(bool run_callback_automatically) {
run_callback_automatically_ = run_callback_automatically;
}
MOCK_METHOD1(MockGetFrameInfo,
void(CodecOutputBufferRenderer* buffer_renderer));
private:
bool run_callback_automatically_ = true;
base::OnceCallback<
void(std::unique_ptr<CodecOutputBufferRenderer>, FrameInfo, bool)>
cb_;
std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer_;
}; };
class VideoFrameFactoryImplTest : public testing::Test { class VideoFrameFactoryImplTest : public testing::Test {
...@@ -68,8 +96,8 @@ class VideoFrameFactoryImplTest : public testing::Test { ...@@ -68,8 +96,8 @@ class VideoFrameFactoryImplTest : public testing::Test {
auto mre_manager = std::make_unique<MockMaybeRenderEarlyManager>(); auto mre_manager = std::make_unique<MockMaybeRenderEarlyManager>();
mre_manager_raw_ = mre_manager.get(); mre_manager_raw_ = mre_manager.get();
auto ycbcr_helper = auto ycbcr_helper = base::SequenceBound<MockFrameInfoHelper>(
base::SequenceBound<MockYCbCrHelper>(task_runner_, &ycbcr_helper_raw_); task_runner_, &ycbcr_helper_raw_);
base::RunLoop().RunUntilIdle(); // Init |ycbcr_helper_raw_|. base::RunLoop().RunUntilIdle(); // Init |ycbcr_helper_raw_|.
ycbcr_destruction_observer_ = ycbcr_destruction_observer_ =
ycbcr_helper_raw_->CreateDestructionObserver(); ycbcr_helper_raw_->CreateDestructionObserver();
...@@ -148,7 +176,7 @@ class VideoFrameFactoryImplTest : public testing::Test { ...@@ -148,7 +176,7 @@ class VideoFrameFactoryImplTest : public testing::Test {
// Sent to |impl_| by RequestVideoFrame.. // Sent to |impl_| by RequestVideoFrame..
base::MockCallback<VideoFrameFactory::OnceOutputCB> output_cb_; base::MockCallback<VideoFrameFactory::OnceOutputCB> output_cb_;
MockYCbCrHelper* ycbcr_helper_raw_ = nullptr; MockFrameInfoHelper* ycbcr_helper_raw_ = nullptr;
std::unique_ptr<DestructionObserver> ycbcr_destruction_observer_; std::unique_ptr<DestructionObserver> ycbcr_destruction_observer_;
gpu::GpuPreferences gpu_preferences_; gpu::GpuPreferences gpu_preferences_;
...@@ -244,77 +272,72 @@ TEST_F(VideoFrameFactoryImplTest, ...@@ -244,77 +272,72 @@ TEST_F(VideoFrameFactoryImplTest,
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
TEST_F(VideoFrameFactoryImplTest, DoesNotCallYCbCrHelperIfNotVulkan) { TEST_F(VideoFrameFactoryImplTest, DoesCallFrameInfoHelperIfVulkan) {
EXPECT_CALL(*ycbcr_helper_raw_, MockGetYCbCrInfo(_)).Times(0); // We will be driving callback by ourselves in this test.
RequestVideoFrame(); ycbcr_helper_raw_->set_run_callback_automatically(false);
auto image_record = MakeImageRecord(); // Expect call to get info for the first frame.
image_record.is_vulkan = false; EXPECT_CALL(*ycbcr_helper_raw_, MockGetFrameInfo(_)).Times(1);
image_provider_raw_->ProvideOneRequestedImage(&image_record);
base::RunLoop().RunUntilIdle();
}
TEST_F(VideoFrameFactoryImplTest, DoesCallYCbCrHelperIfVulkan) {
RequestVideoFrame(); RequestVideoFrame();
auto image_record = MakeImageRecord();
base::OnceCallback<void(YCbCrHelper::OptionalInfo)> cb; // Provide info. It should send image request.
EXPECT_CALL(*ycbcr_helper_raw_, ycbcr_helper_raw_->RunWithYcbCrInfo(true);
MockGetYCbCrInfo(image_record.codec_image_holder))
.Times(1);
image_record.is_vulkan = true;
image_provider_raw_->ProvideOneRequestedImage(&image_record);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
// Provide YCbCrInfo. It should provide the VideoFrame too. testing::Mock::VerifyAndClearExpectations(ycbcr_helper_raw_);
// Fulfilling image request should provide video frame.
EXPECT_CALL(output_cb_, Run(_)).Times(1); EXPECT_CALL(output_cb_, Run(_)).Times(1);
gpu::VulkanYCbCrInfo ycbcr;
std::move(ycbcr_helper_raw_->cb_).Run(ycbcr); auto image_record = MakeImageRecord();
image_provider_raw_->ProvideOneRequestedImage(&image_record);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
// It's okay if the ycbcr helper is destroyed. If not, then verify
// expectations explicitly now.
if (ycbcr_destruction_observer_->destructed())
ycbcr_helper_raw_ = nullptr;
else
testing::Mock::VerifyAndClearExpectations(ycbcr_helper_raw_);
// Verify that no more calls happen, since we don't want thread hops on every // Verify that no more calls happen, since we don't want thread hops on every
// frame. Note that multiple could be dispatched before now. It should still // frame. Note that multiple could be dispatched before now. It should still
// send along a VideoFrame, though. // send along a VideoFrame, though.
EXPECT_CALL(*ycbcr_helper_raw_, MockGetFrameInfo(_)).Times(0);
EXPECT_CALL(output_cb_, Run(_)).Times(1);
RequestVideoFrame(); RequestVideoFrame();
auto other_image_record = MakeImageRecord(); auto other_image_record = MakeImageRecord();
// If the helper hasn't been destroyed, then we don't expect it to be called. // If the helper hasn't been destroyed, then we don't expect it to be called.
if (ycbcr_helper_raw_)
EXPECT_CALL(*ycbcr_helper_raw_, MockGetYCbCrInfo(_)).Times(0);
EXPECT_CALL(output_cb_, Run(_)).Times(1);
image_provider_raw_->ProvideOneRequestedImage(&other_image_record); image_provider_raw_->ProvideOneRequestedImage(&other_image_record);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
TEST_F(VideoFrameFactoryImplTest, NullYCbCrInfoDoesntCrash) { TEST_F(VideoFrameFactoryImplTest, NullYCbCrInfoDoesntCrash) {
// Sending a null YCbCrInfo then requesting a frame shouldn't cause a crash. // We will be driving callback by ourselves in this test.
// See https://crbug.com/1007196 . ycbcr_helper_raw_->set_run_callback_automatically(false);
// Expect call to get info for the first frame.
EXPECT_CALL(*ycbcr_helper_raw_, MockGetFrameInfo(_)).Times(1);
RequestVideoFrame(); RequestVideoFrame();
// Provide info. It should send image request.
ycbcr_helper_raw_->RunWithYcbCrInfo(false);
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(ycbcr_helper_raw_);
// Fulfilling image request should provide video frame.
EXPECT_CALL(output_cb_, Run(_)).Times(1);
auto image_record = MakeImageRecord(); auto image_record = MakeImageRecord();
EXPECT_CALL(*ycbcr_helper_raw_,
MockGetYCbCrInfo(image_record.codec_image_holder))
.Times(1);
image_record.is_vulkan = true;
image_provider_raw_->ProvideOneRequestedImage(&image_record); image_provider_raw_->ProvideOneRequestedImage(&image_record);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
// Provide an empty YCbCrInfo. // Verify that we will get call to GetFrameInfo as previous one failed.
EXPECT_CALL(*ycbcr_helper_raw_, MockGetFrameInfo(_)).Times(1);
EXPECT_CALL(output_cb_, Run(_)).Times(1); EXPECT_CALL(output_cb_, Run(_)).Times(1);
std::move(ycbcr_helper_raw_->cb_).Run(base::nullopt);
base::RunLoop().RunUntilIdle();
// It shouldn't crash on the next frame. crbug.com/1007196
RequestVideoFrame(); RequestVideoFrame();
ycbcr_helper_raw_->RunWithYcbCrInfo(true);
base::RunLoop().RunUntilIdle();
auto other_image_record = MakeImageRecord(); auto other_image_record = MakeImageRecord();
other_image_record.is_vulkan = true; // If the helper hasn't been destroyed, then we don't expect it to be called.
// Should still call the helper, since it didn't get YCbCrInfo last time.
EXPECT_CALL(*ycbcr_helper_raw_,
MockGetYCbCrInfo(other_image_record.codec_image_holder))
.Times(1);
// Since we aren't sending YCbCr info, it won't call us back with a frame.
image_provider_raw_->ProvideOneRequestedImage(&other_image_record); image_provider_raw_->ProvideOneRequestedImage(&other_image_record);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
......
// Copyright 2019 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 "media/gpu/android/ycbcr_helper.h"
#include "gpu/command_buffer/service/shared_image_video.h"
#include "gpu/ipc/service/command_buffer_stub.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
namespace media {
// Concrete implementation of YCbCrHelper that renders output buffers and gets
// the YCbCrInfo they need.
class YCbCrHelperImpl : public YCbCrHelper,
public gpu::CommandBufferStub::DestructionObserver {
public:
YCbCrHelperImpl(SharedImageVideoProvider::GetStubCB get_stub_cb) {
stub_ = get_stub_cb.Run();
if (stub_)
stub_->AddDestructionObserver(this);
}
~YCbCrHelperImpl() override {
if (stub_)
stub_->RemoveDestructionObserver(this);
}
// YCbCrHelper
void GetYCbCrInfo(
scoped_refptr<CodecImageHolder> codec_image_holder,
base::OnceCallback<void(OptionalInfo ycbcr_info)> cb) override {
// If we don't have the info cached, then try to get it. If we have gotten
// it, then don't try again. Assume that our caller asked for it before it
// got the results back. We don't want to render more frames to the front
// buffer if we don't need to.
if (!ycbcr_info_)
ycbcr_info_ = RenderImageAndGetYCbCrInfo(std::move(codec_image_holder));
// Whether we got it or not, send it along.
std::move(cb).Run(ycbcr_info_);
}
void OnWillDestroyStub(bool have_context) override {
DCHECK(stub_);
stub_ = nullptr;
}
private:
// Render the codec output buffer, and use it to get the YCbCrInfo.
OptionalInfo RenderImageAndGetYCbCrInfo(
scoped_refptr<CodecImageHolder> codec_image_holder) {
gpu::ContextResult result;
if (!stub_)
return base::nullopt;
auto shared_context =
stub_->channel()->gpu_channel_manager()->GetSharedContextState(&result);
auto context_provider =
(result == gpu::ContextResult::kSuccess) ? shared_context : nullptr;
if (!context_provider)
return base::nullopt;
return gpu::SharedImageVideo::GetYcbcrInfo(
codec_image_holder->codec_image_raw(), context_provider);
}
gpu::CommandBufferStub* stub_ = nullptr;
OptionalInfo ycbcr_info_;
};
// static
base::SequenceBound<YCbCrHelper> YCbCrHelper::Create(
scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
SharedImageVideoProvider::GetStubCB get_stub_cb) {
return base::SequenceBound<YCbCrHelperImpl>(std::move(gpu_task_runner),
std::move(get_stub_cb));
}
} // namespace media
...@@ -241,8 +241,8 @@ std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder( ...@@ -241,8 +241,8 @@ std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder(
// ignored. If we can tell that here, then VideoFrameFactory can use it // ignored. If we can tell that here, then VideoFrameFactory can use it
// as a signal about whether it's supposed to get YCbCrInfo rather than // as a signal about whether it's supposed to get YCbCrInfo rather than
// requiring the provider to set |is_vulkan| in the ImageRecord. // requiring the provider to set |is_vulkan| in the ImageRecord.
auto ycbcr_helper = auto frame_info_helper =
YCbCrHelper::Create(gpu_task_runner_, std::move(get_stub_cb)); FrameInfoHelper::Create(gpu_task_runner_, std::move(get_stub_cb));
video_decoder = std::make_unique<MediaCodecVideoDecoder>( video_decoder = std::make_unique<MediaCodecVideoDecoder>(
gpu_preferences_, gpu_feature_info_, media_log->Clone(), gpu_preferences_, gpu_feature_info_, media_log->Clone(),
DeviceInfo::GetInstance(), DeviceInfo::GetInstance(),
...@@ -253,7 +253,7 @@ std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder( ...@@ -253,7 +253,7 @@ std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder(
std::make_unique<VideoFrameFactoryImpl>( std::make_unique<VideoFrameFactoryImpl>(
gpu_task_runner_, gpu_preferences_, std::move(image_provider), gpu_task_runner_, gpu_preferences_, std::move(image_provider),
MaybeRenderEarlyManager::Create(gpu_task_runner_), MaybeRenderEarlyManager::Create(gpu_task_runner_),
std::move(ycbcr_helper))); std::move(frame_info_helper)));
#elif BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION) #elif BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
if (IsNewAcceleratedVideoDecoderUsed(gpu_preferences_)) { if (IsNewAcceleratedVideoDecoderUsed(gpu_preferences_)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment