123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "media/gpu/vaapi/vaapi_video_decoder.h"
- #include <vulkan/vulkan.h>
- #include <limits>
- #include <vector>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/command_line.h"
- #include "base/containers/contains.h"
- #include "base/containers/fixed_flat_map.h"
- #include "base/memory/ptr_util.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/trace_event/trace_event.h"
- #include "build/build_config.h"
- #include "build/chromeos_buildflags.h"
- #include "gpu/ipc/common/gpu_memory_buffer_support.h"
- #include "media/base/bind_to_current_loop.h"
- #include "media/base/format_utils.h"
- #include "media/base/media_log.h"
- #include "media/base/media_switches.h"
- #include "media/base/video_frame.h"
- #include "media/base/video_util.h"
- #include "media/gpu/av1_decoder.h"
- #include "media/gpu/chromeos/dmabuf_video_frame_pool.h"
- #include "media/gpu/chromeos/platform_video_frame_utils.h"
- #include "media/gpu/gpu_video_decode_accelerator_helpers.h"
- #include "media/gpu/macros.h"
- #include "media/gpu/vaapi/av1_vaapi_video_decoder_delegate.h"
- #include "media/gpu/vaapi/h264_vaapi_video_decoder_delegate.h"
- #include "media/gpu/vaapi/va_surface.h"
- #include "media/gpu/vaapi/vaapi_utils.h"
- #include "media/gpu/vaapi/vaapi_wrapper.h"
- #include "media/gpu/vaapi/vp8_vaapi_video_decoder_delegate.h"
- #include "media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.h"
- #include "media/media_buildflags.h"
- #include "ui/gfx/buffer_format_util.h"
- #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
- #include "media/gpu/vaapi/h265_vaapi_video_decoder_delegate.h"
- #endif // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- // gn check does not account for BUILDFLAG(), so including these headers will
- // make gn check fail for builds other than ash-chrome. See gn help nogncheck
- // for more information.
- #include "chromeos/components/cdm_factory_daemon/chromeos_cdm_context.h" // nogncheck
- #include "chromeos/components/cdm_factory_daemon/chromeos_cdm_factory.h" // nogncheck
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- namespace media {
- namespace {
- // Size of the timestamp cache, needs to be large enough for frame-reordering.
- constexpr size_t kTimestampCacheSize = 128;
- absl::optional<VideoPixelFormat> GetPixelFormatForBitDepth(uint8_t bit_depth) {
- constexpr auto kSupportedBitDepthAndGfxFormats = base::MakeFixedFlatMap<
- uint8_t, gfx::BufferFormat>({
- #if defined(USE_OZONE)
- {8u, gfx::BufferFormat::YUV_420_BIPLANAR}, {10u, gfx::BufferFormat::P010},
- #else
- {8u, gfx::BufferFormat::RGBX_8888},
- #endif // defined(USE_OZONE)
- });
- if (!base::Contains(kSupportedBitDepthAndGfxFormats, bit_depth)) {
- VLOGF(1) << "Unsupported bit depth: " << base::strict_cast<int>(bit_depth);
- return absl::nullopt;
- }
- return GfxBufferFormatToVideoPixelFormat(
- kSupportedBitDepthAndGfxFormats.at(bit_depth));
- }
- inline int RoundDownToEven(int x) {
- DCHECK_GE(x, 0);
- return x - (x % 2);
- }
- inline int RoundUpToEven(int x) {
- DCHECK_GE(x, 0);
- CHECK_LT(x, std::numeric_limits<int>::max());
- return x + (x % 2);
- }
- } // namespace
- // static
- base::AtomicRefCount VaapiVideoDecoder::num_instances_(0);
- VaapiVideoDecoder::DecodeTask::DecodeTask(scoped_refptr<DecoderBuffer> buffer,
- int32_t buffer_id,
- DecodeCB decode_done_cb)
- : buffer_(std::move(buffer)),
- buffer_id_(buffer_id),
- decode_done_cb_(std::move(decode_done_cb)) {}
- VaapiVideoDecoder::DecodeTask::~DecodeTask() = default;
- VaapiVideoDecoder::DecodeTask::DecodeTask(DecodeTask&&) = default;
- // static
- std::unique_ptr<VideoDecoderMixin> VaapiVideoDecoder::Create(
- std::unique_ptr<MediaLog> media_log,
- scoped_refptr<base::SequencedTaskRunner> decoder_task_runner,
- base::WeakPtr<VideoDecoderMixin::Client> client) {
- const bool can_create_decoder =
- num_instances_.Increment() < kMaxNumOfInstances ||
- !base::FeatureList::IsEnabled(media::kLimitConcurrentDecoderInstances);
- if (!can_create_decoder) {
- num_instances_.Decrement();
- return nullptr;
- }
- return base::WrapUnique<VideoDecoderMixin>(new VaapiVideoDecoder(
- std::move(media_log), std::move(decoder_task_runner), std::move(client)));
- }
- // static
- absl::optional<SupportedVideoDecoderConfigs>
- VaapiVideoDecoder::GetSupportedConfigs() {
- return ConvertFromSupportedProfiles(
- VaapiWrapper::GetSupportedDecodeProfiles(),
- #if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
- true /* allow_encrypted */);
- #else
- false /* allow_encrypted */);
- #endif
- }
- VaapiVideoDecoder::VaapiVideoDecoder(
- std::unique_ptr<MediaLog> media_log,
- scoped_refptr<base::SequencedTaskRunner> decoder_task_runner,
- base::WeakPtr<VideoDecoderMixin::Client> client)
- : VideoDecoderMixin(std::move(media_log),
- std::move(decoder_task_runner),
- std::move(client)),
- buffer_id_to_timestamp_(kTimestampCacheSize),
- weak_this_factory_(this) {
- VLOGF(2);
- DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- weak_this_ = weak_this_factory_.GetWeakPtr();
- }
- VaapiVideoDecoder::~VaapiVideoDecoder() {
- VLOGF(2);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Abort all currently scheduled decode tasks.
- ClearDecodeTaskQueue(DecoderStatus::Codes::kAborted);
- weak_this_factory_.InvalidateWeakPtrs();
- // Notify |decoder_delegate_| of an imminent VAContextID destruction, so it
- // can destroy any internal structures making use of it.
- if (decoder_delegate_)
- decoder_delegate_->OnVAContextDestructionSoon();
- // Destroy explicitly to DCHECK() that |vaapi_wrapper_| references are held
- // 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();
- DCHECK(vaapi_wrapper_->HasOneRef());
- vaapi_wrapper_ = nullptr;
- }
- num_instances_.Decrement();
- }
- void VaapiVideoDecoder::Initialize(const VideoDecoderConfig& config,
- bool /*low_delay*/,
- CdmContext* cdm_context,
- InitCB init_cb,
- const OutputCB& output_cb,
- const WaitingCB& waiting_cb) {
- DVLOGF(2) << config.AsHumanReadableString();
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(config.IsValidConfig());
- // Reinitializing the decoder is allowed if there are no pending decodes.
- 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(DecoderStatus::Codes::kFailed);
- return;
- }
- DCHECK(state_ == State::kError || state_ == State::kUninitialized ||
- state_ == State::kWaitingForInput);
- if (state_ != State::kUninitialized) {
- DVLOGF(3) << "Reinitializing decoder";
- // Notify |decoder_delegate_| of an imminent VAContextID destruction, so it
- // can destroy any internal structures making use of it.
- decoder_delegate_->OnVAContextDestructionSoon();
- decoder_ = nullptr;
- DCHECK(vaapi_wrapper_);
- // To clear |allocated_va_surfaces_|, we have to first DestroyContext().
- vaapi_wrapper_->DestroyContext();
- allocated_va_surfaces_.clear();
- DCHECK(vaapi_wrapper_->HasOneRef());
- vaapi_wrapper_ = nullptr;
- decoder_delegate_ = nullptr;
- // |cdm_context_ref_| is reset after |decoder_| because we passed
- // |cdm_context_ref_->GetCdmContext()| when creating the |decoder_|, so we
- // don't want |decoder_| to have a dangling pointer. We also destroy
- // |cdm_event_cb_registration_| before |cdm_context_ref_| so that we have a
- // CDM at the moment of destroying the callback registration.
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- cdm_event_cb_registration_ = nullptr;
- #endif
- cdm_context_ref_ = nullptr;
- transcryption_ = false;
- SetState(State::kUninitialized);
- }
- DCHECK(!current_decode_task_);
- DCHECK(decode_task_queue_.empty());
- // Destroying the |decoder_| during re-initialization should release all
- // output buffers (and there should be no output buffers to begin with if the
- // decoder was previously uninitialized).
- DCHECK(output_frames_.empty());
- if (config.is_encrypted()) {
- #if !BUILDFLAG(IS_CHROMEOS_ASH)
- SetErrorState("encrypted content is not supported");
- std::move(init_cb).Run(DecoderStatus::Codes::kUnsupportedEncryptionMode);
- return;
- #else
- if (!cdm_context || !cdm_context->GetChromeOsCdmContext()) {
- SetErrorState("cannot support encrypted stream w/out ChromeOsCdmContext");
- std::move(init_cb).Run(DecoderStatus::Codes::kUnsupportedEncryptionMode);
- return;
- }
- 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()));
- std::move(init_cb).Run(DecoderStatus::Codes::kUnsupportedEncryptionMode);
- return;
- }
- cdm_event_cb_registration_ = cdm_context->RegisterEventCB(
- base::BindRepeating(&VaapiVideoDecoder::OnCdmContextEvent,
- weak_this_factory_.GetWeakPtr()));
- cdm_context_ref_ = cdm_context->GetChromeOsCdmContext()->GetCdmContextRef();
- // On AMD the content is transcrypted by the pipeline before reaching us,
- // but we still need to do special handling with it.
- transcryption_ = (VaapiWrapper::GetImplementationType() ==
- VAImplementation::kMesaGallium);
- #endif
- }
- // Initialize VAAPI wrapper.
- const VideoCodecProfile profile = config.profile();
- vaapi_wrapper_ = VaapiWrapper::CreateForVideoCodec(
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- (!cdm_context_ref_ || transcryption_) ? VaapiWrapper::kDecode
- : VaapiWrapper::kDecodeProtected,
- #else
- VaapiWrapper::kDecode,
- #endif
- profile,
- transcryption_ ? EncryptionScheme::kUnencrypted
- : config.encryption_scheme(),
- base::BindRepeating(&ReportVaapiErrorToUMA,
- "Media.VaapiVideoDecoder.VAAPIError"));
- UMA_HISTOGRAM_BOOLEAN("Media.VaapiVideoDecoder.VaapiWrapperCreationSuccess",
- vaapi_wrapper_.get());
- if (!vaapi_wrapper_.get()) {
- SetErrorState(
- base::StringPrintf("failed initializing VaapiWrapper for profile %s, ",
- GetProfileName(profile).c_str()));
- std::move(init_cb).Run(DecoderStatus::Codes::kUnsupportedProfile);
- return;
- }
- profile_ = profile;
- color_space_ = config.color_space_info();
- hdr_metadata_ = config.hdr_metadata();
- encryption_scheme_ = transcryption_ ? EncryptionScheme::kUnencrypted
- : config.encryption_scheme();
- auto accel_status = CreateAcceleratedVideoDecoder();
- if (!accel_status.is_ok()) {
- SetErrorState("failed to create decoder delegate");
- std::move(init_cb).Run(DecoderStatus(DecoderStatus::Codes::kFailed)
- .AddCause(std::move(accel_status)));
- return;
- }
- aspect_ratio_ = config.aspect_ratio();
- output_cb_ = std::move(output_cb);
- waiting_cb_ = std::move(waiting_cb);
- SetState(State::kWaitingForInput);
- // Notify client initialization was successful.
- std::move(init_cb).Run(DecoderStatus::Codes::kOk);
- }
- void VaapiVideoDecoder::OnCdmContextEvent(CdmContext::Event event) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (event != CdmContext::Event::kHasAdditionalUsableKey)
- return;
- // Invoke the callback we'd get for a protected session update because this is
- // the same thing, it's a trigger that there are new keys, so if we were
- // waiting for a key we should fetch them again.
- ProtectedSessionUpdate(true);
- }
- void VaapiVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
- DecodeCB decode_cb) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DVLOGF(4) << "Queuing input buffer, id: " << next_buffer_id_ << ", size: "
- << (buffer->end_of_stream() ? 0 : buffer->data_size());
- // If we're in the error state, immediately fail the decode task.
- if (state_ == State::kError) {
- // VideoDecoder interface: |decode_cb| can't be called from within Decode().
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(decode_cb), DecoderStatus::Codes::kFailed));
- return;
- }
- if (!buffer->end_of_stream())
- buffer_id_to_timestamp_.Put(next_buffer_id_, buffer->timestamp());
- decode_task_queue_.emplace(std::move(buffer), next_buffer_id_,
- std::move(decode_cb));
- // Generate the next positive buffer id. Don't let it overflow because that
- // behavior is undefined for signed integers, we mask it down to 30 bits to
- // avoid that problem.
- next_buffer_id_ = (next_buffer_id_ + 1) & 0x3fffffff;
- // If we were waiting for input buffers, start decoding again.
- if (state_ == State::kWaitingForInput) {
- DCHECK(!current_decode_task_);
- SetState(State::kDecoding);
- ScheduleNextDecodeTask();
- }
- }
- void VaapiVideoDecoder::ScheduleNextDecodeTask() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(state_, State::kDecoding);
- DCHECK(!current_decode_task_);
- DCHECK(!decode_task_queue_.empty());
- // Dequeue the next decode task.
- current_decode_task_ = std::move(decode_task_queue_.front());
- decode_task_queue_.pop();
- if (!current_decode_task_->buffer_->end_of_stream()) {
- decoder_->SetStream(current_decode_task_->buffer_id_,
- *current_decode_task_->buffer_);
- }
- decoder_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&VaapiVideoDecoder::HandleDecodeTask, weak_this_));
- }
- void VaapiVideoDecoder::HandleDecodeTask() {
- DVLOGF(4);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (state_ != State::kDecoding)
- return;
- DCHECK_EQ(state_, State::kDecoding);
- DCHECK(current_decode_task_);
- // Check whether a flush was requested.
- if (current_decode_task_->buffer_->end_of_stream()) {
- Flush();
- return;
- }
- TRACE_EVENT_BEGIN0("media,gpu", "VaapiVideoDecoder::Decode");
- AcceleratedVideoDecoder::DecodeResult decode_result = decoder_->Decode();
- TRACE_EVENT_END0("media,gpu", "VaapiVideoDecoder::Decode");
- switch (decode_result) {
- case AcceleratedVideoDecoder::kRanOutOfStreamData:
- // Decoding was successful, notify client and try to schedule the next
- // task. Switch to the idle state if we ran out of buffers to decode.
- std::move(current_decode_task_->decode_done_cb_)
- .Run(DecoderStatus::Codes::kOk);
- current_decode_task_ = absl::nullopt;
- if (!decode_task_queue_.empty()) {
- ScheduleNextDecodeTask();
- } else {
- SetState(State::kWaitingForInput);
- }
- break;
- case AcceleratedVideoDecoder::kConfigChange:
- // A new set of output buffers is requested. We either didn't have any
- // output buffers yet or encountered a resolution change.
- // After the pipeline flushes all frames, ApplyResolutionChange() will be
- // called and we can start changing resolution.
- DCHECK(client_);
- SetState(State::kChangingResolution);
- client_->PrepareChangeResolution();
- break;
- case AcceleratedVideoDecoder::kRanOutOfSurfaces:
- // No more surfaces to decode into available, wait until client returns
- // video frames to the frame pool.
- SetState(State::kWaitingForOutput);
- break;
- case AcceleratedVideoDecoder::kNeedContextUpdate:
- SetErrorState("context updates not supported");
- break;
- case AcceleratedVideoDecoder::kDecodeError:
- UMA_HISTOGRAM_BOOLEAN("Media.VaapiVideoDecoder.DecodeError", true);
- SetErrorState("error decoding stream");
- break;
- case AcceleratedVideoDecoder::kTryAgain:
- DVLOG(1) << "Decoder going into the waiting for protected state";
- DCHECK_NE(encryption_scheme_, EncryptionScheme::kUnencrypted);
- SetState(State::kWaitingForProtected);
- // If we have lost our protected HW session, it should be recoverable, so
- // indicate that we have lost our decoder state so it can be reloaded.
- if (decoder_delegate_->HasInitiatedProtectedRecovery())
- waiting_cb_.Run(WaitingReason::kDecoderStateLost);
- break;
- }
- }
- void VaapiVideoDecoder::ClearDecodeTaskQueue(DecoderStatus status) {
- DVLOGF(4);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (current_decode_task_) {
- std::move(current_decode_task_->decode_done_cb_).Run(status);
- current_decode_task_ = absl::nullopt;
- }
- while (!decode_task_queue_.empty()) {
- std::move(decode_task_queue_.front().decode_done_cb_).Run(status);
- decode_task_queue_.pop();
- }
- }
- scoped_refptr<VASurface> VaapiVideoDecoder::CreateSurface() {
- DVLOGF(4);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(state_, State::kDecoding);
- DCHECK(current_decode_task_);
- // Get a video frame from the video frame pool.
- 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(
- base::BindOnce(&VaapiVideoDecoder::NotifyFrameAvailable, weak_this_));
- return nullptr;
- }
- // |frame|s coming from ARC++ are not GpuMemoryBuffer-backed, but they have
- // DmaBufs whose fd numbers are consistent along the lifetime of the VA
- // surfaces they back.
- DCHECK(frame->GetGpuMemoryBuffer() || frame->HasDmaBufs());
- const gfx::GpuMemoryBufferId frame_id =
- frame->GetGpuMemoryBuffer()
- ? frame->GetGpuMemoryBuffer()->GetId()
- : gfx::GpuMemoryBufferId(frame->DmabufFds()[0].get());
- scoped_refptr<VASurface> va_surface;
- if (!base::Contains(allocated_va_surfaces_, frame_id)) {
- scoped_refptr<gfx::NativePixmap> pixmap =
- CreateNativePixmapDmaBuf(frame.get());
- if (!pixmap) {
- SetErrorState("failed to create NativePixmap from VideoFrame");
- return nullptr;
- }
- 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;
- }
- allocated_va_surfaces_[frame_id] = va_surface;
- } else {
- va_surface = allocated_va_surfaces_[frame_id];
- DCHECK_EQ(frame->coded_size(), va_surface->size());
- }
- // Store the mapping between surface and video frame, so we know which video
- // frame to output when the surface is ready. It's also important to keep a
- // reference to the video frame during decoding, as the frame will be
- // automatically returned to the pool when the last reference is dropped.
- VASurfaceID surface_id = va_surface->id();
- DCHECK_EQ(output_frames_.count(surface_id), 0u);
- output_frames_[surface_id] = frame;
- // When the decoder is done using the frame for output or reference, it will
- // drop its reference to the surface. We can then safely remove the associated
- // video frame from |output_frames_|. To be notified when this happens we wrap
- // the surface in another surface with ReleaseVideoFrame() as destruction
- // observer.
- VASurface::ReleaseCB release_frame_cb =
- base::BindOnce(&VaapiVideoDecoder::ReleaseVideoFrame, weak_this_);
- return new VASurface(surface_id, frame->layout().coded_size(),
- va_surface->format(), std::move(release_frame_cb));
- }
- void VaapiVideoDecoder::SurfaceReady(scoped_refptr<VASurface> va_surface,
- int32_t buffer_id,
- const gfx::Rect& visible_rect,
- const VideoColorSpace& color_space) {
- DVLOGF(4);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(state_, State::kDecoding);
- // Find the timestamp associated with |buffer_id|. It's possible that a
- // surface is output multiple times for different |buffer_id|s (e.g. VP9
- // show_existing_frame feature). This means we need to output the same frame
- // again with a different timestamp.
- // On some rare occasions it's also possible that a single DecoderBuffer
- // produces multiple surfaces with the same |buffer_id|, so we shouldn't
- // remove the timestamp from the cache.
- const auto it = buffer_id_to_timestamp_.Peek(buffer_id);
- DCHECK(it != buffer_id_to_timestamp_.end());
- base::TimeDelta timestamp = it->second;
- // Find the frame associated with the surface. We won't erase it from
- // |output_frames_| yet, as the decoder might still be using it for reference.
- DCHECK_EQ(output_frames_.count(va_surface->id()), 1u);
- scoped_refptr<VideoFrame> video_frame = output_frames_[va_surface->id()];
- // Set the timestamp at which the decode operation started on the
- // |video_frame|. If the frame has been outputted before (e.g. because of VP9
- // show-existing-frame feature) we can't overwrite the timestamp directly, as
- // the original frame might still be in use. Instead we wrap the frame in
- // another frame with a different timestamp.
- if (video_frame->timestamp().is_zero())
- video_frame->set_timestamp(timestamp);
- if (video_frame->visible_rect() != visible_rect ||
- video_frame->timestamp() != timestamp) {
- gfx::Size natural_size = aspect_ratio_.GetNaturalSize(visible_rect);
- scoped_refptr<VideoFrame> wrapped_frame = VideoFrame::WrapVideoFrame(
- video_frame, video_frame->format(), visible_rect, natural_size);
- wrapped_frame->set_timestamp(timestamp);
- video_frame = std::move(wrapped_frame);
- }
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- if (cdm_context_ref_ && !transcryption_) {
- // 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();
- if (gfx_color_space.IsValid())
- video_frame->set_color_space(gfx_color_space);
- video_frame->set_hdr_metadata(hdr_metadata_);
- output_cb_.Run(std::move(video_frame));
- }
- void VaapiVideoDecoder::ApplyResolutionChange() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(state_ == State::kChangingResolution ||
- state_ == State::kWaitingForInput);
- DCHECK(output_frames_.empty());
- VLOGF(2);
- if (cdm_context_ref_ && !transcryption_) {
- // Get the screen resolutions so we can determine if we should pre-scale
- // content during decoding to maximize use of overlay downscaling since
- // protected content requires overlays currently.
- // NOTE: Only use this for protected content as other requirements for using
- // it are tied to protected content.
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- cdm_context_ref_->GetCdmContext()
- ->GetChromeOsCdmContext()
- ->GetScreenResolutions(BindToCurrentLoop(base::BindOnce(
- &VaapiVideoDecoder::ApplyResolutionChangeWithScreenSizes,
- weak_this_)));
- return;
- #endif
- }
- ApplyResolutionChangeWithScreenSizes(std::vector<gfx::Size>());
- }
- void VaapiVideoDecoder::ApplyResolutionChangeWithScreenSizes(
- const std::vector<gfx::Size>& screen_resolutions) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(state_ == State::kChangingResolution ||
- state_ == State::kWaitingForInput || state_ == State::kResetting ||
- state_ == State::kError);
- DCHECK(output_frames_.empty());
- VLOGF(2);
- // If we are not in the state for changing resolution, then skip doing it. For
- // all the other states, those can occur because something happened after the
- // async call to get the screen sizes in ApplyResolutionChange(), and in that
- // case we will get another resolution change event when the decoder parses
- // the resolution and notifies us.
- if (state_ != State::kChangingResolution)
- return;
- const uint8_t bit_depth = decoder_->GetBitDepth();
- const absl::optional<VideoPixelFormat> format =
- GetPixelFormatForBitDepth(bit_depth);
- if (!format) {
- SetErrorState(base::StringPrintf("unsupported bit depth: %d", bit_depth));
- return;
- }
- // Notify |decoder_delegate_| of an imminent VAContextID destruction, so it
- // can destroy any internal structures making use of it.
- decoder_delegate_->OnVAContextDestructionSoon();
- // 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_|.
- vaapi_wrapper_->DestroyContext();
- allocated_va_surfaces_.clear();
- 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()) {
- // Ideally we would base this off visible size, but that can change
- // midstream without forcing a config change, so we need to scale the
- // overall decoded image and then apply that same relative scaling to the
- // visible rect later.
- CHECK(cdm_context_ref_);
- gfx::Size max_desired_size;
- 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;
- int target_width;
- int target_height;
- const float screen_aspect =
- static_cast<float>(screen.width()) / screen.height();
- if (pic_aspect >= screen_aspect) {
- // Constrain on 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 = decoder_pic_size.width();
- target_height = decoder_pic_size.height();
- }
- } else {
- // Constrain on 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 = decoder_pic_size.height();
- target_width = decoder_pic_size.width();
- }
- }
- if (target_width > max_desired_size.width() ||
- target_height > max_desired_size.height()) {
- max_desired_size.SetSize(target_width, target_height);
- }
- }
- if (!max_desired_size.IsEmpty() &&
- 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()));
- const auto decode_to_output_scale_factor =
- static_cast<float>(max_desired_size.width()) /
- decoder_pic_size.width();
- output_pic_size = max_desired_size;
- 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));
- }
- }
- if (profile_ != decoder_->GetProfile()) {
- // When a profile is changed, we need to re-initialize VaapiWrapper.
- profile_ = decoder_->GetProfile();
- auto new_vaapi_wrapper = VaapiWrapper::CreateForVideoCodec(
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- (!cdm_context_ref_ || transcryption_) ? VaapiWrapper::kDecode
- : VaapiWrapper::kDecodeProtected,
- #else
- VaapiWrapper::kDecode,
- #endif
- profile_, encryption_scheme_,
- base::BindRepeating(&ReportVaapiErrorToUMA,
- "Media.VaapiVideoDecoder.VAAPIError"));
- if (!new_vaapi_wrapper.get()) {
- SetErrorState("failed (re)creating VaapiWrapper");
- return;
- }
- decoder_delegate_->set_vaapi_wrapper(new_vaapi_wrapper.get());
- vaapi_wrapper_ = std::move(new_vaapi_wrapper);
- }
- if (!vaapi_wrapper_->CreateContext(decoder_pic_size)) {
- SetErrorState("failed creating VAContext");
- return;
- }
- const gfx::Size decoder_natural_size =
- aspect_ratio_.GetNaturalSize(decoder_visible_rect);
- #if BUILDFLAG(IS_LINUX)
- absl::optional<DmabufVideoFramePool::CreateFrameCB> allocator =
- base::BindRepeating(&AllocateCustomFrameProxy, weak_this_);
- std::vector<ImageProcessor::PixelLayoutCandidate> candidates = {
- {.fourcc = *format_fourcc,
- .size = decoder_pic_size,
- .modifier = gfx::NativePixmapHandle::kNoModifier}};
- #elif BUILDFLAG(IS_CHROMEOS_LACROS)
- absl::optional<DmabufVideoFramePool::CreateFrameCB> allocator = absl::nullopt;
- std::vector<ImageProcessor::PixelLayoutCandidate> candidates = {
- {.fourcc = *format_fourcc,
- .size = decoder_pic_size,
- .modifier = gfx::NativePixmapHandle::kNoModifier}};
- #else
- absl::optional<DmabufVideoFramePool::CreateFrameCB> allocator = absl::nullopt;
- // TODO(b/203240043): We assume that the |dummy_frame|'s modifier matches the
- // buffer returned by the video frame pool. We should create a test to make
- // sure this assumption is never violated.
- // TODO(b/203240043): Create a GMB directly instead of allocating a
- // VideoFrame.
- scoped_refptr<VideoFrame> dummy_frame = CreateGpuMemoryBufferVideoFrame(
- *format, decoder_pic_size, decoder_visible_rect, decoder_natural_size,
- /*timestamp=*/base::TimeDelta(),
- cdm_context_ref_ ? gfx::BufferUsage::PROTECTED_SCANOUT_VDA_WRITE
- : gfx::BufferUsage::SCANOUT_VDA_WRITE);
- if (!dummy_frame) {
- SetErrorState("failed to allocate a dummy buffer");
- return;
- }
- std::vector<ImageProcessor::PixelLayoutCandidate> candidates = {
- {.fourcc = *format_fourcc,
- .size = decoder_pic_size,
- .modifier = dummy_frame->layout().modifier()}};
- #endif // BUILDFLAG(IS_LINUX)
- auto status_or_layout = client_->PickDecoderOutputFormat(
- candidates, decoder_visible_rect, decoder_natural_size,
- output_visible_rect.size(), decoder_->GetRequiredNumOfPictures(),
- /*use_protected=*/!!cdm_context_ref_,
- /*need_aux_frame_pool=*/true, std::move(allocator));
- if (status_or_layout.has_error()) {
- if (status_or_layout == CroStatus::Codes::kResetRequired) {
- DVLOGF(2) << "The frame pool initialization is aborted";
- SetState(State::kExpectingReset);
- } else {
- // TODO(crbug/1103510): don't drop the error on the floor here.
- SetErrorState("failed Initialize()ing the frame pool");
- }
- return;
- }
- DCHECK(current_decode_task_);
- // Retry the current decode task.
- SetState(State::kDecoding);
- decoder_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&VaapiVideoDecoder::HandleDecodeTask, weak_this_));
- }
- // Static
- CroStatus::Or<scoped_refptr<VideoFrame>>
- VaapiVideoDecoder::AllocateCustomFrameProxy(
- base::WeakPtr<VaapiVideoDecoder> decoder,
- VideoPixelFormat format,
- const gfx::Size& coded_size,
- const gfx::Rect& visible_rect,
- const gfx::Size& natural_size,
- bool use_protected,
- bool use_linear_buffers,
- base::TimeDelta timestamp) {
- if (!decoder)
- return CroStatus::Codes::kFailedToCreateVideoFrame;
- return decoder->AllocateCustomFrame(format, coded_size, visible_rect,
- natural_size, use_protected,
- use_linear_buffers, timestamp);
- }
- CroStatus::Or<scoped_refptr<VideoFrame>> VaapiVideoDecoder::AllocateCustomFrame(
- VideoPixelFormat format,
- const gfx::Size& coded_size,
- const gfx::Rect& visible_rect,
- const gfx::Size& natural_size,
- bool use_protected,
- bool use_linear_buffers,
- base::TimeDelta timestamp) {
- DVLOGF(2);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(state_ == State::kChangingResolution || state_ == State::kDecoding);
- DCHECK(!use_linear_buffers);
- if (format != PIXEL_FORMAT_NV12)
- return CroStatus::Codes::kFailedToCreateVideoFrame;
- auto surface = vaapi_wrapper_->CreateVASurfaceWithUsageHints(
- VA_RT_FORMAT_YUV420, coded_size,
- {VaapiWrapper::SurfaceUsageHint::kVideoDecoder});
- if (!surface)
- return CroStatus::Codes::kFailedToCreateVideoFrame;
- auto pixmap_and_info =
- vaapi_wrapper_->ExportVASurfaceAsNativePixmapDmaBuf(*surface);
- if (!pixmap_and_info)
- return CroStatus::Codes::kFailedToCreateVideoFrame;
- gfx::GpuMemoryBufferHandle gmb_handle;
- auto handle_id = GetNextGpuMemoryBufferId();
- gmb_handle.id = handle_id;
- gmb_handle.type = gfx::GpuMemoryBufferType::NATIVE_PIXMAP;
- gmb_handle.native_pixmap_handle = pixmap_and_info->pixmap->ExportHandle();
- if (gmb_handle.native_pixmap_handle.planes.empty())
- return CroStatus::Codes::kFailedToCreateVideoFrame;
- gpu::GpuMemoryBufferSupport gmb_support;
- auto gmb = gmb_support.CreateGpuMemoryBufferImplFromHandle(
- std::move(gmb_handle), pixmap_and_info->pixmap->GetBufferSize(),
- pixmap_and_info->pixmap->GetBufferFormat(),
- gfx::BufferUsage::SCANOUT_VDA_WRITE, base::NullCallback());
- if (!gmb)
- return CroStatus::Codes::kFailedToCreateVideoFrame;
- const gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes] = {};
- auto frame = VideoFrame::WrapExternalGpuMemoryBuffer(
- visible_rect, natural_size, std::move(gmb), mailbox_holders,
- base::NullCallback(), timestamp);
- if (!frame)
- return CroStatus::Codes::kFailedToCreateVideoFrame;
- frame->set_ycbcr_info(gpu::VulkanYCbCrInfo(
- /*image_format=*/VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
- /*external_format=*/0,
- /*suggested_ycbcr_model=*/VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709,
- /*suggested_ycbcr_range=*/VK_SAMPLER_YCBCR_RANGE_ITU_NARROW,
- /*suggested_xchroma_offset=*/VK_CHROMA_LOCATION_COSITED_EVEN,
- /*suggested_ychroma_offset=*/VK_CHROMA_LOCATION_COSITED_EVEN,
- /*format_features=*/VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
- VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
- VK_FORMAT_FEATURE_TRANSFER_DST_BIT |
- VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT |
- VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT |
- VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT));
- allocated_va_surfaces_[handle_id] = surface;
- return frame;
- }
- bool VaapiVideoDecoder::NeedsBitstreamConversion() const {
- DCHECK(output_cb_) << "VaapiVideoDecoder hasn't been initialized";
- NOTREACHED();
- return (profile_ >= H264PROFILE_MIN && profile_ <= H264PROFILE_MAX) ||
- (profile_ >= HEVCPROFILE_MIN && profile_ <= HEVCPROFILE_MAX);
- }
- bool VaapiVideoDecoder::CanReadWithoutStalling() const {
- NOTIMPLEMENTED();
- NOTREACHED();
- return true;
- }
- int VaapiVideoDecoder::GetMaxDecodeRequests() const {
- NOTREACHED();
- return 4;
- }
- VideoDecoderType VaapiVideoDecoder::GetDecoderType() const {
- return VideoDecoderType::kVaapi;
- }
- bool VaapiVideoDecoder::IsPlatformDecoder() const {
- return true;
- }
- bool VaapiVideoDecoder::NeedsTranscryption() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(state_ == State::kWaitingForInput);
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- // We do not need to invoke transcryption if this is coming from a remote CDM
- // since it will already have been done.
- if (cdm_context_ref_ &&
- cdm_context_ref_->GetCdmContext()->GetChromeOsCdmContext() &&
- cdm_context_ref_->GetCdmContext()
- ->GetChromeOsCdmContext()
- ->IsRemoteCdm()) {
- return false;
- }
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- return transcryption_;
- }
- void VaapiVideoDecoder::ReleaseVideoFrame(VASurfaceID surface_id) {
- DVLOGF(4);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // The decoder has finished using the frame associated with |surface_id| for
- // output or reference, so it's safe to drop our reference here. Once the
- // client drops its reference the frame will be automatically returned to the
- // pool for reuse.
- size_t num_erased = output_frames_.erase(surface_id);
- DCHECK_EQ(num_erased, 1u);
- }
- void VaapiVideoDecoder::NotifyFrameAvailable() {
- DVLOGF(4);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // If we were waiting for output buffers, retry the current decode task.
- if (state_ == State::kWaitingForOutput) {
- DCHECK(current_decode_task_);
- SetState(State::kDecoding);
- decoder_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&VaapiVideoDecoder::HandleDecodeTask, weak_this_));
- }
- }
- void VaapiVideoDecoder::ProtectedSessionUpdate(bool success) {
- DVLOGF(4);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!success) {
- SetErrorState("terminating decoding after failed protected update");
- return;
- }
- // If we were waiting for a protected update, retry the current decode task.
- if (state_ != State::kWaitingForProtected)
- return;
- DCHECK(current_decode_task_);
- SetState(State::kDecoding);
- decoder_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&VaapiVideoDecoder::HandleDecodeTask, weak_this_));
- }
- void VaapiVideoDecoder::Flush() {
- DVLOGF(2);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(state_, State::kDecoding);
- DCHECK(current_decode_task_);
- DCHECK(current_decode_task_->buffer_->end_of_stream());
- DCHECK(decode_task_queue_.empty());
- // Flush will block until SurfaceReady() has been called for every frame
- // currently decoding.
- if (!decoder_->Flush()) {
- SetErrorState("failed to flush the decoder delegate");
- return;
- }
- // Put the decoder in an idle state, ready to resume. This will release all
- // VASurfaces currently held, so |output_frames_| should be empty after reset.
- decoder_->Reset();
- DCHECK(output_frames_.empty());
- // Notify the client flushing is done.
- std::move(current_decode_task_->decode_done_cb_)
- .Run(DecoderStatus::Codes::kOk);
- current_decode_task_ = absl::nullopt;
- // Wait for new decodes, no decode tasks should be queued while flushing.
- SetState(State::kWaitingForInput);
- }
- void VaapiVideoDecoder::Reset(base::OnceClosure reset_cb) {
- DVLOGF(2);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // If we encountered an error, skip reset and notify client.
- if (state_ == State::kError) {
- std::move(reset_cb).Run();
- return;
- }
- 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");
- std::move(reset_cb).Run();
- return;
- }
- } else {
- // Put the decoder in an idle state, ready to resume. This will release all
- // VASurfaces currently held, so |output_frames_| should be empty after
- // reset.
- decoder_->Reset();
- }
- DCHECK(output_frames_.empty());
- SetState(State::kResetting);
- // Wait until any pending decode task has been aborted.
- decoder_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&VaapiVideoDecoder::ResetDone, weak_this_,
- std::move(reset_cb)));
- }
- VaapiStatus 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(
- &VaapiVideoDecoder::ProtectedSessionUpdate, weak_this_));
- if (profile_ >= H264PROFILE_MIN && profile_ <= H264PROFILE_MAX) {
- auto accelerator = std::make_unique<H264VaapiVideoDecoderDelegate>(
- this, vaapi_wrapper_, std::move(protected_update_cb),
- cdm_context_ref_ ? cdm_context_ref_->GetCdmContext() : nullptr,
- encryption_scheme_);
- decoder_delegate_ = accelerator.get();
- decoder_ = std::make_unique<H264Decoder>(std::move(accelerator), profile_,
- color_space_);
- } else if (profile_ >= VP8PROFILE_MIN && profile_ <= VP8PROFILE_MAX) {
- auto accelerator =
- std::make_unique<VP8VaapiVideoDecoderDelegate>(this, vaapi_wrapper_);
- decoder_delegate_ = accelerator.get();
- decoder_ =
- std::make_unique<VP8Decoder>(std::move(accelerator), color_space_);
- } else if (profile_ >= VP9PROFILE_MIN && profile_ <= VP9PROFILE_MAX) {
- auto accelerator = std::make_unique<VP9VaapiVideoDecoderDelegate>(
- this, vaapi_wrapper_, std::move(protected_update_cb),
- cdm_context_ref_ ? cdm_context_ref_->GetCdmContext() : nullptr,
- encryption_scheme_);
- decoder_delegate_ = accelerator.get();
- decoder_ = std::make_unique<VP9Decoder>(std::move(accelerator), profile_,
- color_space_);
- }
- #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
- else if (profile_ >= HEVCPROFILE_MIN && profile_ <= HEVCPROFILE_MAX) {
- auto accelerator = std::make_unique<H265VaapiVideoDecoderDelegate>(
- this, vaapi_wrapper_, std::move(protected_update_cb),
- cdm_context_ref_ ? cdm_context_ref_->GetCdmContext() : nullptr,
- encryption_scheme_);
- decoder_delegate_ = accelerator.get();
- decoder_ = std::make_unique<H265Decoder>(std::move(accelerator), profile_,
- color_space_);
- }
- #endif // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
- else if (profile_ >= AV1PROFILE_MIN && profile_ <= AV1PROFILE_MAX) {
- 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_ = std::make_unique<AV1Decoder>(std::move(accelerator), profile_,
- color_space_);
- } else {
- return VaapiStatus(VaapiStatus::Codes::kUnsupportedProfile)
- .WithData("profile", profile_);
- }
- return VaapiStatus::Codes::kOk;
- }
- void VaapiVideoDecoder::ResetDone(base::OnceClosure reset_cb) {
- DVLOGF(2);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(state_, State::kResetting);
- DCHECK(!current_decode_task_);
- DCHECK(decode_task_queue_.empty());
- std::move(reset_cb).Run();
- SetState(State::kWaitingForInput);
- }
- void VaapiVideoDecoder::SetState(State state) {
- DVLOGF(3) << static_cast<int>(state)
- << ", current state: " << static_cast<int>(state_);
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Check whether the state change is valid.
- switch (state) {
- case State::kUninitialized:
- DCHECK(state_ == State::kWaitingForInput || state_ == State::kError);
- break;
- case State::kWaitingForInput:
- DCHECK(decode_task_queue_.empty());
- DCHECK(!current_decode_task_);
- DCHECK(state_ == State::kUninitialized || state_ == State::kDecoding ||
- state_ == State::kResetting);
- break;
- case State::kWaitingForProtected:
- DCHECK(!!cdm_context_ref_);
- [[fallthrough]];
- case State::kWaitingForOutput:
- DCHECK(current_decode_task_);
- DCHECK_EQ(state_, State::kDecoding);
- break;
- case State::kDecoding:
- DCHECK(state_ == State::kWaitingForInput ||
- state_ == State::kWaitingForOutput ||
- state_ == State::kChangingResolution ||
- state_ == State::kWaitingForProtected);
- break;
- case State::kResetting:
- DCHECK(state_ == State::kWaitingForInput ||
- state_ == State::kWaitingForOutput || state_ == State::kDecoding ||
- state_ == State::kWaitingForProtected ||
- state_ == State::kChangingResolution ||
- state_ == State::kExpectingReset);
- ClearDecodeTaskQueue(DecoderStatus::Codes::kAborted);
- break;
- case State::kChangingResolution:
- DCHECK_EQ(state_, State::kDecoding);
- break;
- case State::kExpectingReset:
- DCHECK_EQ(state_, State::kChangingResolution);
- break;
- case State::kError:
- ClearDecodeTaskQueue(DecoderStatus::Codes::kFailed);
- break;
- }
- state_ = state;
- }
- void VaapiVideoDecoder::SetErrorState(std::string message) {
- LOG(ERROR) << message;
- if (media_log_)
- MEDIA_LOG(ERROR, media_log_) << "VaapiVideoDecoder: " << message;
- SetState(State::kError);
- }
- } // namespace media
|