blob: f953716dfbef5d28fd41218aa9adb8ee26d31a8f [file] [log] [blame]
// 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.
#ifndef MEDIA_GPU_VAAPI_VAAPI_VIDEO_DECODER_H_
#define MEDIA_GPU_VAAPI_VAAPI_VIDEO_DECODER_H_
#include <stdint.h>
#include <va/va.h>
#include <map>
#include <memory>
#include <utility>
#include "base/containers/mru_cache.h"
#include "base/containers/queue.h"
#include "base/containers/small_map.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "build/chromeos_buildflags.h"
#include "media/base/callback_registry.h"
#include "media/base/cdm_context.h"
#include "media/base/status.h"
#include "media/base/supported_video_decoder_config.h"
#include "media/base/video_aspect_ratio.h"
#include "media/base/video_codecs.h"
#include "media/base/video_frame_layout.h"
#include "media/gpu/chromeos/video_decoder_pipeline.h"
#include "media/gpu/decode_surface_handler.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/gfx/hdr_metadata.h"
namespace media {
class AcceleratedVideoDecoder;
class VaapiVideoDecoderDelegate;
class DmabufVideoFramePool;
class VaapiWrapper;
class VideoFrame;
class VASurface;
class VaapiVideoDecoder : public VideoDecoderMixin,
public DecodeSurfaceHandler<VASurface> {
public:
static std::unique_ptr<VideoDecoderMixin> Create(
std::unique_ptr<MediaLog> media_log,
scoped_refptr<base::SequencedTaskRunner> decoder_task_runner,
base::WeakPtr<VideoDecoderMixin::Client> client);
static absl::optional<SupportedVideoDecoderConfigs> GetSupportedConfigs();
// VideoDecoderMixin implementation, VideoDecoder part.
void Initialize(const VideoDecoderConfig& config,
bool low_delay,
CdmContext* cdm_context,
InitCB init_cb,
const OutputCB& output_cb,
const WaitingCB& waiting_cb) override;
void Decode(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb) override;
void Reset(base::OnceClosure reset_cb) override;
bool NeedsBitstreamConversion() const override;
bool CanReadWithoutStalling() const override;
int GetMaxDecodeRequests() const override;
VideoDecoderType GetDecoderType() const override;
bool IsPlatformDecoder() const override;
// VideoDecoderMixin implementation, specific part.
void ApplyResolutionChange() override;
bool NeedsTranscryption() override;
// DecodeSurfaceHandler<VASurface> implementation.
scoped_refptr<VASurface> CreateSurface() override;
void SurfaceReady(scoped_refptr<VASurface> va_surface,
int32_t buffer_id,
const gfx::Rect& visible_rect,
const VideoColorSpace& color_space) override;
private:
// Decode task holding single decode request.
struct DecodeTask {
DecodeTask(scoped_refptr<DecoderBuffer> buffer,
int32_t buffer_id,
DecodeCB decode_done_cb);
~DecodeTask();
DecodeTask(DecodeTask&&);
DecodeTask& operator=(DecodeTask&&) = default;
scoped_refptr<DecoderBuffer> buffer_;
int32_t buffer_id_ = -1;
DecodeCB decode_done_cb_;
DISALLOW_COPY_AND_ASSIGN(DecodeTask);
};
enum class State {
kUninitialized, // not initialized yet or initialization failed.
kWaitingForInput, // waiting for input buffers.
kWaitingForOutput, // waiting for output buffers.
kWaitingForProtected, // waiting on something related to protected content,
// either setup, full sample parsing or key loading.
kDecoding, // decoding buffers.
kChangingResolution, // need to change resolution, waiting for pipeline to
// be flushed.
kExpectingReset, // resolution change is aborted, waiting for decoder
// to be reset.
kResetting, // resetting decoder.
kError, // decoder encountered an error.
};
VaapiVideoDecoder(
std::unique_ptr<MediaLog> media_log,
scoped_refptr<base::SequencedTaskRunner> decoder_task_runner,
base::WeakPtr<VideoDecoderMixin::Client> client);
~VaapiVideoDecoder() override;
// Schedule the next decode task in the queue to be executed.
void ScheduleNextDecodeTask();
// Try to decode a single input buffer.
void HandleDecodeTask();
// Clear the decode task queue. This is done when resetting or destroying the
// decoder, or encountering an error.
void ClearDecodeTaskQueue(DecodeStatus status);
// Releases the local reference to the VideoFrame associated with the
// specified |surface_id| on the decoder thread. This is called when
// |decoder_| has outputted the VideoFrame and stopped using it as a
// reference frame. Note that this doesn't mean the frame can be reused
// immediately, as it might still be used by the client.
void ReleaseVideoFrame(VASurfaceID surface_id);
// Callback for the frame pool to notify us when a frame becomes available.
void NotifyFrameAvailable();
// Callback from accelerator to indicate the protected state has been updated
// so we can proceed or fail.
void ProtectedSessionUpdate(bool success);
// Flushes |decoder_|, blocking until all pending decode tasks have been
// executed and all frames have been output.
void Flush();
// Called when resetting the decoder is finished, to execute |reset_cb|.
void ResetDone(base::OnceClosure reset_cb);
// Create codec-specific AcceleratedVideoDecoder and reset related variables.
Status CreateAcceleratedVideoDecoder();
// Change the current |state_| to the specified |state|.
void SetState(State state);
// Tell SetState() to change the |state_| to kError and send |message| to
// MediaLog and to LOG(ERROR).
void SetErrorState(std::string message);
// Callback for the CDM to notify |this|.
void OnCdmContextEvent(CdmContext::Event event);
// This is a callback from ApplyResolutionChange() when we need to query the
// browser process for the screen sizes.
void ApplyResolutionChangeWithScreenSizes(
const std::vector<gfx::Size>& screen_resolution);
// Having too many decoder instances at once may cause us to run out of FDs
// and subsequently crash (b/181264362). To avoid that, we limit the maximum
// number of decoder instances that can exist at once. |num_instances_| tracks
// that number.
//
// TODO(andrescj): we can relax this once we extract video decoding into its
// own process.
static constexpr int kMaxNumOfInstances = 16;
static base::AtomicRefCount num_instances_;
// The video decoder's state.
State state_ = State::kUninitialized;
// Callback used to notify the client when a frame is available for output.
OutputCB output_cb_;
// Callback used to notify the client when we have lost decode context and
// request a reset (Used in protected decoding).
WaitingCB waiting_cb_;
// Bitstream information, written during Initialize().
VideoCodecProfile profile_ = VIDEO_CODEC_PROFILE_UNKNOWN;
VideoColorSpace color_space_;
absl::optional<gfx::HDRMetadata> hdr_metadata_;
// Aspect ratio from the config.
VideoAspectRatio aspect_ratio_;
// The time at which each buffer decode operation started. Not each decode
// operation leads to a frame being output and frames might be reordered, so
// we don't know when it's safe to drop a timestamp. This means we need to use
// a cache here, with a size large enough to account for frame reordering.
base::MRUCache<int32_t, base::TimeDelta> buffer_id_to_timestamp_;
// Queue containing all requested decode tasks.
base::queue<DecodeTask> decode_task_queue_;
// The decode task we're currently trying to execute.
absl::optional<DecodeTask> current_decode_task_;
// The next input buffer id.
int32_t next_buffer_id_ = 0;
// The list of frames currently used as output buffers or reference frames.
std::map<VASurfaceID, scoped_refptr<VideoFrame>> output_frames_;
// VASurfaces are created via importing resources from a DmabufVideoFramePool
// into libva in CreateSurface(). The following map keeps those VASurfaces for
// reuse according to the expectations of libva vaDestroySurfaces(): "Surfaces
// can only be destroyed after all contexts using these surfaces have been
// destroyed."
// TODO(crbug.com/1040291): remove this keep-alive when using SharedImages.
base::small_map<std::map<gfx::GpuMemoryBufferId, scoped_refptr<VASurface>>>
allocated_va_surfaces_;
// We need to use a CdmContextRef so that we destruct
// |cdm_event_cb_registration_| before the CDM is destructed. The CDM has
// mechanisms to ensure destruction on the proper thread.
//
// For clarity, the MojoVideoDecoderService does hold a reference to both the
// decoder and the CDM to ensure the CDM doesn't get destructed before the
// decoder; however, in the VideoDecoderPipeline, which owns the
// VaapiVideoDecoder, it uses an asynchronous destructor to destroy the
// pipeline (and thus the VaapiVideoDecoder) on the decoder thread.
std::unique_ptr<CdmContextRef> cdm_context_ref_;
EncryptionScheme encryption_scheme_;
#if BUILDFLAG(IS_CHROMEOS_ASH)
// To keep the CdmContext event callback registered.
std::unique_ptr<CallbackRegistration> cdm_event_cb_registration_;
#endif
// Platform and codec specific video decoder.
std::unique_ptr<AcceleratedVideoDecoder> decoder_;
scoped_refptr<VaapiWrapper> vaapi_wrapper_;
// TODO(crbug.com/1022246): Instead of having the raw pointer here, getting
// the pointer from AcceleratedVideoDecoder.
VaapiVideoDecoderDelegate* decoder_delegate_ = nullptr;
// This is used on AMD protected content implementations to indicate that the
// DecoderBuffers we receive have been transcrypted and need special handling.
bool transcryption_ = false;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtr<VaapiVideoDecoder> weak_this_;
base::WeakPtrFactory<VaapiVideoDecoder> weak_this_factory_;
DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecoder);
};
} // namespace media
#endif // MEDIA_GPU_VAAPI_VAAPI_VIDEO_DECODER_H_