| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/filters/fuchsia/fuchsia_video_decoder.h" |
| |
| #include <fuchsia/mediacodec/cpp/fidl.h> |
| #include <lib/sys/cpp/component_context.h> |
| #include <vulkan/vulkan.h> |
| |
| #include "base/bind.h" |
| #include "base/bits.h" |
| #include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| #include "base/fuchsia/fuchsia_logging.h" |
| #include "base/fuchsia/process_context.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/process/process_metrics.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| #include "components/viz/common/gpu/raster_context_provider.h" |
| #include "gpu/command_buffer/client/context_support.h" |
| #include "gpu/command_buffer/client/shared_image_interface.h" |
| #include "gpu/command_buffer/common/shared_image_usage.h" |
| #include "gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.h" |
| #include "media/base/bind_to_current_loop.h" |
| #include "media/base/cdm_context.h" |
| #include "media/base/media_switches.h" |
| #include "media/base/video_aspect_ratio.h" |
| #include "media/base/video_frame.h" |
| #include "media/base/video_util.h" |
| #include "media/fuchsia/cdm/fuchsia_cdm_context.h" |
| #include "media/fuchsia/cdm/fuchsia_decryptor.h" |
| #include "media/fuchsia/cdm/fuchsia_stream_decryptor.h" |
| #include "media/fuchsia/common/decrypting_sysmem_buffer_stream.h" |
| #include "media/fuchsia/common/passthrough_sysmem_buffer_stream.h" |
| #include "media/fuchsia/common/stream_processor_helper.h" |
| #include "third_party/libyuv/include/libyuv/video_common.h" |
| #include "ui/gfx/buffer_types.h" |
| #include "ui/gfx/client_native_pixmap_factory.h" |
| #include "ui/ozone/public/client_native_pixmap_factory_ozone.h" |
| |
| namespace media { |
| |
| namespace { |
| |
| // Number of output buffers allocated "for camping". This value is passed to |
| // sysmem to ensure that we get one output buffer for the frame currently |
| // displayed on the screen. |
| constexpr uint32_t kOutputBuffersForCamping = 1; |
| |
| // Maximum number of frames we expect to have queued up while playing video. |
| // Higher values require more memory for output buffers. Lower values make it |
| // more likely that renderer will stall because decoded frames are not available |
| // on time. |
| constexpr uint32_t kMaxUsedOutputBuffers = 5; |
| |
| // Use 2 buffers for decoder input. Allocating more than one buffers ensures |
| // that when the decoder is done working on one packet it will have another one |
| // waiting in the queue. Limiting number of buffers to 2 allows to minimize |
| // required memory, without significant effect on performance. |
| constexpr size_t kNumInputBuffers = 2; |
| |
| // Some codecs do not support splitting video frames across multiple input |
| // buffers, so the buffers need to be large enough to fit all video frames. The |
| // buffer size is calculated to fit 1080p I420 frame with MinCR=2 (per H264 |
| // spec), plus 128KiB for SEI/SPS/PPS. (note that the same size is used for all |
| // codecs, not just H264). |
| constexpr size_t kInputBufferSize = 1920 * 1080 * 3 / 2 / 2 + 128 * 1024; |
| |
| } // namespace |
| |
| // Helper used to hold mailboxes for the output textures. OutputMailbox may |
| // outlive FuchsiaVideoDecoder if is referenced by a VideoFrame. |
| class FuchsiaVideoDecoder::OutputMailbox { |
| public: |
| OutputMailbox( |
| scoped_refptr<viz::RasterContextProvider> raster_context_provider, |
| std::unique_ptr<gfx::GpuMemoryBuffer> gmb) |
| : raster_context_provider_(raster_context_provider), weak_factory_(this) { |
| uint32_t usage = gpu::SHARED_IMAGE_USAGE_DISPLAY | |
| gpu::SHARED_IMAGE_USAGE_SCANOUT | |
| gpu::SHARED_IMAGE_USAGE_VIDEO_DECODE; |
| mailbox_ = |
| raster_context_provider_->SharedImageInterface()->CreateSharedImage( |
| gmb.get(), nullptr, gfx::ColorSpace(), kTopLeft_GrSurfaceOrigin, |
| kPremul_SkAlphaType, usage); |
| } |
| |
| OutputMailbox(const OutputMailbox&) = delete; |
| OutputMailbox& operator=(const OutputMailbox&) = delete; |
| |
| ~OutputMailbox() { |
| raster_context_provider_->SharedImageInterface()->DestroySharedImage( |
| sync_token_, mailbox_); |
| } |
| |
| const gpu::Mailbox& mailbox() { return mailbox_; } |
| |
| // Create a new video frame that wraps the mailbox. |reuse_callback| will be |
| // called when the mailbox can be reused. |
| scoped_refptr<VideoFrame> CreateFrame(VideoPixelFormat pixel_format, |
| const gfx::Size& coded_size, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| base::TimeDelta timestamp, |
| base::OnceClosure reuse_callback) { |
| DCHECK(!is_used_); |
| is_used_ = true; |
| reuse_callback_ = std::move(reuse_callback); |
| |
| gpu::MailboxHolder mailboxes[VideoFrame::kMaxPlanes]; |
| mailboxes[0].mailbox = mailbox_; |
| mailboxes[0].sync_token = raster_context_provider_->SharedImageInterface() |
| ->GenUnverifiedSyncToken(); |
| |
| auto frame = VideoFrame::WrapNativeTextures( |
| pixel_format, mailboxes, |
| BindToCurrentLoop(base::BindOnce(&OutputMailbox::OnFrameDestroyed, |
| base::Unretained(this))), |
| coded_size, visible_rect, natural_size, timestamp); |
| |
| // Request a fence we'll wait on before reusing the buffer. |
| frame->metadata().read_lock_fences_enabled = true; |
| |
| return frame; |
| } |
| |
| // Called by FuchsiaVideoDecoder when it no longer needs this mailbox. |
| void Release() { |
| if (is_used_) { |
| // The mailbox is referenced by a VideoFrame. It will be deleted as soon |
| // as the frame is destroyed. |
| DCHECK(reuse_callback_); |
| reuse_callback_ = base::OnceClosure(); |
| } else { |
| delete this; |
| } |
| } |
| |
| private: |
| void OnFrameDestroyed(const gpu::SyncToken& sync_token) { |
| DCHECK(is_used_); |
| is_used_ = false; |
| sync_token_ = sync_token; |
| |
| if (!reuse_callback_) { |
| // If the mailbox cannot be reused then we can just delete it. |
| delete this; |
| return; |
| } |
| |
| raster_context_provider_->ContextSupport()->SignalSyncToken( |
| sync_token_, |
| BindToCurrentLoop(base::BindOnce(&OutputMailbox::OnSyncTokenSignaled, |
| weak_factory_.GetWeakPtr()))); |
| } |
| |
| void OnSyncTokenSignaled() { |
| sync_token_.Clear(); |
| std::move(reuse_callback_).Run(); |
| } |
| |
| const scoped_refptr<viz::RasterContextProvider> raster_context_provider_; |
| |
| gpu::Mailbox mailbox_; |
| gpu::SyncToken sync_token_; |
| |
| // Set to true when the mailbox is referenced by a video frame. |
| bool is_used_ = false; |
| |
| base::OnceClosure reuse_callback_; |
| |
| base::WeakPtrFactory<OutputMailbox> weak_factory_; |
| }; |
| |
| // static |
| std::unique_ptr<VideoDecoder> FuchsiaVideoDecoder::Create( |
| scoped_refptr<viz::RasterContextProvider> raster_context_provider) { |
| return std::make_unique<FuchsiaVideoDecoder>( |
| std::move(raster_context_provider), |
| /*enable_sw_decoding=*/false); |
| } |
| |
| // static |
| std::unique_ptr<VideoDecoder> FuchsiaVideoDecoder::CreateForTests( |
| scoped_refptr<viz::RasterContextProvider> raster_context_provider, |
| bool enable_sw_decoding) { |
| return std::make_unique<FuchsiaVideoDecoder>( |
| std::move(raster_context_provider), enable_sw_decoding); |
| } |
| |
| FuchsiaVideoDecoder::FuchsiaVideoDecoder( |
| scoped_refptr<viz::RasterContextProvider> raster_context_provider, |
| bool enable_sw_decoding) |
| : raster_context_provider_(raster_context_provider), |
| enable_sw_decoding_(enable_sw_decoding), |
| use_overlays_for_video_(base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kUseOverlaysForVideo)), |
| sysmem_allocator_("CrFuchsiaVideoDecoder"), |
| client_native_pixmap_factory_( |
| ui::CreateClientNativePixmapFactoryOzone()) { |
| DCHECK(raster_context_provider_); |
| weak_this_ = weak_factory_.GetWeakPtr(); |
| } |
| |
| FuchsiaVideoDecoder::~FuchsiaVideoDecoder() { |
| // Reset SysmemBufferStream to ensure it doesn't try to send new packets when |
| // the |decoder_| is destroyed. |
| sysmem_buffer_stream_.reset(); |
| decoder_.reset(); |
| |
| // Release mailboxes used for output frames. |
| ReleaseOutputBuffers(); |
| } |
| |
| bool FuchsiaVideoDecoder::IsPlatformDecoder() const { |
| return true; |
| } |
| |
| bool FuchsiaVideoDecoder::SupportsDecryption() const { |
| return true; |
| } |
| |
| VideoDecoderType FuchsiaVideoDecoder::GetDecoderType() const { |
| return VideoDecoderType::kFuchsia; |
| } |
| |
| void FuchsiaVideoDecoder::Initialize(const VideoDecoderConfig& config, |
| bool low_delay, |
| CdmContext* cdm_context, |
| InitCB init_cb, |
| const OutputCB& output_cb, |
| const WaitingCB& waiting_cb) { |
| DCHECK(output_cb); |
| DCHECK(waiting_cb); |
| DCHECK(decode_callbacks_.empty()); |
| |
| auto done_callback = BindToCurrentLoop(std::move(init_cb)); |
| |
| // There should be no pending decode request, so DropInputQueue() is not |
| // expected to fail. |
| bool result = DropInputQueue(DecodeStatus::ABORTED); |
| DCHECK(result); |
| |
| output_cb_ = output_cb; |
| waiting_cb_ = waiting_cb; |
| container_aspect_ratio_ = config.aspect_ratio(); |
| |
| // Keep decoder and decryptor if the configuration hasn't changed. |
| if (decoder_ && current_config_.codec() == config.codec() && |
| current_config_.is_encrypted() == config.is_encrypted()) { |
| std::move(done_callback).Run(OkStatus()); |
| return; |
| } |
| |
| sysmem_buffer_stream_.reset(); |
| decoder_.reset(); |
| |
| // Initialize the stream. |
| bool secure_mode = false; |
| StatusCode status = InitializeSysmemBufferStream(config.is_encrypted(), |
| cdm_context, &secure_mode); |
| if (status != StatusCode::kOk) { |
| std::move(done_callback).Run(StatusCode::kOk); |
| return; |
| } |
| |
| // Reset output buffers since we won't be able to re-use them. |
| ReleaseOutputBuffers(); |
| |
| fuchsia::mediacodec::CreateDecoder_Params decoder_params; |
| decoder_params.mutable_input_details()->set_format_details_version_ordinal(0); |
| |
| switch (config.codec()) { |
| case VideoCodec::kH264: |
| decoder_params.mutable_input_details()->set_mime_type("video/h264"); |
| break; |
| case VideoCodec::kVP8: |
| decoder_params.mutable_input_details()->set_mime_type("video/vp8"); |
| break; |
| case VideoCodec::kVP9: |
| decoder_params.mutable_input_details()->set_mime_type("video/vp9"); |
| break; |
| case VideoCodec::kHEVC: |
| decoder_params.mutable_input_details()->set_mime_type("video/hevc"); |
| break; |
| case VideoCodec::kAV1: |
| decoder_params.mutable_input_details()->set_mime_type("video/av1"); |
| break; |
| |
| default: |
| std::move(done_callback).Run(StatusCode::kDecoderUnsupportedCodec); |
| return; |
| } |
| |
| if (secure_mode) { |
| decoder_params.set_secure_input_mode( |
| fuchsia::mediacodec::SecureMemoryMode::ON); |
| } |
| |
| if (secure_mode || base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kForceProtectedVideoOutputBuffers)) { |
| decoder_params.set_secure_output_mode( |
| fuchsia::mediacodec::SecureMemoryMode::ON); |
| } |
| |
| decoder_params.set_promise_separate_access_units_on_input(true); |
| decoder_params.set_require_hw(!enable_sw_decoding_); |
| |
| auto decoder_factory = base::ComponentContextForProcess() |
| ->svc() |
| ->Connect<fuchsia::mediacodec::CodecFactory>(); |
| fuchsia::media::StreamProcessorPtr decoder; |
| decoder_factory->CreateDecoder(std::move(decoder_params), |
| decoder.NewRequest()); |
| decoder_ = std::make_unique<StreamProcessorHelper>(std::move(decoder), this); |
| |
| current_config_ = config; |
| |
| std::move(done_callback).Run(OkStatus()); |
| } |
| |
| void FuchsiaVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer, |
| DecodeCB decode_cb) { |
| if (!decoder_) { |
| // Post the callback to the current sequence as DecoderStream doesn't expect |
| // Decode() to complete synchronously. |
| base::SequencedTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce(std::move(decode_cb), DecodeStatus::DECODE_ERROR)); |
| return; |
| } |
| |
| decode_callbacks_.push_back(std::move(decode_cb)); |
| |
| sysmem_buffer_stream_->EnqueueBuffer(std::move(buffer)); |
| } |
| |
| void FuchsiaVideoDecoder::Reset(base::OnceClosure closure) { |
| DropInputQueue(DecodeStatus::ABORTED); |
| base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, |
| std::move(closure)); |
| } |
| |
| bool FuchsiaVideoDecoder::NeedsBitstreamConversion() const { |
| return true; |
| } |
| |
| bool FuchsiaVideoDecoder::CanReadWithoutStalling() const { |
| return num_used_output_buffers_ < kMaxUsedOutputBuffers; |
| } |
| |
| int FuchsiaVideoDecoder::GetMaxDecodeRequests() const { |
| return max_decoder_requests_; |
| } |
| |
| StatusCode FuchsiaVideoDecoder::InitializeSysmemBufferStream( |
| bool is_encrypted, |
| CdmContext* cdm_context, |
| bool* out_secure_mode) { |
| DCHECK(!sysmem_buffer_stream_); |
| |
| *out_secure_mode = false; |
| |
| // By default queue as many decode requests as the input buffers available |
| // with one extra request to be able to send a new InputBuffer immediately. |
| max_decoder_requests_ = kNumInputBuffers + 1; |
| |
| if (is_encrypted) { |
| // Caller makes sure |cdm_context| is available if the stream is encrypted. |
| if (!cdm_context) { |
| DLOG(ERROR) << "No cdm context for encrypted stream."; |
| return StatusCode::kDecoderMissingCdmForEncryptedContent; |
| } |
| |
| // Use FuchsiaStreamDecryptor with FuchsiaCdm (it doesn't support |
| // media::Decryptor interface). Otherwise (e.g. for ClearKey CDM) use |
| // DecryptingSysmemBufferStream. |
| FuchsiaCdmContext* fuchsia_cdm = cdm_context->GetFuchsiaCdmContext(); |
| if (fuchsia_cdm) { |
| *out_secure_mode = base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kEnableProtectedVideoBuffers); |
| sysmem_buffer_stream_ = |
| fuchsia_cdm->CreateStreamDecryptor(*out_secure_mode); |
| |
| // For optimal performance allow more requests to fill the decryptor |
| // queue. |
| max_decoder_requests_ += FuchsiaStreamDecryptor::kInputBufferCount; |
| } else { |
| sysmem_buffer_stream_ = std::make_unique<DecryptingSysmemBufferStream>( |
| &sysmem_allocator_, cdm_context, Decryptor::kVideo); |
| } |
| } else { |
| sysmem_buffer_stream_ = |
| std::make_unique<PassthroughSysmemBufferStream>(&sysmem_allocator_); |
| } |
| |
| sysmem_buffer_stream_->Initialize(this, kInputBufferSize, kNumInputBuffers); |
| |
| return StatusCode::kOk; |
| } |
| |
| void FuchsiaVideoDecoder::OnSysmemBufferStreamBufferCollectionToken( |
| fuchsia::sysmem::BufferCollectionTokenPtr token) { |
| DCHECK(decoder_); |
| decoder_->SetInputBufferCollectionToken(std::move(token)); |
| } |
| |
| void FuchsiaVideoDecoder::OnSysmemBufferStreamOutputPacket( |
| StreamProcessorHelper::IoPacket packet) { |
| packet.AddOnDestroyClosure( |
| base::BindOnce(&FuchsiaVideoDecoder::CallNextDecodeCallback, |
| decode_callbacks_weak_factory_.GetWeakPtr())); |
| decoder_->Process(std::move(packet)); |
| } |
| |
| void FuchsiaVideoDecoder::OnSysmemBufferStreamEndOfStream() { |
| decoder_->ProcessEos(); |
| } |
| |
| void FuchsiaVideoDecoder::OnSysmemBufferStreamError() { |
| OnError(); |
| } |
| |
| void FuchsiaVideoDecoder::OnSysmemBufferStreamNoKey() { |
| waiting_cb_.Run(WaitingReason::kNoDecryptionKey); |
| } |
| |
| void FuchsiaVideoDecoder::OnStreamProcessorAllocateOutputBuffers( |
| const fuchsia::media::StreamBufferConstraints& output_constraints) { |
| ReleaseOutputBuffers(); |
| |
| output_buffer_collection_ = sysmem_allocator_.AllocateNewCollection(); |
| |
| output_buffer_collection_->CreateSharedToken( |
| base::BindOnce(&StreamProcessorHelper::CompleteOutputBuffersAllocation, |
| base::Unretained(decoder_.get())), |
| "codec"); |
| output_buffer_collection_->CreateSharedToken( |
| base::BindOnce(&FuchsiaVideoDecoder::SetBufferCollectionTokenForGpu, |
| base::Unretained(this)), |
| "gpu"); |
| |
| fuchsia::sysmem::BufferCollectionConstraints buffer_constraints; |
| buffer_constraints.usage.none = fuchsia::sysmem::noneUsage; |
| buffer_constraints.min_buffer_count_for_camping = kOutputBuffersForCamping; |
| buffer_constraints.min_buffer_count_for_shared_slack = |
| kMaxUsedOutputBuffers - kOutputBuffersForCamping; |
| output_buffer_collection_->Initialize(std::move(buffer_constraints), |
| "ChromiumVideoDecoderOutput"); |
| } |
| |
| void FuchsiaVideoDecoder::OnStreamProcessorEndOfStream() { |
| // Decode() is not supposed to be called again after EOF. |
| DCHECK_EQ(decode_callbacks_.size(), 1U); |
| CallNextDecodeCallback(); |
| } |
| |
| void FuchsiaVideoDecoder::OnStreamProcessorOutputFormat( |
| fuchsia::media::StreamOutputFormat output_format) { |
| auto* format = output_format.mutable_format_details(); |
| if (!format->has_domain() || !format->domain().is_video() || |
| !format->domain().video().is_uncompressed()) { |
| DLOG(ERROR) << "Received OnOutputFormat() with invalid format."; |
| OnError(); |
| return; |
| } |
| |
| output_format_ = std::move(format->mutable_domain()->video().uncompressed()); |
| } |
| |
| void FuchsiaVideoDecoder::OnStreamProcessorOutputPacket( |
| StreamProcessorHelper::IoPacket output_packet) { |
| fuchsia::sysmem::PixelFormatType sysmem_pixel_format = |
| output_format_.image_format.pixel_format.type; |
| |
| VideoPixelFormat pixel_format; |
| gfx::BufferFormat buffer_format; |
| VkFormat vk_format; |
| switch (sysmem_pixel_format) { |
| case fuchsia::sysmem::PixelFormatType::NV12: |
| pixel_format = PIXEL_FORMAT_NV12; |
| buffer_format = gfx::BufferFormat::YUV_420_BIPLANAR; |
| vk_format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM; |
| break; |
| |
| case fuchsia::sysmem::PixelFormatType::I420: |
| case fuchsia::sysmem::PixelFormatType::YV12: |
| pixel_format = PIXEL_FORMAT_I420; |
| buffer_format = gfx::BufferFormat::YVU_420; |
| vk_format = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM; |
| break; |
| |
| default: |
| DLOG(ERROR) << "Unsupported pixel format: " |
| << static_cast<int>(sysmem_pixel_format); |
| OnError(); |
| return; |
| } |
| |
| size_t buffer_index = output_packet.buffer_index(); |
| |
| if (buffer_index >= output_mailboxes_.size()) |
| output_mailboxes_.resize(buffer_index + 1, nullptr); |
| |
| auto coded_size = gfx::Size(output_format_.primary_width_pixels, |
| output_format_.primary_height_pixels); |
| |
| if (!output_mailboxes_[buffer_index]) { |
| gfx::GpuMemoryBufferHandle gmb_handle; |
| gmb_handle.type = gfx::NATIVE_PIXMAP; |
| gmb_handle.native_pixmap_handle.buffer_collection_id = |
| output_buffer_collection_id_; |
| gmb_handle.native_pixmap_handle.buffer_index = buffer_index; |
| |
| auto gmb = gpu::GpuMemoryBufferImplNativePixmap::CreateFromHandle( |
| client_native_pixmap_factory_.get(), std::move(gmb_handle), coded_size, |
| buffer_format, gfx::BufferUsage::GPU_READ, |
| gpu::GpuMemoryBufferImpl::DestructionCallback()); |
| |
| output_mailboxes_[buffer_index] = |
| new OutputMailbox(raster_context_provider_, std::move(gmb)); |
| } else { |
| raster_context_provider_->SharedImageInterface()->UpdateSharedImage( |
| gpu::SyncToken(), output_mailboxes_[buffer_index]->mailbox()); |
| } |
| |
| auto display_rect = gfx::Rect(output_format_.primary_display_width_pixels, |
| output_format_.primary_display_height_pixels); |
| |
| VideoAspectRatio aspect_ratio = container_aspect_ratio_; |
| if (!aspect_ratio.IsValid() && output_format_.has_pixel_aspect_ratio) { |
| aspect_ratio = |
| VideoAspectRatio::PAR(output_format_.pixel_aspect_ratio_width, |
| output_format_.pixel_aspect_ratio_height); |
| } |
| |
| auto timestamp = output_packet.timestamp(); |
| |
| // SendInputPacket() sets timestamp for all packets sent to the decoder, so we |
| // expect to receive timestamp for all decoded frames. Missing timestamp |
| // indicates a bug in the decoder implementation. |
| if (timestamp == kNoTimestamp) { |
| LOG(ERROR) << "Received frame without timestamp."; |
| OnError(); |
| return; |
| } |
| |
| num_used_output_buffers_++; |
| |
| auto frame = output_mailboxes_[buffer_index]->CreateFrame( |
| pixel_format, coded_size, display_rect, |
| aspect_ratio.GetNaturalSize(display_rect), timestamp, |
| base::BindOnce(&FuchsiaVideoDecoder::ReleaseOutputPacket, |
| base::Unretained(this), std::move(output_packet))); |
| |
| // Currently sysmem doesn't specify location of chroma samples relative to |
| // luma (see fxb/13677). Assume they are cosited with luma. YCbCr info here |
| // must match the values passed for the same buffer in |
| // ui::SysmemBufferCollection::CreateVkImage() (see |
| // ui/ozone/platform/scenic/sysmem_buffer_collection.cc). |format_features| |
| // are resolved later in the GPU process before this info is passed to Skia. |
| frame->set_ycbcr_info(gpu::VulkanYCbCrInfo( |
| vk_format, /*external_format=*/0, |
| VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709, |
| VK_SAMPLER_YCBCR_RANGE_ITU_NARROW, VK_CHROMA_LOCATION_COSITED_EVEN, |
| VK_CHROMA_LOCATION_COSITED_EVEN, /*format_features=*/0)); |
| |
| // Mark the frame as power-efficient when software decoders are disabled. The |
| // codec may still decode on hardware even when |enable_sw_decoding_| is set |
| // (i.e. power_efficient flag would not be set correctly in that case). It |
| // doesn't matter because software decoders can be enabled only for tests. |
| frame->metadata().power_efficient = !enable_sw_decoding_; |
| |
| // Allow this video frame to be promoted as an overlay, because it was |
| // registered with an ImagePipe. |
| frame->metadata().allow_overlay = use_overlays_for_video_; |
| |
| output_cb_.Run(std::move(frame)); |
| } |
| |
| void FuchsiaVideoDecoder::OnStreamProcessorNoKey() { |
| // Decoder is not expected to produce NoKey() error. |
| DLOG(ERROR) << "Video decoder failed with DECRYPTOR_NO_KEY expectedly"; |
| OnError(); |
| } |
| |
| void FuchsiaVideoDecoder::OnStreamProcessorError() { |
| OnError(); |
| } |
| |
| void FuchsiaVideoDecoder::CallNextDecodeCallback() { |
| DCHECK(!decode_callbacks_.empty()); |
| auto cb = std::move(decode_callbacks_.front()); |
| decode_callbacks_.pop_front(); |
| |
| std::move(cb).Run(DecodeStatus::OK); |
| } |
| |
| bool FuchsiaVideoDecoder::DropInputQueue(DecodeStatus status) { |
| // Invalidate callbacks for CallNextDecodeCallback(), so the callbacks are not |
| // called when the |decoder_| is dropped below. The callbacks are called |
| // explicitly later. |
| decode_callbacks_weak_factory_.InvalidateWeakPtrs(); |
| |
| if (decoder_) { |
| decoder_->Reset(); |
| } |
| |
| if (sysmem_buffer_stream_) { |
| sysmem_buffer_stream_->Reset(); |
| } |
| |
| auto weak_this = weak_this_; |
| |
| for (auto& cb : decode_callbacks_) { |
| std::move(cb).Run(status); |
| |
| // DecodeCB may destroy |this|. |
| if (!weak_this) |
| return false; |
| } |
| decode_callbacks_.clear(); |
| |
| return true; |
| } |
| |
| void FuchsiaVideoDecoder::OnError() { |
| sysmem_buffer_stream_.reset(); |
| decoder_.reset(); |
| |
| ReleaseOutputBuffers(); |
| |
| DropInputQueue(DecodeStatus::DECODE_ERROR); |
| } |
| |
| void FuchsiaVideoDecoder::SetBufferCollectionTokenForGpu( |
| fuchsia::sysmem::BufferCollectionTokenPtr token) { |
| // Register the new collection with the GPU process. |
| DCHECK(!output_buffer_collection_id_); |
| output_buffer_collection_id_ = gfx::SysmemBufferCollectionId::Create(); |
| raster_context_provider_->SharedImageInterface() |
| ->RegisterSysmemBufferCollection( |
| output_buffer_collection_id_, token.Unbind().TakeChannel(), |
| gfx::BufferFormat::YUV_420_BIPLANAR, gfx::BufferUsage::GPU_READ, |
| use_overlays_for_video_ /*register_with_image_pipe*/); |
| |
| // Exact number of buffers sysmem will allocate is unknown here. |
| // |output_mailboxes_| is resized when we start receiving output frames. |
| DCHECK(output_mailboxes_.empty()); |
| } |
| |
| void FuchsiaVideoDecoder::ReleaseOutputBuffers() { |
| // Release the buffer collection. |
| num_used_output_buffers_ = 0; |
| if (output_buffer_collection_) { |
| output_buffer_collection_.reset(); |
| } |
| |
| // Release all output mailboxes. |
| for (OutputMailbox* mailbox : output_mailboxes_) { |
| if (mailbox) |
| mailbox->Release(); |
| } |
| output_mailboxes_.clear(); |
| |
| // Tell the GPU process to drop the buffer collection. |
| if (output_buffer_collection_id_) { |
| raster_context_provider_->SharedImageInterface() |
| ->ReleaseSysmemBufferCollection(output_buffer_collection_id_); |
| output_buffer_collection_id_ = {}; |
| } |
| } |
| |
| void FuchsiaVideoDecoder::ReleaseOutputPacket( |
| StreamProcessorHelper::IoPacket output_packet) { |
| DCHECK_GT(num_used_output_buffers_, 0U); |
| num_used_output_buffers_--; |
| } |
| |
| } // namespace media |