diff options
Diffstat (limited to 'chromium/media/gpu/vaapi/vaapi_video_decoder.cc')
-rw-r--r-- | chromium/media/gpu/vaapi/vaapi_video_decoder.cc | 275 |
1 files changed, 115 insertions, 160 deletions
diff --git a/chromium/media/gpu/vaapi/vaapi_video_decoder.cc b/chromium/media/gpu/vaapi/vaapi_video_decoder.cc index b398d0b257d..d559da0d22d 100644 --- a/chromium/media/gpu/vaapi/vaapi_video_decoder.cc +++ b/chromium/media/gpu/vaapi/vaapi_video_decoder.cc @@ -154,16 +154,13 @@ VaapiVideoDecoder::~VaapiVideoDecoder() { decoder_delegate_->OnVAContextDestructionSoon(); // Destroy explicitly to DCHECK() that |vaapi_wrapper_| references are held - // inside the accelerator in |decoder_|, by the |allocated_va_surfaces_|, by - // the |decode_surface_pool_for_scaling_| and of course by this class. To - // clear |allocated_va_surfaces_| and |decode_surface_pool_for_scaling_| we - // have to first DestroyContext(). + // inside the accelerator in |decoder_|, by the |allocated_va_surfaces_| and + // of course by this class. To clear |allocated_va_surfaces_| we have to first + // DestroyContext(). decoder_ = nullptr; if (vaapi_wrapper_) { vaapi_wrapper_->DestroyContext(); allocated_va_surfaces_.clear(); - while (!decode_surface_pool_for_scaling_.empty()) - decode_surface_pool_for_scaling_.pop(); DCHECK(vaapi_wrapper_->HasOneRef()); vaapi_wrapper_ = nullptr; @@ -181,17 +178,18 @@ void VaapiVideoDecoder::Initialize(const VideoDecoderConfig& config, DVLOGF(2) << config.AsHumanReadableString(); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(config.IsValidConfig()); - DCHECK(state_ == State::kError || state_ == State::kUninitialized || - state_ == State::kWaitingForInput); // Reinitializing the decoder is allowed if there are no pending decodes. - if (current_decode_task_ || !decode_task_queue_.empty()) { + if (current_decode_task_ || !decode_task_queue_.empty() || + state_ == State::kExpectingReset) { LOG(ERROR) << "Don't call Initialize() while there are pending decode tasks"; std::move(init_cb).Run(StatusCode::kVaapiReinitializedDuringDecode); return; } + DCHECK(state_ == State::kError || state_ == State::kUninitialized || + state_ == State::kWaitingForInput); if (state_ != State::kUninitialized) { DVLOGF(3) << "Reinitializing decoder"; @@ -201,13 +199,9 @@ void VaapiVideoDecoder::Initialize(const VideoDecoderConfig& config, decoder_ = nullptr; DCHECK(vaapi_wrapper_); - // To clear |allocated_va_surfaces_| and |decode_surface_pool_for_scaling_| - // we have to first DestroyContext(). + // To clear |allocated_va_surfaces_|, we have to first DestroyContext(). vaapi_wrapper_->DestroyContext(); allocated_va_surfaces_.clear(); - while (!decode_surface_pool_for_scaling_.empty()) - decode_surface_pool_for_scaling_.pop(); - decode_to_output_scale_factor_.reset(); DCHECK(vaapi_wrapper_->HasOneRef()); vaapi_wrapper_ = nullptr; @@ -245,8 +239,17 @@ void VaapiVideoDecoder::Initialize(const VideoDecoderConfig& config, std::move(init_cb).Run(StatusCode::kDecoderMissingCdmForEncryptedContent); return; } - if (config.codec() != kCodecH264 && config.codec() != kCodecVP9 && - config.codec() != kCodecHEVC) { + bool encrypted_av1_support = false; +#if BUILDFLAG(USE_CHROMEOS_PROTECTED_AV1) + encrypted_av1_support = true; +#elif BUILDFLAG(IS_CHROMEOS_LACROS) + encrypted_av1_support = base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kLacrosUseChromeosProtectedAv1); +#endif + if (config.codec() != VideoCodec::kH264 && + config.codec() != VideoCodec::kVP9 && + (config.codec() != VideoCodec::kAV1 || !encrypted_av1_support) && + config.codec() != VideoCodec::kHEVC) { SetErrorState( base::StringPrintf("%s is not supported for encrypted content", GetCodecName(config.codec()).c_str())); @@ -263,7 +266,7 @@ void VaapiVideoDecoder::Initialize(const VideoDecoderConfig& config, VAImplementation::kMesaGallium); #endif #if BUILDFLAG(ENABLE_PLATFORM_HEVC_DECODING) - } else if (config.codec() == kCodecHEVC && + } else if (config.codec() == VideoCodec::kHEVC && !base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableClearHevcForTesting)) { SetErrorState("clear HEVC content is not supported"); @@ -308,10 +311,6 @@ void VaapiVideoDecoder::Initialize(const VideoDecoderConfig& config, return; } - // Get and initialize the frame pool. - DCHECK(client_); - frame_pool_ = client_->GetVideoFramePool(); - aspect_ratio_ = config.aspect_ratio(); output_cb_ = std::move(output_cb); @@ -390,7 +389,7 @@ void VaapiVideoDecoder::HandleDecodeTask() { DVLOGF(4); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (state_ == State::kError || state_ == State::kResetting) + if (state_ != State::kDecoding) return; DCHECK_EQ(state_, State::kDecoding); @@ -472,11 +471,14 @@ scoped_refptr<VASurface> VaapiVideoDecoder::CreateSurface() { DCHECK(current_decode_task_); // Get a video frame from the video frame pool. - scoped_refptr<VideoFrame> frame = frame_pool_->GetFrame(); + DCHECK(client_); + DmabufVideoFramePool* frame_pool = client_->GetVideoFramePool(); + DCHECK(frame_pool); + scoped_refptr<VideoFrame> frame = frame_pool->GetFrame(); if (!frame) { // Ask the video frame pool to notify us when new frames are available, so // we can retry the current decode task. - frame_pool_->NotifyWhenFrameAvailable( + frame_pool->NotifyWhenFrameAvailable( base::BindOnce(&VaapiVideoDecoder::NotifyFrameAvailable, weak_this_)); return nullptr; } @@ -499,8 +501,8 @@ scoped_refptr<VASurface> VaapiVideoDecoder::CreateSurface() { return nullptr; } - va_surface = vaapi_wrapper_->CreateVASurfaceForPixmap(std::move(pixmap), - transcryption_); + va_surface = vaapi_wrapper_->CreateVASurfaceForPixmap( + std::move(pixmap), cdm_context_ref_ || transcryption_); if (!va_surface || va_surface->id() == VA_INVALID_ID) { SetErrorState("failed to create VASurface from VideoFrame"); return nullptr; @@ -532,55 +534,6 @@ scoped_refptr<VASurface> VaapiVideoDecoder::CreateSurface() { va_surface->format(), std::move(release_frame_cb)); } -scoped_refptr<VASurface> VaapiVideoDecoder::CreateDecodeSurface() { - DVLOGF(4); - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK_EQ(state_, State::kDecoding); - DCHECK(current_decode_task_); - - if (decode_surface_pool_for_scaling_.empty()) - return nullptr; - - // Get surface from pool. - std::unique_ptr<ScopedVASurface> surface = - std::move(decode_surface_pool_for_scaling_.front()); - decode_surface_pool_for_scaling_.pop(); - // Gather information about the surface to avoid use-after-move. - const VASurfaceID surface_id = surface->id(); - const gfx::Size surface_size = surface->size(); - const unsigned int surface_format = surface->format(); - // Wrap the ScopedVASurface inside a VASurface indirectly. - VASurface::ReleaseCB release_decode_surface_cb = - base::BindOnce(&VaapiVideoDecoder::ReturnDecodeSurfaceToPool, weak_this_, - std::move(surface)); - return new VASurface(surface_id, surface_size, surface_format, - std::move(release_decode_surface_cb)); -} - -bool VaapiVideoDecoder::IsScalingDecode() { - // If we're not decoding while scaling, we shouldn't have any surfaces for - // that purpose. - DCHECK(!!decode_to_output_scale_factor_ || - decode_surface_pool_for_scaling_.empty()); - return !!decode_to_output_scale_factor_; -} - -const gfx::Rect VaapiVideoDecoder::GetOutputVisibleRect( - const gfx::Rect& decode_visible_rect, - const gfx::Size& output_picture_size) { - if (!IsScalingDecode()) - return decode_visible_rect; - DCHECK_LT(*decode_to_output_scale_factor_, 1.0f); - gfx::Rect output_rect = - ScaleToEnclosedRect(decode_visible_rect, *decode_to_output_scale_factor_); - // Make the dimensions even numbered to align with other requirements later in - // the pipeline. - output_rect.set_width(RoundDownToEven(output_rect.width())); - output_rect.set_height(RoundDownToEven(output_rect.height())); - CHECK(gfx::Rect(output_picture_size).Contains(output_rect)); - return output_rect; -} - void VaapiVideoDecoder::SurfaceReady(scoped_refptr<VASurface> va_surface, int32_t buffer_id, const gfx::Rect& visible_rect, @@ -629,6 +582,23 @@ void VaapiVideoDecoder::SurfaceReady(scoped_refptr<VASurface> va_surface, // still valid once we get to the compositor stage. uint32_t protected_instance_id = vaapi_wrapper_->GetProtectedInstanceID(); video_frame->metadata().hw_protected_validation_id = protected_instance_id; + +#if BUILDFLAG(IS_CHROMEOS_ASH) + // Additionally, we store the VA-API protected session ID so that it can be + // re-used for scaling the decoded video frame later in the pipeline. + VAProtectedSessionID va_protected_session_id = + vaapi_wrapper_->GetProtectedSessionID(); + + static_assert( + std::is_same<decltype(va_protected_session_id), + decltype( + video_frame->metadata() + .hw_va_protected_session_id)::value_type>::value, + "The type of VideoFrameMetadata::hw_va_protected_session_id " + "does not match the type exposed by VaapiWrapper"); + video_frame->metadata().hw_va_protected_session_id = + va_protected_session_id; +#endif // BUILDFLAG(IS_CHROMEOS_ASH) } const auto gfx_color_space = color_space.ToGfxColorSpace(); @@ -691,21 +661,18 @@ void VaapiVideoDecoder::ApplyResolutionChangeWithScreenSizes( // All pending decode operations will be completed before triggering a // resolution change, so we can safely DestroyContext() here; that, in turn, - // allows for clearing the |allocated_va_surfaces_| and the - // |decode_surface_pool_for_scaling_|. + // allows for clearing the |allocated_va_surfaces_|. vaapi_wrapper_->DestroyContext(); allocated_va_surfaces_.clear(); - while (!decode_surface_pool_for_scaling_.empty()) - decode_surface_pool_for_scaling_.pop(); - decode_to_output_scale_factor_.reset(); - - gfx::Rect output_visible_rect = decoder_->GetVisibleRect(); - gfx::Size output_pic_size = decoder_->GetPicSize(); - if (output_pic_size.IsEmpty()) { + const gfx::Rect decoder_visible_rect = decoder_->GetVisibleRect(); + const gfx::Size decoder_pic_size = decoder_->GetPicSize(); + if (decoder_pic_size.IsEmpty()) { SetErrorState("|decoder_| returned an empty picture size"); return; } + gfx::Rect output_visible_rect = decoder_visible_rect; + gfx::Size output_pic_size = decoder_pic_size; const auto format_fourcc = Fourcc::FromVideoPixelFormat(*format); CHECK(format_fourcc); if (!screen_resolutions.empty()) { @@ -715,8 +682,8 @@ void VaapiVideoDecoder::ApplyResolutionChangeWithScreenSizes( // visible rect later. CHECK(cdm_context_ref_); gfx::Size max_desired_size; - const float pic_aspect = - static_cast<float>(output_pic_size.width()) / output_pic_size.height(); + const float pic_aspect = static_cast<float>(decoder_pic_size.width()) / + decoder_pic_size.height(); for (const auto& screen : screen_resolutions) { if (screen.IsEmpty()) continue; @@ -726,23 +693,23 @@ void VaapiVideoDecoder::ApplyResolutionChangeWithScreenSizes( static_cast<float>(screen.width()) / screen.height(); if (pic_aspect >= screen_aspect) { // Constrain on width. - if (screen.width() < output_pic_size.width()) { + if (screen.width() < decoder_pic_size.width()) { target_width = screen.width(); target_height = base::checked_cast<int>(std::lround(target_width / pic_aspect)); } else { - target_width = output_pic_size.width(); - target_height = output_pic_size.height(); + target_width = decoder_pic_size.width(); + target_height = decoder_pic_size.height(); } } else { // Constrain on height. - if (screen.height() < output_pic_size.height()) { + if (screen.height() < decoder_pic_size.height()) { target_height = screen.height(); target_width = base::checked_cast<int>(std::lround(target_height * pic_aspect)); } else { - target_height = output_pic_size.height(); - target_width = output_pic_size.width(); + target_height = decoder_pic_size.height(); + target_width = decoder_pic_size.width(); } } if (target_width > max_desired_size.width() || @@ -751,64 +718,26 @@ void VaapiVideoDecoder::ApplyResolutionChangeWithScreenSizes( } } if (!max_desired_size.IsEmpty() && - max_desired_size.width() < output_pic_size.width()) { + max_desired_size.width() < decoder_pic_size.width()) { // Fix this so we are sure it's on a multiple of two to deal with // subsampling. max_desired_size.set_width(RoundUpToEven(max_desired_size.width())); max_desired_size.set_height(RoundUpToEven(max_desired_size.height())); - decode_to_output_scale_factor_ = + const auto decode_to_output_scale_factor = static_cast<float>(max_desired_size.width()) / - output_pic_size.width(); + decoder_pic_size.width(); output_pic_size = max_desired_size; - output_visible_rect = - GetOutputVisibleRect(output_visible_rect, output_pic_size); - - // Create the surface pool for decoding, the normal pool will be used for - // output. - const size_t decode_pool_size = decoder_->GetRequiredNumOfPictures(); - const absl::optional<gfx::BufferFormat> buffer_format = - VideoPixelFormatToGfxBufferFormat(*format); - if (!buffer_format) { - decode_to_output_scale_factor_.reset(); - SetErrorState( - base::StringPrintf("unsupported pixel format: %s", - VideoPixelFormatToString(*format).c_str())); - return; - } - const uint32_t va_fourcc = - VaapiWrapper::BufferFormatToVAFourCC(*buffer_format); - const uint32_t va_rt_format = - VaapiWrapper::BufferFormatToVARTFormat(*buffer_format); - if (!va_fourcc || !va_rt_format) { - decode_to_output_scale_factor_.reset(); - SetErrorState( - base::StringPrintf("VA-API does not support: %s", - gfx::BufferFormatToString(*buffer_format))); - return; - } - const gfx::Size decoder_pic_size = decoder_->GetPicSize(); - auto scoped_va_surfaces = vaapi_wrapper_->CreateScopedVASurfaces( - base::strict_cast<unsigned int>(va_rt_format), decoder_pic_size, - {VaapiWrapper::SurfaceUsageHint::kVideoDecoder}, decode_pool_size, - /*visible_size=*/absl::nullopt, va_fourcc); - if (scoped_va_surfaces.empty()) { - decode_to_output_scale_factor_.reset(); - SetErrorState("failed creating VASurfaces"); - return; - } - - for (auto&& scoped_va_surface : scoped_va_surfaces) - decode_surface_pool_for_scaling_.push(std::move(scoped_va_surface)); + output_visible_rect = ScaleToEnclosedRect(decoder_visible_rect, + decode_to_output_scale_factor); + // Make the dimensions even numbered to align with other requirements + // later in the pipeline. + output_visible_rect.set_width( + RoundDownToEven(output_visible_rect.width())); + output_visible_rect.set_height( + RoundDownToEven(output_visible_rect.height())); + CHECK(gfx::Rect(output_pic_size).Contains(output_visible_rect)); } } - const gfx::Size natural_size = - aspect_ratio_.GetNaturalSize(output_visible_rect); - if (!frame_pool_->Initialize( - *format_fourcc, output_pic_size, output_visible_rect, natural_size, - decoder_->GetRequiredNumOfPictures(), !!cdm_context_ref_)) { - SetErrorState("failed Initialize()ing the frame pool"); - return; - } if (profile_ != decoder_->GetProfile()) { // When a profile is changed, we need to re-initialize VaapiWrapper. @@ -831,11 +760,29 @@ void VaapiVideoDecoder::ApplyResolutionChangeWithScreenSizes( vaapi_wrapper_ = std::move(new_vaapi_wrapper); } - if (!vaapi_wrapper_->CreateContext(decoder_->GetPicSize())) { + if (!vaapi_wrapper_->CreateContext(decoder_pic_size)) { SetErrorState("failed creating VAContext"); return; } + const gfx::Size decoder_natural_size = + aspect_ratio_.GetNaturalSize(decoder_visible_rect); + auto status_or_layout = client_->PickDecoderOutputFormat( + /*candidates=*/{{*format_fourcc, decoder_pic_size}}, decoder_visible_rect, + decoder_natural_size, output_visible_rect.size(), + decoder_->GetRequiredNumOfPictures(), + /*use_protected=*/!!cdm_context_ref_, + /*need_aux_frame_pool=*/true); + if (status_or_layout.has_error()) { + if (std::move(status_or_layout).error().code() == StatusCode::kAborted) { + DVLOGF(2) << "The frame pool initialization is aborted."; + SetState(State::kExpectingReset); + } else { + SetErrorState("failed Initialize()ing the frame pool"); + } + return; + } + DCHECK(current_decode_task_); // Retry the current decode task. SetState(State::kDecoding); @@ -871,6 +818,9 @@ bool VaapiVideoDecoder::IsPlatformDecoder() const { } bool VaapiVideoDecoder::NeedsTranscryption() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(state_ == State::kWaitingForInput); + return transcryption_; } @@ -958,12 +908,16 @@ void VaapiVideoDecoder::Reset(base::OnceClosure reset_cb) { return; } - if (state_ == State::kChangingResolution) { - // Recreate |decoder_| and |decoder_delegate_| if we are Reset() in the - // interim between calling |client_|s PrepareChangeResolution() and being - // called back on ApplyResolutionChange(), so the latter will find a fresh - // |decoder_|. Also give a chance to |decoder_delegate_| to release its - // internal data structures. + if (state_ == State::kChangingResolution || + state_ == State::kExpectingReset) { + // Recreate |decoder_| and |decoder_delegate_| if we are either: + // a) Reset() in the interim between calling |client_|s + // PrepareChangeResolution() and being called back on + // ApplyResolutionChange(), so the latter will find a fresh |decoder_|; + // b) expecting a Reset() after the initialization of the frame pool was + // aborted. + // Also give a chance to |decoder_delegate_| to release its internal data + // structures. decoder_delegate_->OnVAContextDestructionSoon(); if (!CreateAcceleratedVideoDecoder().is_ok()) { SetErrorState("failed to (re)create decoder/delegate"); @@ -989,6 +943,9 @@ void VaapiVideoDecoder::Reset(base::OnceClosure reset_cb) { Status VaapiVideoDecoder::CreateAcceleratedVideoDecoder() { DVLOGF(2); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(state_ == State::kUninitialized || + state_ == State::kChangingResolution || + state_ == State::kExpectingReset); VaapiVideoDecoderDelegate::ProtectedSessionUpdateCB protected_update_cb = BindToCurrentLoop(base::BindRepeating( @@ -1031,8 +988,10 @@ Status VaapiVideoDecoder::CreateAcceleratedVideoDecoder() { } #endif // BUILDFLAG(ENABLE_PLATFORM_HEVC_DECODING) else if (profile_ >= AV1PROFILE_MIN && profile_ <= AV1PROFILE_MAX) { - auto accelerator = - std::make_unique<AV1VaapiVideoDecoderDelegate>(this, vaapi_wrapper_); + auto accelerator = std::make_unique<AV1VaapiVideoDecoderDelegate>( + this, vaapi_wrapper_, std::move(protected_update_cb), + cdm_context_ref_ ? cdm_context_ref_->GetCdmContext() : nullptr, + encryption_scheme_); decoder_delegate_ = accelerator.get(); decoder_.reset(new AV1Decoder(std::move(accelerator), profile_)); @@ -1087,12 +1046,16 @@ void VaapiVideoDecoder::SetState(State state) { DCHECK(state_ == State::kWaitingForInput || state_ == State::kWaitingForOutput || state_ == State::kDecoding || state_ == State::kWaitingForProtected || - state_ == State::kChangingResolution); + state_ == State::kChangingResolution || + state_ == State::kExpectingReset); ClearDecodeTaskQueue(DecodeStatus::ABORTED); break; case State::kChangingResolution: DCHECK_EQ(state_, State::kDecoding); break; + case State::kExpectingReset: + DCHECK_EQ(state_, State::kChangingResolution); + break; case State::kError: ClearDecodeTaskQueue(DecodeStatus::DECODE_ERROR); break; @@ -1108,12 +1071,4 @@ void VaapiVideoDecoder::SetErrorState(std::string message) { SetState(State::kError); } -void VaapiVideoDecoder::ReturnDecodeSurfaceToPool( - std::unique_ptr<ScopedVASurface> surface, - VASurfaceID) { - DVLOGF(4); - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - decode_surface_pool_for_scaling_.push(std::move(surface)); -} - } // namespace media |