| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/fuchsia/video/fuchsia_video_decoder.h" |
| |
| #include <fuchsia/mediacodec/cpp/fidl.h> |
| #include <fuchsia/sysmem/cpp/fidl.h> |
| #include <lib/sys/cpp/component_context.h> |
| |
| #include <memory> |
| |
| #include "base/containers/flat_map.h" |
| #include "base/containers/flat_set.h" |
| #include "base/fuchsia/fuchsia_logging.h" |
| #include "base/fuchsia/koid.h" |
| #include "base/fuchsia/process_context.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/notreached.h" |
| #include "base/process/process_handle.h" |
| #include "base/test/bind.h" |
| #include "base/test/task_environment.h" |
| #include "components/viz/common/gpu/raster_context_provider.h" |
| #include "components/viz/test/test_context_support.h" |
| #include "gpu/command_buffer/client/shared_image_interface.h" |
| #include "gpu/config/gpu_feature_info.h" |
| #include "media/base/test_data_util.h" |
| #include "media/base/test_helpers.h" |
| #include "media/base/video_decoder.h" |
| #include "media/base/video_frame.h" |
| #include "media/mojo/mojom/fuchsia_media.mojom.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/client_native_pixmap_factory.h" |
| #include "ui/gfx/gpu_fence.h" |
| #include "ui/gfx/gpu_memory_buffer.h" |
| #include "ui/gfx/native_pixmap_handle.h" |
| |
| namespace media { |
| |
| namespace { |
| |
| class TestBufferCollection { |
| public: |
| TestBufferCollection(zx::eventpair handle, zx::channel collection_token) |
| : handle_(std::move(handle)) { |
| sysmem_allocator_ = base::ComponentContextForProcess() |
| ->svc() |
| ->Connect<fuchsia::sysmem::Allocator>(); |
| sysmem_allocator_.set_error_handler([](zx_status_t status) { |
| ZX_LOG(FATAL, status) |
| << "The fuchsia.sysmem.Allocator channel was terminated."; |
| }); |
| sysmem_allocator_->SetDebugClientInfo("CrTestBufferCollection", |
| base::GetCurrentProcId()); |
| |
| sysmem_allocator_->BindSharedCollection( |
| fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>( |
| std::move(collection_token)), |
| buffers_collection_.NewRequest()); |
| |
| fuchsia::sysmem::BufferCollectionConstraints buffer_constraints; |
| buffer_constraints.usage.cpu = fuchsia::sysmem::cpuUsageRead; |
| zx_status_t status = buffers_collection_->SetConstraints( |
| /*has_constraints=*/true, std::move(buffer_constraints)); |
| ZX_CHECK(status == ZX_OK, status) << "BufferCollection::SetConstraints()"; |
| } |
| |
| TestBufferCollection(const TestBufferCollection&) = delete; |
| TestBufferCollection& operator=(const TestBufferCollection&) = delete; |
| |
| ~TestBufferCollection() { buffers_collection_->Close(); } |
| |
| size_t GetNumBuffers() { |
| if (!buffer_collection_info_) { |
| zx_status_t wait_status; |
| fuchsia::sysmem::BufferCollectionInfo_2 info; |
| zx_status_t status = |
| buffers_collection_->WaitForBuffersAllocated(&wait_status, &info); |
| ZX_CHECK(status == ZX_OK, status) |
| << "BufferCollection::WaitForBuffersAllocated()"; |
| ZX_CHECK(wait_status == ZX_OK, wait_status) |
| << "BufferCollection::WaitForBuffersAllocated()"; |
| buffer_collection_info_ = std::move(info); |
| } |
| return buffer_collection_info_->buffer_count; |
| } |
| |
| private: |
| zx::eventpair handle_; |
| |
| fuchsia::sysmem::AllocatorPtr sysmem_allocator_; |
| fuchsia::sysmem::BufferCollectionSyncPtr buffers_collection_; |
| |
| absl::optional<fuchsia::sysmem::BufferCollectionInfo_2> |
| buffer_collection_info_; |
| }; |
| |
| class TestSharedImageInterface : public gpu::SharedImageInterface { |
| public: |
| TestSharedImageInterface() = default; |
| ~TestSharedImageInterface() override = default; |
| |
| gpu::Mailbox CreateSharedImage(viz::SharedImageFormat format, |
| const gfx::Size& size, |
| const gfx::ColorSpace& color_space, |
| GrSurfaceOrigin surface_origin, |
| SkAlphaType alpha_type, |
| uint32_t usage, |
| base::StringPiece debug_label, |
| gpu::SurfaceHandle surface_handle) override { |
| ADD_FAILURE(); |
| return gpu::Mailbox(); |
| } |
| |
| gpu::Mailbox CreateSharedImage( |
| viz::SharedImageFormat format, |
| const gfx::Size& size, |
| const gfx::ColorSpace& color_space, |
| GrSurfaceOrigin surface_origin, |
| SkAlphaType alpha_type, |
| uint32_t usage, |
| base::StringPiece debug_label, |
| base::span<const uint8_t> pixel_data) override { |
| ADD_FAILURE(); |
| return gpu::Mailbox(); |
| } |
| |
| gpu::Mailbox CreateSharedImage( |
| viz::SharedImageFormat format, |
| const gfx::Size& size, |
| const gfx::ColorSpace& color_space, |
| GrSurfaceOrigin surface_origin, |
| SkAlphaType alpha_type, |
| uint32_t usage, |
| base::StringPiece debug_label, |
| gfx::GpuMemoryBufferHandle buffer_handle) override { |
| ADD_FAILURE(); |
| return gpu::Mailbox(); |
| } |
| |
| gpu::Mailbox CreateSharedImage( |
| gfx::GpuMemoryBuffer* gpu_memory_buffer, |
| gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, |
| gfx::BufferPlane plane, |
| const gfx::ColorSpace& color_space, |
| GrSurfaceOrigin surface_origin, |
| SkAlphaType alpha_type, |
| uint32_t usage, |
| base::StringPiece debug_label) override { |
| gfx::GpuMemoryBufferHandle handle = gpu_memory_buffer->CloneHandle(); |
| CHECK_EQ(handle.type, gfx::GpuMemoryBufferType::NATIVE_PIXMAP); |
| |
| zx_koid_t id = base::GetRelatedKoid( |
| handle.native_pixmap_handle.buffer_collection_handle) |
| .value(); |
| auto collection_it = sysmem_buffer_collections_.find(id); |
| CHECK(collection_it != sysmem_buffer_collections_.end()); |
| CHECK_LT(handle.native_pixmap_handle.buffer_index, |
| collection_it->second->GetNumBuffers()); |
| |
| auto result = gpu::Mailbox::GenerateForSharedImage(); |
| mailboxes_.insert(result); |
| return result; |
| } |
| |
| void UpdateSharedImage(const gpu::SyncToken& sync_token, |
| const gpu::Mailbox& mailbox) override { |
| ADD_FAILURE(); |
| } |
| void UpdateSharedImage(const gpu::SyncToken& sync_token, |
| std::unique_ptr<gfx::GpuFence> acquire_fence, |
| const gpu::Mailbox& mailbox) override { |
| ADD_FAILURE(); |
| } |
| |
| void AddReferenceToSharedImage(const gpu::SyncToken& sync_token, |
| const gpu::Mailbox& mailbox, |
| uint32_t usage) override { |
| ADD_FAILURE(); |
| } |
| |
| void DestroySharedImage(const gpu::SyncToken& sync_token, |
| const gpu::Mailbox& mailbox) override { |
| CHECK_EQ(mailboxes_.erase(mailbox), 1U); |
| } |
| |
| SwapChainMailboxes CreateSwapChain(viz::SharedImageFormat format, |
| const gfx::Size& size, |
| const gfx::ColorSpace& color_space, |
| GrSurfaceOrigin surface_origin, |
| SkAlphaType alpha_type, |
| uint32_t usage) override { |
| ADD_FAILURE(); |
| return SwapChainMailboxes(); |
| } |
| void PresentSwapChain(const gpu::SyncToken& sync_token, |
| const gpu::Mailbox& mailbox) override { |
| ADD_FAILURE(); |
| } |
| |
| void RegisterSysmemBufferCollection(zx::eventpair handle, |
| zx::channel token, |
| gfx::BufferFormat format, |
| gfx::BufferUsage usage, |
| bool register_with_image_pipe) override { |
| EXPECT_EQ(format, gfx::BufferFormat::YUV_420_BIPLANAR); |
| EXPECT_EQ(usage, gfx::BufferUsage::GPU_READ); |
| zx_koid_t id = base::GetKoid(handle).value(); |
| std::unique_ptr<TestBufferCollection>& collection = |
| sysmem_buffer_collections_[id]; |
| EXPECT_FALSE(collection); |
| collection = std::make_unique<TestBufferCollection>(std::move(handle), |
| std::move(token)); |
| } |
| |
| gpu::SyncToken GenVerifiedSyncToken() override { |
| gpu::SyncToken token(gpu::CommandBufferNamespace::GPU_IO, |
| gpu::CommandBufferId(33), 1); |
| token.SetVerifyFlush(); |
| return token; |
| } |
| |
| gpu::SyncToken GenUnverifiedSyncToken() override { |
| ADD_FAILURE(); |
| return gpu::SyncToken(); |
| } |
| |
| void WaitSyncToken(const gpu::SyncToken& sync_token) override { |
| ADD_FAILURE(); |
| } |
| |
| void Flush() override { ADD_FAILURE(); } |
| |
| scoped_refptr<gfx::NativePixmap> GetNativePixmap( |
| const gpu::Mailbox& mailbox) override { |
| return nullptr; |
| } |
| |
| private: |
| base::flat_map<zx_koid_t, std::unique_ptr<TestBufferCollection>> |
| sysmem_buffer_collections_; |
| |
| base::flat_set<gpu::Mailbox> mailboxes_; |
| }; |
| |
| class TestRasterContextProvider |
| : public base::RefCountedThreadSafe<TestRasterContextProvider>, |
| public viz::RasterContextProvider { |
| public: |
| TestRasterContextProvider() {} |
| |
| TestRasterContextProvider(TestRasterContextProvider&) = delete; |
| TestRasterContextProvider& operator=(TestRasterContextProvider&) = delete; |
| |
| void SetOnDestroyedClosure(base::OnceClosure on_destroyed) { |
| on_destroyed_ = std::move(on_destroyed); |
| } |
| |
| // viz::RasterContextProvider implementation; |
| void AddRef() const override { |
| base::RefCountedThreadSafe<TestRasterContextProvider>::AddRef(); |
| } |
| void Release() const override { |
| base::RefCountedThreadSafe<TestRasterContextProvider>::Release(); |
| } |
| gpu::ContextResult BindToCurrentSequence() override { |
| ADD_FAILURE(); |
| return gpu::ContextResult::kFatalFailure; |
| } |
| void AddObserver(viz::ContextLostObserver* obs) override { ADD_FAILURE(); } |
| void RemoveObserver(viz::ContextLostObserver* obs) override { ADD_FAILURE(); } |
| base::Lock* GetLock() override { |
| ADD_FAILURE(); |
| return nullptr; |
| } |
| viz::ContextCacheController* CacheController() override { |
| ADD_FAILURE(); |
| return nullptr; |
| } |
| gpu::ContextSupport* ContextSupport() override { |
| return &gpu_context_support_; |
| } |
| class GrDirectContext* GrContext() override { |
| ADD_FAILURE(); |
| return nullptr; |
| } |
| gpu::SharedImageInterface* SharedImageInterface() override { |
| return &shared_image_interface_; |
| } |
| const gpu::Capabilities& ContextCapabilities() const override { |
| ADD_FAILURE(); |
| static gpu::Capabilities dummy_caps; |
| return dummy_caps; |
| } |
| const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const override { |
| ADD_FAILURE(); |
| static gpu::GpuFeatureInfo dummy_feature_info; |
| return dummy_feature_info; |
| } |
| gpu::gles2::GLES2Interface* ContextGL() override { |
| ADD_FAILURE(); |
| return nullptr; |
| } |
| gpu::raster::RasterInterface* RasterInterface() override { |
| ADD_FAILURE(); |
| return nullptr; |
| } |
| |
| private: |
| friend class base::RefCountedThreadSafe<TestRasterContextProvider>; |
| |
| ~TestRasterContextProvider() override { |
| if (on_destroyed_) |
| std::move(on_destroyed_).Run(); |
| } |
| |
| TestSharedImageInterface shared_image_interface_; |
| viz::TestContextSupport gpu_context_support_; |
| |
| base::OnceClosure on_destroyed_; |
| }; |
| |
| class TestFuchsiaMediaCodecProvider |
| : public media::mojom::FuchsiaMediaCodecProvider { |
| public: |
| // media::mojom::FuchsiaMediaCodecProvider implementation. |
| void CreateVideoDecoder( |
| media::VideoCodec codec, |
| media::mojom::VideoDecoderSecureMemoryMode secure_mode, |
| fidl::InterfaceRequest<fuchsia::media::StreamProcessor> |
| stream_processor_request) final { |
| EXPECT_TRUE(secure_mode == |
| media::mojom::VideoDecoderSecureMemoryMode::CLEAR); |
| |
| fuchsia::mediacodec::CreateDecoder_Params decoder_params; |
| decoder_params.mutable_input_details()->set_format_details_version_ordinal( |
| 0); |
| |
| switch (codec) { |
| case VideoCodec::kH264: |
| decoder_params.mutable_input_details()->set_mime_type("video/h264"); |
| break; |
| case VideoCodec::kVP9: |
| decoder_params.mutable_input_details()->set_mime_type("video/vp9"); |
| break; |
| |
| default: |
| ADD_FAILURE() << "CreateVideoDecoder() called with unexpected codec: " |
| << static_cast<int>(codec); |
| return; |
| } |
| |
| decoder_params.set_promise_separate_access_units_on_input(true); |
| decoder_params.set_require_hw(false); |
| |
| auto decoder_factory = base::ComponentContextForProcess() |
| ->svc() |
| ->Connect<fuchsia::mediacodec::CodecFactory>(); |
| decoder_factory->CreateDecoder(std::move(decoder_params), |
| std::move(stream_processor_request)); |
| } |
| |
| void GetSupportedVideoDecoderConfigs( |
| GetSupportedVideoDecoderConfigsCallback callback) override { |
| ADD_FAILURE(); |
| } |
| |
| mojo::Receiver<media::mojom::FuchsiaMediaCodecProvider> receiver_{this}; |
| }; |
| |
| class FakeClientNativePixmap : public gfx::ClientNativePixmap { |
| public: |
| FakeClientNativePixmap(gfx::NativePixmapHandle handle) |
| : handle_(std::move(handle)) { |
| CHECK(handle_.buffer_collection_handle); |
| } |
| |
| ~FakeClientNativePixmap() override = default; |
| |
| // gfx::ClientNativePixmap implementation. |
| bool Map() override { |
| NOTREACHED(); |
| return false; |
| } |
| void Unmap() override { NOTREACHED(); } |
| size_t GetNumberOfPlanes() const override { |
| NOTREACHED(); |
| return 0; |
| } |
| void* GetMemoryAddress(size_t plane) const override { |
| NOTREACHED(); |
| return nullptr; |
| } |
| int GetStride(size_t plane) const override { |
| NOTREACHED(); |
| return 0; |
| } |
| gfx::NativePixmapHandle CloneHandleForIPC() const override { |
| return gfx::CloneHandleForIPC(handle_); |
| } |
| |
| private: |
| gfx::NativePixmapHandle handle_; |
| }; |
| |
| class FakeClientNativePixmapFactory : public gfx::ClientNativePixmapFactory { |
| public: |
| FakeClientNativePixmapFactory() = default; |
| ~FakeClientNativePixmapFactory() override = default; |
| |
| std::unique_ptr<gfx::ClientNativePixmap> ImportFromHandle( |
| gfx::NativePixmapHandle handle, |
| const gfx::Size& size, |
| gfx::BufferFormat format, |
| gfx::BufferUsage usage) override { |
| return std::make_unique<FakeClientNativePixmap>(std::move(handle)); |
| } |
| }; |
| |
| } // namespace |
| |
| class FuchsiaVideoDecoderTest : public testing::Test { |
| public: |
| FuchsiaVideoDecoderTest() |
| : raster_context_provider_( |
| base::MakeRefCounted<TestRasterContextProvider>()) { |
| auto decoder = std::make_unique<FuchsiaVideoDecoder>( |
| raster_context_provider_.get(), |
| mojo::SharedRemote<media::mojom::FuchsiaMediaCodecProvider>( |
| test_media_codec_provider_.receiver_.BindNewPipeAndPassRemote()), |
| /*allow_overlays=*/false); |
| decoder->SetClientNativePixmapFactoryForTests( |
| std::make_unique<FakeClientNativePixmapFactory>()); |
| decoder_ = std::move(decoder); |
| } |
| |
| FuchsiaVideoDecoderTest(const FuchsiaVideoDecoderTest&) = delete; |
| FuchsiaVideoDecoderTest& operator=(const FuchsiaVideoDecoderTest&) = delete; |
| |
| ~FuchsiaVideoDecoderTest() override { |
| // The decoder uses async destruction callbacks for VideoFrames, so we need |
| // to run the message loop after releasing the frames to avoid memory leaks |
| // (see crbug.com/1287362). |
| output_frames_.clear(); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| [[nodiscard]] bool InitializeDecoder(VideoDecoderConfig config) { |
| base::RunLoop run_loop; |
| bool init_cb_result = false; |
| decoder_->Initialize( |
| config, true, /*cdm_context=*/nullptr, |
| base::BindRepeating( |
| [](bool* init_cb_result, base::RunLoop* run_loop, |
| DecoderStatus status) { |
| *init_cb_result = status.is_ok(); |
| run_loop->Quit(); |
| }, |
| &init_cb_result, &run_loop), |
| base::BindRepeating(&FuchsiaVideoDecoderTest::OnVideoFrame, |
| weak_factory_.GetWeakPtr()), |
| base::DoNothing()); |
| |
| run_loop.Run(); |
| return init_cb_result; |
| } |
| |
| void ResetDecoder() { |
| base::RunLoop run_loop; |
| decoder_->Reset(base::BindRepeating( |
| [](base::RunLoop* run_loop) { run_loop->Quit(); }, &run_loop)); |
| run_loop.Run(); |
| } |
| |
| void OnVideoFrame(scoped_refptr<VideoFrame> frame) { |
| num_output_frames_++; |
| CHECK(frame->HasTextures()); |
| output_frames_.push_back(std::move(frame)); |
| while (output_frames_.size() > frames_to_keep_) { |
| output_frames_.pop_front(); |
| } |
| if (run_loop_) |
| run_loop_->Quit(); |
| } |
| |
| void DecodeBuffer(scoped_refptr<DecoderBuffer> buffer) { |
| decoder_->Decode( |
| buffer, |
| base::BindRepeating(&FuchsiaVideoDecoderTest::OnFrameDecoded, |
| weak_factory_.GetWeakPtr(), num_input_buffers_)); |
| num_input_buffers_ += 1; |
| } |
| |
| void ReadAndDecodeFrame(const std::string& name) { |
| DecodeBuffer(ReadTestDataFile(name)); |
| } |
| |
| void OnFrameDecoded(size_t frame_pos, DecoderStatus status) { |
| EXPECT_EQ(frame_pos, num_decoded_buffers_); |
| num_decoded_buffers_ += 1; |
| last_decode_status_ = std::move(status); |
| if (run_loop_) |
| run_loop_->Quit(); |
| } |
| |
| // Waits until all pending decode requests are finished. |
| void WaitDecodeDone() { |
| size_t target_pos = num_input_buffers_; |
| while (num_decoded_buffers_ < target_pos) { |
| base::RunLoop run_loop; |
| run_loop_ = &run_loop; |
| run_loop.Run(); |
| run_loop_ = nullptr; |
| ASSERT_TRUE(last_decode_status_.is_ok()); |
| } |
| } |
| |
| protected: |
| base::test::SingleThreadTaskEnvironment task_environment_{ |
| base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; |
| |
| TestFuchsiaMediaCodecProvider test_media_codec_provider_; |
| |
| scoped_refptr<TestRasterContextProvider> raster_context_provider_; |
| |
| std::unique_ptr<VideoDecoder> decoder_; |
| |
| size_t num_input_buffers_ = 0; |
| |
| // Number of frames for which DecodeCB has been called. That doesn't mean |
| // we've received corresponding output frames. |
| size_t num_decoded_buffers_ = 0; |
| |
| std::list<scoped_refptr<VideoFrame>> output_frames_; |
| size_t num_output_frames_ = 0; |
| |
| DecoderStatus last_decode_status_; |
| base::RunLoop* run_loop_ = nullptr; |
| |
| // Number of frames that OnVideoFrame() should keep in |output_frames_|. |
| size_t frames_to_keep_ = 2; |
| |
| base::WeakPtrFactory<FuchsiaVideoDecoderTest> weak_factory_{this}; |
| }; |
| |
| scoped_refptr<DecoderBuffer> GetH264Frame(size_t frame_num) { |
| static scoped_refptr<DecoderBuffer> frames[] = { |
| ReadTestDataFile("h264-320x180-frame-0"), |
| ReadTestDataFile("h264-320x180-frame-1"), |
| ReadTestDataFile("h264-320x180-frame-2"), |
| ReadTestDataFile("h264-320x180-frame-3")}; |
| CHECK_LT(frame_num, std::size(frames)); |
| return frames[frame_num]; |
| } |
| |
| TEST_F(FuchsiaVideoDecoderTest, CreateAndDestroy) {} |
| |
| TEST_F(FuchsiaVideoDecoderTest, CreateInitDestroy) { |
| EXPECT_TRUE(InitializeDecoder(TestVideoConfig::NormalH264())); |
| } |
| |
| TEST_F(FuchsiaVideoDecoderTest, DISABLED_VP9) { |
| ASSERT_TRUE(InitializeDecoder(TestVideoConfig::Normal(VideoCodec::kVP9))); |
| |
| DecodeBuffer(ReadTestDataFile("vp9-I-frame-320x240")); |
| DecodeBuffer(DecoderBuffer::CreateEOSBuffer()); |
| ASSERT_NO_FATAL_FAILURE(WaitDecodeDone()); |
| |
| EXPECT_EQ(num_output_frames_, 1U); |
| } |
| |
| TEST_F(FuchsiaVideoDecoderTest, H264) { |
| ASSERT_TRUE(InitializeDecoder(TestVideoConfig::NormalH264())); |
| |
| DecodeBuffer(GetH264Frame(0)); |
| DecodeBuffer(GetH264Frame(1)); |
| ASSERT_NO_FATAL_FAILURE(WaitDecodeDone()); |
| |
| DecodeBuffer(GetH264Frame(2)); |
| DecodeBuffer(GetH264Frame(3)); |
| DecodeBuffer(DecoderBuffer::CreateEOSBuffer()); |
| ASSERT_NO_FATAL_FAILURE(WaitDecodeDone()); |
| |
| EXPECT_EQ(num_output_frames_, 4U); |
| } |
| |
| // Verify that the decoder can be re-initialized while there pending decode |
| // requests. |
| TEST_F(FuchsiaVideoDecoderTest, ReinitializeH264) { |
| ASSERT_TRUE(InitializeDecoder(TestVideoConfig::NormalH264())); |
| DecodeBuffer(GetH264Frame(0)); |
| DecodeBuffer(GetH264Frame(1)); |
| ASSERT_NO_FATAL_FAILURE(WaitDecodeDone()); |
| |
| num_output_frames_ = 0; |
| |
| // Re-initialize decoder and send the same data again. |
| ASSERT_TRUE(InitializeDecoder(TestVideoConfig::NormalH264())); |
| DecodeBuffer(GetH264Frame(0)); |
| DecodeBuffer(GetH264Frame(1)); |
| DecodeBuffer(GetH264Frame(2)); |
| DecodeBuffer(GetH264Frame(3)); |
| DecodeBuffer(DecoderBuffer::CreateEOSBuffer()); |
| ASSERT_NO_FATAL_FAILURE(WaitDecodeDone()); |
| |
| EXPECT_EQ(num_output_frames_, 4U); |
| } |
| |
| // Verify that the decoder can be re-initialized after Reset(). |
| TEST_F(FuchsiaVideoDecoderTest, ResetAndReinitializeH264) { |
| ASSERT_TRUE(InitializeDecoder(TestVideoConfig::NormalH264())); |
| DecodeBuffer(GetH264Frame(0)); |
| DecodeBuffer(GetH264Frame(1)); |
| ASSERT_NO_FATAL_FAILURE(WaitDecodeDone()); |
| |
| ResetDecoder(); |
| |
| num_output_frames_ = 0; |
| |
| // Re-initialize decoder and send the same data again. |
| ASSERT_TRUE(InitializeDecoder(TestVideoConfig::NormalH264())); |
| DecodeBuffer(GetH264Frame(0)); |
| DecodeBuffer(GetH264Frame(1)); |
| DecodeBuffer(GetH264Frame(2)); |
| DecodeBuffer(GetH264Frame(3)); |
| DecodeBuffer(DecoderBuffer::CreateEOSBuffer()); |
| ASSERT_NO_FATAL_FAILURE(WaitDecodeDone()); |
| |
| EXPECT_EQ(num_output_frames_, 4U); |
| } |
| |
| // Verifies that the decoder keeps reference to the RasterContextProvider. |
| TEST_F(FuchsiaVideoDecoderTest, RasterContextLifetime) { |
| bool context_destroyed = false; |
| raster_context_provider_->SetOnDestroyedClosure(base::BindLambdaForTesting( |
| [&context_destroyed]() { context_destroyed = true; })); |
| ASSERT_TRUE(InitializeDecoder(TestVideoConfig::NormalH264())); |
| ASSERT_FALSE(context_destroyed); |
| |
| // Decoder should keep reference to RasterContextProvider. |
| raster_context_provider_.reset(); |
| ASSERT_FALSE(context_destroyed); |
| |
| // Feed some frames to decoder to get decoded video frames. |
| for (int i = 0; i < 4; ++i) { |
| DecodeBuffer(GetH264Frame(i)); |
| } |
| ASSERT_NO_FATAL_FAILURE(WaitDecodeDone()); |
| |
| // Destroy the decoder. RasterContextProvider will not be destroyed since |
| // it's still referenced by frames in |output_frames_|. |
| decoder_.reset(); |
| task_environment_.RunUntilIdle(); |
| ASSERT_FALSE(context_destroyed); |
| |
| // RasterContextProvider reference should be dropped once all frames are |
| // dropped. |
| output_frames_.clear(); |
| task_environment_.RunUntilIdle(); |
| ASSERT_TRUE(context_destroyed); |
| } |
| |
| } // namespace media |