Index: media/gpu/android_video_decode_accelerator_unittest.cc |
diff --git a/media/gpu/android_video_decode_accelerator_unittest.cc b/media/gpu/android_video_decode_accelerator_unittest.cc |
index f8c2aa85a6614e7578cbbc588266463f8f41aece..bba00b8faf13d2a09bd8a711dd92133e80e02410 100644 |
--- a/media/gpu/android_video_decode_accelerator_unittest.cc |
+++ b/media/gpu/android_video_decode_accelerator_unittest.cc |
@@ -11,24 +11,33 @@ |
#include "base/android/jni_android.h" |
#include "base/bind.h" |
#include "base/logging.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/memory/weak_ptr.h" |
#include "base/message_loop/message_loop.h" |
#include "base/run_loop.h" |
#include "base/threading/thread_task_runner_handle.h" |
#include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h" |
#include "media/base/android/media_codec_util.h" |
#include "media/base/android/media_jni_registrar.h" |
+#include "media/base/android/mock_android_overlay.h" |
+#include "media/base/android/mock_media_codec_bridge.h" |
#include "media/gpu/android_video_decode_accelerator.h" |
#include "media/gpu/avda_codec_allocator.h" |
+#include "media/gpu/avda_overlay_helper.h" |
#include "media/video/picture.h" |
#include "media/video/video_decode_accelerator.h" |
#include "testing/gmock/include/gmock/gmock.h" |
#include "testing/gtest/include/gtest/gtest.h" |
+#include "ui/gl/android/surface_texture.h" |
#include "ui/gl/gl_context.h" |
#include "ui/gl/gl_surface.h" |
#include "ui/gl/init/gl_factory.h" |
using ::testing::_; |
using ::testing::NiceMock; |
+using ::testing::NotNull; |
+using ::testing::Return; |
+using ::testing::StrictMock; |
namespace media { |
namespace { |
@@ -48,43 +57,240 @@ base::WeakPtr<gpu::gles2::GLES2Decoder> GetGLES2Decoder( |
return decoder; |
} |
-ACTION(PostNullCodec) { |
- base::ThreadTaskRunnerHandle::Get()->PostTask( |
- FROM_HERE, base::Bind(&media::AVDACodecAllocatorClient::OnCodecConfigured, |
- arg0, nullptr)); |
-} |
- |
} // namespace |
-class MockVDAClient : public VideoDecodeAccelerator::Client { |
+class AndroidVideoDecodeAcceleratorTest : public testing::Test { |
public: |
- MOCK_METHOD1(NotifyInitializationComplete, void(bool)); |
- MOCK_METHOD5( |
- ProvidePictureBuffers, |
- void(uint32_t, VideoPixelFormat, uint32_t, const gfx::Size&, uint32_t)); |
- MOCK_METHOD1(DismissPictureBuffer, void(int32_t)); |
- MOCK_METHOD1(PictureReady, void(const Picture&)); |
- MOCK_METHOD1(NotifyEndOfBitstreamBuffer, void(int32_t)); |
- MOCK_METHOD0(NotifyFlushDone, void()); |
- MOCK_METHOD0(NotifyResetDone, void()); |
- MOCK_METHOD1(NotifyError, void(VideoDecodeAccelerator::Error)); |
-}; |
+ class MockVDAClient : public VideoDecodeAccelerator::Client { |
+ public: |
+ MockVDAClient() {} |
-class MockCodecAllocator : public AVDACodecAllocator { |
- public: |
- MOCK_METHOD2(CreateMediaCodecAsync, |
- void(base::WeakPtr<AVDACodecAllocatorClient>, |
- scoped_refptr<CodecConfig>)); |
-}; |
+ MOCK_METHOD1(NotifyInitializationComplete, void(bool)); |
+ MOCK_METHOD5( |
+ ProvidePictureBuffers, |
+ void(uint32_t, VideoPixelFormat, uint32_t, const gfx::Size&, uint32_t)); |
+ MOCK_METHOD1(DismissPictureBuffer, void(int32_t)); |
+ MOCK_METHOD1(PictureReady, void(const Picture&)); |
+ MOCK_METHOD1(NotifyEndOfBitstreamBuffer, void(int32_t)); |
+ MOCK_METHOD0(NotifyFlushDone, void()); |
+ MOCK_METHOD0(NotifyResetDone, void()); |
+ MOCK_METHOD1(NotifyError, void(VideoDecodeAccelerator::Error)); |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(MockVDAClient); |
+ }; |
+ |
+ // Codec allocator that lets you count calls on Mock* methods, and also |
+ // lets you answer the codec. |
+ class FakeCodecAllocator : public StrictMock<AVDACodecAllocator> { |
+ public: |
+ FakeCodecAllocator() {} |
+ |
+ // These are called with some parameters of the codec config by our |
+ // implementatin of their respective functions. This allows tests to set |
+ // expectations on them. |
+ MOCK_METHOD2(MockCreateMediaCodecSync, |
+ void(AndroidOverlay*, gl::SurfaceTexture*)); |
+ MOCK_METHOD2(MockCreateMediaCodecAsync, |
+ void(AndroidOverlay*, gl::SurfaceTexture*)); |
+ |
+ // Note that this doesn't exactly match the signature, since unique_ptr |
+ // doesn't work. plus, we expand |surface_bundle| a bit to make it more |
+ // convenient to set expectations. |
+ MOCK_METHOD3(MockReleaseMediaCodec, |
+ void(MediaCodecBridge*, AndroidOverlay*, gl::SurfaceTexture*)); |
+ |
+ std::unique_ptr<MediaCodecBridge> CreateMediaCodecSync( |
+ scoped_refptr<CodecConfig> codec_config) override { |
+ MockCreateMediaCodecSync( |
+ codec_config->surface_bundle->overlay.get(), |
+ codec_config->surface_bundle->surface_texture.get()); |
+ |
+ CopyCodecAllocParams(codec_config); |
+ |
+ std::unique_ptr<MockMediaCodecBridge> codec; |
+ if (allow_sync_creation) |
+ codec = base::MakeUnique<MockMediaCodecBridge>(); |
+ |
+ if (codec) |
+ most_recent_codec_ = codec->GetWeakPtr(); |
+ else |
+ most_recent_codec_ = nullptr; |
+ |
+ return std::move(codec); |
+ } |
+ |
+ void CreateMediaCodecAsync(base::WeakPtr<AVDACodecAllocatorClient> client, |
+ scoped_refptr<CodecConfig> config) override { |
+ // Clear |most_recent_codec_| until somebody calls Provide*CodecAsync(). |
+ most_recent_codec_ = nullptr; |
+ CopyCodecAllocParams(config); |
+ client_ = client; |
+ |
+ MockCreateMediaCodecAsync(most_recent_overlay(), |
+ most_recent_surface_texture()); |
+ } |
+ |
+ void ReleaseMediaCodec( |
+ std::unique_ptr<MediaCodecBridge> media_codec, |
+ TaskType task_type, |
+ const scoped_refptr<AVDASurfaceBundle>& surface_bundle) override { |
+ MockReleaseMediaCodec(media_codec.get(), surface_bundle->overlay.get(), |
+ surface_bundle->surface_texture.get()); |
+ } |
+ |
+ // Provide AVDA with a mock codec. |
+ void ProvideMockCodecAsync() { |
+ // If AVDA hasn't requested a codec, then get mad. |
+ ASSERT_TRUE(client_); |
+ if (!client_) |
+ return; |
+ |
+ std::unique_ptr<MockMediaCodecBridge> codec = |
+ base::MakeUnique<MockMediaCodecBridge>(); |
+ most_recent_codec_ = codec->GetWeakPtr(); |
+ client_->OnCodecConfigured(std::move(codec)); |
+ } |
+ |
+ // Provide AVDA will a null codec. |
+ void ProvideNullCodecAsync() { |
+ // If AVDA hasn't requested a codec, then get mad. |
+ ASSERT_TRUE(client_); |
+ if (!client_) |
+ return; |
+ |
+ most_recent_codec_ = nullptr; |
+ client_->OnCodecConfigured(nullptr); |
+ } |
+ |
+ // Return the most recent bundle that we've received for codec allocation. |
+ scoped_refptr<AVDASurfaceBundle> most_recent_bundle() { |
+ return most_recent_bundle_; |
+ } |
+ |
+ // Return the most recent codec that we provided, or null if it has been |
+ // freed already. |
+ MockMediaCodecBridge* most_recent_codec() { |
+ return most_recent_codec_.get(); |
+ } |
+ |
+ // Return the most recent overlay / etc. that we were given during codec |
+ // allocation (sync or async). |
+ AndroidOverlay* most_recent_overlay() { return most_recent_overlay_; } |
+ gl::SurfaceTexture* most_recent_surface_texture() { |
+ return most_recent_surface_texture_; |
+ } |
+ |
+ // Most recent codec that we've created via CreateMockCodec, since we have |
+ // to assign ownership. |
+ base::WeakPtr<MockMediaCodecBridge> most_recent_codec_; |
+ |
+ // Most recent surface bundle that we've gotten during codec allocation. |
+ // This should be the same as |config_->surface_bundle| initially, but AVDA |
+ // might change it. |
+ scoped_refptr<AVDASurfaceBundle> most_recent_bundle_; |
+ |
+ // Most recent overlay provided during codec allocation. |
+ AndroidOverlay* most_recent_overlay_ = nullptr; |
+ |
+ // Most recent surface texture provided during codec allocation. |
+ gl::SurfaceTexture* most_recent_surface_texture_ = nullptr; |
+ |
+ // Should a call to CreateMediaCodecSync succeed? |
+ bool allow_sync_creation = true; |
+ |
+ private: |
+ // Copy |config| and all of the parameters we care about. We don't leave |
+ // them all in config, since AVDA could change them at any time. |
+ void CopyCodecAllocParams(scoped_refptr<CodecConfig> config) { |
+ config_ = config; |
+ most_recent_bundle_ = config->surface_bundle; |
+ most_recent_overlay_ = config->surface_bundle->overlay.get(); |
+ |
+ most_recent_surface_texture_ = |
+ config->surface_bundle->surface_texture.get(); |
+ } |
+ |
+ base::WeakPtr<AVDACodecAllocatorClient> client_; |
+ scoped_refptr<CodecConfig> config_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(FakeCodecAllocator); |
+ }; |
+ |
+ // Overlay helper that calls mocked functions to allow counting calls, but |
+ // also records arguments. Note that gmock can't mock out unique_ptrs |
+ // anyway, so we can't mock AVDAOverlayHelper directly. |
+ class FakeOverlayHelper : public StrictMock<AVDAOverlayHelper> { |
+ public: |
+ FakeOverlayHelper() : weak_factory_(this) {} |
+ |
+ // These are called by the real functions. You may set expectations on |
+ // them if you like. |
+ MOCK_METHOD0(MockStartup, void()); |
+ MOCK_METHOD0(MockOnOverlayFactory, void()); |
+ |
+ base::WeakPtr<FakeOverlayHelper> GetWeakPtr() { |
+ return weak_factory_.GetWeakPtr(); |
+ } |
+ |
+ void Startup( |
+ const UseOverlayCB& use_overlay_cb, |
+ const UseSurfaceTextureCB& use_surface_texture_cb, |
+ const StopUsingOverlayImmediatelyCB& stop_immediately_cb, |
+ std::unique_ptr<AndroidOverlayFactory> initial_factory) override { |
+ MockStartup(); |
+ |
+ factory_ = std::move(initial_factory); |
+ use_overlay_cb_ = use_overlay_cb; |
+ use_surface_texture_cb_ = use_surface_texture_cb; |
+ stop_immediately_cb_ = stop_immediately_cb; |
+ } |
+ |
+ void OnOverlayFactory( |
+ std::unique_ptr<AndroidOverlayFactory> factory) override { |
+ MockOnOverlayFactory(); |
+ factory_ = std::move(factory); |
+ } |
+ |
+ // Notify AVDA to use a surface texture. |
+ void ProvideSurfaceTexture() { |
+ use_surface_texture_cb_.Run(); |
+ base::RunLoop().RunUntilIdle(); |
+ } |
+ |
+ void ProvideOverlay(std::unique_ptr<AndroidOverlay> overlay) { |
+ use_overlay_cb_.Run(std::move(overlay)); |
+ base::RunLoop().RunUntilIdle(); |
+ } |
+ |
+ void StopImmediately(AndroidOverlay* overlay) { |
+ stop_immediately_cb_.Run(overlay); |
+ base::RunLoop().RunUntilIdle(); |
+ } |
+ |
+ UseOverlayCB use_overlay_cb_; |
+ UseSurfaceTextureCB use_surface_texture_cb_; |
+ StopUsingOverlayImmediatelyCB stop_immediately_cb_; |
+ |
+ std::unique_ptr<AndroidOverlayFactory> factory_; |
+ |
+ base::WeakPtrFactory<FakeOverlayHelper> weak_factory_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(FakeOverlayHelper); |
+ }; |
+ |
+ // We pick this profile since it's always supported. |
+ AndroidVideoDecodeAcceleratorTest() : config_(H264PROFILE_BASELINE) {} |
-class AndroidVideoDecodeAcceleratorTest : public testing::Test { |
- public: |
~AndroidVideoDecodeAcceleratorTest() override {} |
void SetUp() override { |
JNIEnv* env = base::android::AttachCurrentThread(); |
RegisterJni(env); |
+ main_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
+ |
gl::init::ShutdownGL(); |
ASSERT_TRUE(gl::init::InitializeGLOneOff()); |
surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size(16, 16)); |
@@ -92,59 +298,424 @@ class AndroidVideoDecodeAcceleratorTest : public testing::Test { |
gl::GLContextAttribs()); |
context_->MakeCurrent(surface_.get()); |
- vda_.reset(new AndroidVideoDecodeAccelerator( |
- &codec_allocator_, base::Bind(&MakeContextCurrent), |
- base::Bind(&GetGLES2Decoder, gl_decoder_.AsWeakPtr()))); |
+ helper_that_is_usually_null_ = base::MakeUnique<FakeOverlayHelper>(); |
+ helper_ = helper_that_is_usually_null_->GetWeakPtr(); |
+ |
+ platform_config_.sdk_int = base::android::SDK_VERSION_MARSHMALLOW; |
+ platform_config_.allow_setsurface = true; |
+ platform_config_.force_deferred_surface_creation = false; |
+ |
+ // By default, allow deferred init. |
+ config_.is_deferred_initialization_allowed = true; |
+ } |
+ |
+ // Create and initialize AVDA with |config_|, and return the result. |
+ bool InitializeAVDA() { |
+ // Because VDA has a custom deleter, we must assign it to |vda_| carefully. |
+ AndroidVideoDecodeAccelerator* avda = new AndroidVideoDecodeAccelerator( |
+ &codec_allocator_, std::move(helper_that_is_usually_null_), |
+ base::Bind(&MakeContextCurrent), |
+ base::Bind(&GetGLES2Decoder, gl_decoder_.AsWeakPtr()), |
+ &platform_config_); |
+ avda_ = avda->GetWeakPtr(); |
+ vda_.reset(avda); |
+ |
+ bool result = vda_->Initialize(config_, &client_); |
+ base::RunLoop().RunUntilIdle(); |
+ return result; |
+ } |
+ |
+ // Initialize |vda_|, providing a new surface for it. You may get the surface |
+ // by asking |codec_allocator_|. |
+ void InitializeAVDAWithOverlay() { |
+ EXPECT_CALL(*helper_, MockStartup()); |
+ |
+ config_.surface_id = 123; |
+ ASSERT_TRUE(InitializeAVDA()); |
+ base::RunLoop().RunUntilIdle(); |
+ ASSERT_TRUE(helper_->factory_ != nullptr); |
+ |
+ // Have the factory provide an overlay, and verify that codec creation is |
+ // provided with that overlay. |
+ std::unique_ptr<MockAndroidOverlay> overlay = |
+ base::MakeUnique<MockAndroidOverlay>(); |
+ // Set the expectations first, since ProvideOverlay might cause callbacks. |
+ EXPECT_CALL(codec_allocator_, |
+ MockCreateMediaCodecAsync(overlay.get(), nullptr)); |
+ helper_->ProvideOverlay(std::move(overlay)); |
+ |
+ // Provide the codec so that we can check if it's freed properly. |
+ EXPECT_CALL(client_, NotifyInitializationComplete(true)); |
+ codec_allocator_.ProvideMockCodecAsync(); |
+ base::RunLoop().RunUntilIdle(); |
+ } |
+ |
+ void InitializeAVDAWithSurfaceTexture() { |
+ EXPECT_CALL(*helper_, MockStartup()); |
+ |
+ ASSERT_TRUE(InitializeAVDA()); |
+ base::RunLoop().RunUntilIdle(); |
+ // We do not expect a factory, since we are using SurfaceTexture. |
+ ASSERT_TRUE(helper_->factory_ == nullptr); |
+ |
+ // Set the expectations first, since ProvideOverlay might cause callbacks. |
+ EXPECT_CALL(codec_allocator_, |
+ MockCreateMediaCodecAsync(nullptr, NotNull())); |
+ helper_->ProvideSurfaceTexture(); |
+ |
+ // Provide the codec so that we can check if it's freed properly. |
+ EXPECT_CALL(client_, NotifyInitializationComplete(true)); |
+ codec_allocator_.ProvideMockCodecAsync(); |
+ base::RunLoop().RunUntilIdle(); |
+ } |
+ |
+ // Set whether HasUnrendereredPictureBuffers will return true or false. |
+ // TODO(liberato): We can't actually do this yet. It turns out to be okay, |
+ // because AVDA doesn't actually SetSurface before DequeueOutput. It could do |
+ // so, though, if there aren't unrendered buffers. Should AVDA ever start |
+ // switching surfaces immediately upon receiving them, rather than waiting for |
+ // DequeueOutput, then we'll want to be able to indicate that it has |
+ // unrendered pictures to prevent that behavior. |
+ void SetHasUnrenderedPictureBuffers(bool flag) {} |
+ |
+ // Tell |avda_| to switch surfaces to its incoming surface. This is a method |
+ // since we're a friend of AVDA, and the tests are subclasses. It's also |
+ // somewhat hacky, but much less hacky than trying to run it via a timer. |
+ void LetAVDAUpdateSurface() { |
+ SetHasUnrenderedPictureBuffers(false); |
+ avda_->DequeueOutput(); |
} |
base::MessageLoop message_loop_; |
scoped_refptr<gl::GLSurface> surface_; |
scoped_refptr<gl::GLContext> context_; |
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; |
NiceMock<gpu::gles2::MockGLES2Decoder> gl_decoder_; |
- NiceMock<MockVDAClient> client_; |
- NiceMock<MockCodecAllocator> codec_allocator_; |
+ StrictMock<MockVDAClient> client_; |
+ FakeCodecAllocator codec_allocator_; |
+ VideoDecodeAccelerator::Config config_; |
+ |
+ AndroidVideoDecodeAccelerator::PlatformConfig platform_config_; |
+ |
+ // We maintain a weak ref to this since AVDA owns it. |
+ base::WeakPtr<FakeOverlayHelper> helper_; |
// This must be a unique pointer to a VDA, not an AVDA, to ensure the |
// the default_delete specialization that calls Destroy() will be used. |
std::unique_ptr<VideoDecodeAccelerator> vda_; |
+ |
+ // Weak ptr to |vda_| as an AVDA. |
+ base::WeakPtr<AndroidVideoDecodeAccelerator> avda_; |
+ |
+ private: |
+ // This is the object that |helper_| points to, or nullptr once we assign |
+ // ownership to AVDA. |
+ std::unique_ptr<FakeOverlayHelper> helper_that_is_usually_null_; |
}; |
TEST_F(AndroidVideoDecodeAcceleratorTest, ConfigureUnsupportedCodec) { |
SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); |
- ASSERT_FALSE(vda_->Initialize( |
- VideoDecodeAccelerator::Config(VIDEO_CODEC_PROFILE_UNKNOWN), &client_)); |
+ config_ = VideoDecodeAccelerator::Config(VIDEO_CODEC_PROFILE_UNKNOWN); |
+ ASSERT_FALSE(InitializeAVDA()); |
} |
-TEST_F(AndroidVideoDecodeAcceleratorTest, ConfigureSupportedCodec) { |
+TEST_F(AndroidVideoDecodeAcceleratorTest, |
+ ConfigureSupportedCodecSynchronously) { |
SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); |
- // H264 is always supported by AVDA. |
- ASSERT_TRUE(vda_->Initialize( |
- VideoDecodeAccelerator::Config(H264PROFILE_BASELINE), &client_)); |
+ config_.is_deferred_initialization_allowed = false; |
+ |
+ EXPECT_CALL(codec_allocator_, MockCreateMediaCodecSync(nullptr, NotNull())); |
watk
2017/04/22 00:29:07
This looks like noise caused by StrictMock.
liberato (no reviews please)
2017/04/27 18:30:12
i want to be sure that it uses the sync path for c
|
+ ASSERT_TRUE(InitializeAVDA()); |
} |
-TEST_F(AndroidVideoDecodeAcceleratorTest, FailingToCreateACodecIsAnError) { |
- EXPECT_CALL(codec_allocator_, CreateMediaCodecAsync(_, _)) |
- .WillOnce(PostNullCodec()); |
+TEST_F(AndroidVideoDecodeAcceleratorTest, FailingToCreateACodecSyncIsAnError) { |
+ // Failuew to create a codec during sync init should cause Initialize to fail. |
+ SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); |
+ |
+ config_.is_deferred_initialization_allowed = false; |
+ codec_allocator_.allow_sync_creation = false; |
+ |
+ EXPECT_CALL(codec_allocator_, MockCreateMediaCodecSync(nullptr, NotNull())); |
+ ASSERT_FALSE(InitializeAVDA()); |
+} |
+ |
+TEST_F(AndroidVideoDecodeAcceleratorTest, FailingToCreateACodecAsyncIsAnError) { |
+ // Verify that a null codec signals error for async init when it doesn't get a |
+ // mediacodec instance. |
+ // |
+ // Also assert that there's only one call to CreateMediaCodecAsync. And since |
+ // it replies with a null codec, AVDA will be in an error state when it shuts |
+ // down. Since we know that it's constructed before we destroy the VDA, we |
+ // verify that AVDA doens't create codecs during destruction. |
+ SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); |
+ |
+ // Note that if we somehow end up deferring surface creation, then this would |
+ // no longer be expected to fail. It would signal success before asking for a |
+ // surface or codec. |
+ EXPECT_CALL(*helper_, MockStartup()); |
watk
2017/04/22 00:29:07
Seems like noise.
liberato (no reviews please)
2017/04/27 18:30:12
Done.
|
+ EXPECT_CALL(codec_allocator_, MockCreateMediaCodecAsync(_, NotNull())); |
EXPECT_CALL(client_, NotifyInitializationComplete(false)); |
- VideoDecodeAccelerator::Config config(H264PROFILE_BASELINE); |
- config.is_deferred_initialization_allowed = true; |
- vda_->Initialize(config, &client_); |
+ ASSERT_TRUE(InitializeAVDA()); |
+ helper_->ProvideSurfaceTexture(); |
+ codec_allocator_.ProvideNullCodecAsync(); |
+ |
+ // Make sure that codec allocation has happened before destroying the VDA. |
+ testing::Mock::VerifyAndClearExpectations(&codec_allocator_); |
+ |
+ vda_ = nullptr; |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+TEST_F(AndroidVideoDecodeAcceleratorTest, |
+ LowEndDevicesSucceedInitWithoutASurface) { |
+ // If AVDA decides that we should defer surface creation, then it should |
+ // signal success before we provide a surface. It should still ask for a |
+ // surface, though. |
+ SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); |
+ |
+ // It would be nicer if we didn't just force this on, since we might do so |
+ // in a state that AVDA isn't supposed to handle (e.g., if we give it a |
+ // surface, then it would never decide to defer surface creation). |
+ platform_config_.force_deferred_surface_creation = true; |
+ config_.surface_id = SurfaceManager::kNoSurfaceID; |
+ |
+ EXPECT_CALL(*helper_, MockStartup()).Times(0); |
+ EXPECT_CALL(client_, NotifyInitializationComplete(true)); |
+ |
+ InitializeAVDA(); |
+} |
+ |
+TEST_F(AndroidVideoDecodeAcceleratorTest, WillStartHelperImmediately) { |
+ // AVDA should initialize |helper_| immediately if it's not waiting for a Cdm. |
+ // It should provide a null factory, since |config_| defaults to |
+ // SurfaceTexture. We also expect that AVDA will not create a codec before it |
+ // gets a surface, nor will it signal deferred init success to us. |
+ SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); |
+ |
+ EXPECT_CALL(*helper_, MockStartup()); |
+ EXPECT_CALL(codec_allocator_, MockCreateMediaCodecAsync(_, NotNull())) |
+ .Times(0); |
+ EXPECT_CALL(client_, NotifyInitializationComplete(_)).Times(0); |
+ |
+ InitializeAVDA(); |
+ ASSERT_TRUE(helper_->factory_ == nullptr); |
+} |
+ |
+TEST_F(AndroidVideoDecodeAcceleratorTest, |
+ AsyncInitWithSurfaceTextureAndDelete) { |
+ // When configuring with a SurfaceTexture and deferred init, we should be |
+ // asked for a codec, and be notified of init success if we provide one. When |
+ // AVDA is destroyed, it should release the codec and surface texture. |
+ SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); |
+ |
+ InitializeAVDAWithSurfaceTexture(); |
+ |
+ // Delete the VDA, and make sure that it tries to free the codec and the right |
+ // surface texture. |
+ EXPECT_CALL( |
+ codec_allocator_, |
+ MockReleaseMediaCodec(codec_allocator_.most_recent_codec(), |
+ codec_allocator_.most_recent_overlay(), |
+ codec_allocator_.most_recent_surface_texture())); |
+ vda_ = nullptr; |
base::RunLoop().RunUntilIdle(); |
} |
-TEST_F(AndroidVideoDecodeAcceleratorTest, NoCodecsAreCreatedDuringDestruction) { |
- // Assert that there's only one call to CreateMediaCodecAsync. And since it |
- // replies with a null codec, AVDA will be in an error state when it shuts |
- // down. |
- EXPECT_CALL(codec_allocator_, CreateMediaCodecAsync(_, _)) |
- .WillOnce(PostNullCodec()); |
+TEST_F(AndroidVideoDecodeAcceleratorTest, AsyncInitWithSurfaceAndDelete) { |
+ // When |config_| specifies a surface, we should be given a factory during |
+ // startup for it. When |helper_| provides an overlay, the codec should be |
+ // allocated using it. Shutdown should provide the overlay when releasing the |
+ // media codec. |
+ SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); |
+ |
+ InitializeAVDAWithOverlay(); |
+ |
+ // Delete the VDA, and make sure that it tries to free the codec and the |
+ // overlay that it provided to us. |
+ EXPECT_CALL( |
+ codec_allocator_, |
+ MockReleaseMediaCodec(codec_allocator_.most_recent_codec(), |
+ codec_allocator_.most_recent_overlay(), |
+ codec_allocator_.most_recent_surface_texture())); |
+ vda_ = nullptr; |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+TEST_F(AndroidVideoDecodeAcceleratorTest, |
+ SwitchesToSurfaceTextureWhenSurfaceDestroyed) { |
+ // Provide a surface, and a codec, then destroy the surface. AVDA should use |
+ // SetSurface to switch to SurfaceTexture. |
+ SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); |
+ |
+ InitializeAVDAWithOverlay(); |
+ |
+ // It would be nice if we knew that this was a surface texture. As it is, we |
+ // just destroy the VDA and expect that we're provided with one. Hopefully, |
+ // AVDA is actually calling SetSurface properly. |
+ EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_)) |
+ .WillOnce(Return(true)); |
+ helper_->StopImmediately(codec_allocator_.most_recent_overlay()); |
+ |
+ EXPECT_CALL(codec_allocator_, |
+ MockReleaseMediaCodec(codec_allocator_.most_recent_codec(), |
+ nullptr, NotNull())); |
+ vda_ = nullptr; |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+TEST_F(AndroidVideoDecodeAcceleratorTest, SwitchesToSurfaceTextureEventually) { |
+ // Provide a surface, and a codec, then request that AVDA switches to a |
+ // surface texture. Verify that it does. |
+ SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); |
+ |
+ InitializeAVDAWithOverlay(); |
+ |
+ EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_)) |
+ .WillOnce(Return(true)); |
+ |
+ // Note that it's okay if |avda_| switches before ProvideSurfaceTexture |
+ // returns, since it has no queued output anyway. |
+ helper_->ProvideSurfaceTexture(); |
+ LetAVDAUpdateSurface(); |
+ |
+ // Verify that we're now using some surface texture. |
+ EXPECT_CALL(codec_allocator_, |
+ MockReleaseMediaCodec(codec_allocator_.most_recent_codec(), |
+ nullptr, NotNull())); |
+ vda_ = nullptr; |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+TEST_F(AndroidVideoDecodeAcceleratorTest, |
+ SetSurfaceFailureDoesntSwitchSurfaces) { |
+ // Initialize AVDA with a surface, then request that AVDA switches to a |
+ // surface texture. When it tries to UpdateSurface, pretend to fail. AVDA |
+ // should notify error, and also release the original surface. |
+ SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); |
+ |
+ InitializeAVDAWithOverlay(); |
+ |
+ EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_)) |
+ .WillOnce(Return(false)); |
+ EXPECT_CALL(client_, |
+ NotifyError(AndroidVideoDecodeAccelerator::PLATFORM_FAILURE)) |
+ .Times(1); |
+ helper_->ProvideSurfaceTexture(); |
+ |
+ // Verify that we're still using an overlay. |
+ EXPECT_CALL(codec_allocator_, |
+ MockReleaseMediaCodec(codec_allocator_.most_recent_codec(), |
+ NotNull(), nullptr)); |
+ |
+ // Also destroy the VDA to be sure that it cleans up the codec. It can do so |
+ // earlier if it wants to, but isn't required to. |
+ LetAVDAUpdateSurface(); |
+ vda_ = nullptr; |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+TEST_F(AndroidVideoDecodeAcceleratorTest, |
+ SwitchToSurfaceAndBackBeforeSetSurface) { |
+ // Ask AVDA to switch from ST to overlay, then back to ST before it has a |
+ // chance to do the first switch. It should simply drop the overlay. |
+ SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); |
+ |
+ InitializeAVDAWithSurfaceTexture(); |
+ |
+ // Don't let AVDA switch immediately, else it could choose to SetSurface when |
+ // it first gets the overlay. |
+ SetHasUnrenderedPictureBuffers(true); |
+ EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_)).Times(0); |
+ std::unique_ptr<MockAndroidOverlay> overlay = |
+ base::MakeUnique<MockAndroidOverlay>(); |
+ auto overlay_weak = overlay->GetWeakPtr(); |
+ helper_->ProvideOverlay(std::move(overlay)); |
+ // ProvideOverlay should not drop |overlay_weak| yet. |
+ ASSERT_TRUE(overlay_weak.get() != nullptr); |
+ |
+ // While the incoming surface is pending, switch back to SurfaceTexture. |
+ helper_->ProvideSurfaceTexture(); |
+ // ProvideOverlay should drop |overlay_weak|. |
+ ASSERT_TRUE(overlay_weak.get() == nullptr); |
+ |
+ // Cleanup should free the the original SurfaceTexture. It would probably be |
+ // fine to free a new one, but that seems like a waste of resources. So, we |
+ // enforce that it should elide the switch entirely. |
+ EXPECT_CALL( |
+ codec_allocator_, |
+ MockReleaseMediaCodec(codec_allocator_.most_recent_codec(), nullptr, |
+ codec_allocator_.most_recent_surface_texture())); |
+ LetAVDAUpdateSurface(); |
+ vda_ = nullptr; |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+TEST_F(AndroidVideoDecodeAcceleratorTest, |
+ ChangingOutputSurfaceVoluntarilyWithoutSetSurfaceIsIgnored) { |
+ // If we ask AVDA to change to SurfaceTexture should be ignored on platforms |
+ // that don't support SetSurface (pre-M or blacklisted). It should also |
+ // ignore SurfaceTexture => overlay, but we don't check that. |
+ // |
+ // Also note that there are other probably reasonable things to do (like |
+ // signal an error), but we want to be sure that it doesn't try to SetSurface. |
+ // We also want to be sure that, if it doesn't signal an error, that it also |
+ // doesn't get confused about which surface is in use. So, we assume that it |
+ // doesn't signal an error, and we check that it releases the right surface |
+ // with the codec. |
+ EXPECT_CALL(client_, NotifyError(_)).Times(0); |
+ |
+ platform_config_.allow_setsurface = false; |
+ InitializeAVDAWithOverlay(); |
+ EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_)).Times(0); |
+ |
+ // This should not switch to SurfaceTexture. |
+ helper_->ProvideSurfaceTexture(); |
+ LetAVDAUpdateSurface(); |
+ |
+ // It should free an overlay with the codec. |
+ EXPECT_CALL( |
+ codec_allocator_, |
+ MockReleaseMediaCodec(codec_allocator_.most_recent_codec(), |
+ codec_allocator_.most_recent_overlay(), nullptr)); |
+ vda_ = nullptr; |
+ base::RunLoop().RunUntilIdle(); |
+} |
+ |
+TEST_F(AndroidVideoDecodeAcceleratorTest, |
+ OnSurfaceDestroyedWithoutSetSurfaceFreesTheCodec) { |
+ // If AVDA receives OnSurfaceDestroyed without support for SetSurface, then it |
+ // should free the codec. |
+ platform_config_.allow_setsurface = false; |
+ InitializeAVDAWithOverlay(); |
+ EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_)).Times(0); |
+ |
+ // This should free the codec. |
+ EXPECT_CALL( |
+ codec_allocator_, |
+ MockReleaseMediaCodec(codec_allocator_.most_recent_codec(), |
+ codec_allocator_.most_recent_overlay(), nullptr)); |
+ helper_->StopImmediately(codec_allocator_.most_recent_overlay()); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ // Verify that the codec has been released, since |vda_| will be destroyed |
+ // soon. The expectations must be met before that. |
+ testing::Mock::VerifyAndClearExpectations(&codec_allocator_); |
+} |
+ |
+TEST_F(AndroidVideoDecodeAcceleratorTest, |
+ MultipleSurfaceTextureCallbacksAreIgnored) { |
+ // Ask AVDA to switch to ST when it's already using ST, nothing should happen. |
+ SKIP_IF_MEDIACODEC_IS_NOT_AVAILABLE(); |
+ |
+ InitializeAVDAWithSurfaceTexture(); |
+ |
+ // This should do nothing. |
+ EXPECT_CALL(*codec_allocator_.most_recent_codec(), SetSurface(_)).Times(0); |
+ helper_->ProvideSurfaceTexture(); |
- VideoDecodeAccelerator::Config config(H264PROFILE_BASELINE); |
- config.is_deferred_initialization_allowed = true; |
- vda_->Initialize(config, &client_); |
base::RunLoop().RunUntilIdle(); |
} |