blob: e367ed70f157276ab118df9896352087e716589b [file] [log] [blame]
// Copyright 2017 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_D3D11_VIDEO_DECODER_H_
#define MEDIA_GPU_D3D11_VIDEO_DECODER_H_
#include <d3d11.h>
#include <string>
#include <vector>
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "gpu/config/gpu_preferences.h"
#include "media/base/callback_registry.h"
#include "media/base/video_decoder.h"
#include "media/base/win/d3d11_create_device_cb.h"
#include "media/gpu/command_buffer_helper.h"
#include "media/gpu/media_gpu_export.h"
#include "media/gpu/windows/d3d11_com_defs.h"
#include "media/gpu/windows/d3d11_h264_accelerator.h"
#include "media/gpu/windows/d3d11_texture_selector.h"
#include "media/gpu/windows/d3d11_video_decoder_client.h"
#include "media/gpu/windows/d3d11_vp9_accelerator.h"
#include "media/video/supported_video_decoder_config.h"
namespace gpu {
class CommandBufferStub;
} // namespace gpu
namespace media {
class D3D11PictureBuffer;
class D3D11VideoDecoderImpl;
class D3D11VideoDecoderTest;
class MediaLog;
// Video decoder that uses D3D11 directly. It is intended that this class will
// run the decoder on whatever thread it lives on. However, at the moment, it
// only works if it's on the gpu main thread.
class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder,
public D3D11VideoDecoderClient {
public:
// Callback to get a D3D11 device.
using GetD3D11DeviceCB = base::RepeatingCallback<ComD3D11Device()>;
// List of configs that we'll check against when initializing. This is only
// needed since GpuMojoMediaClient merges our supported configs with the VDA
// supported configs.
using SupportedConfigs = std::vector<SupportedVideoDecoderConfig>;
// |helper| must be called from |gpu_task_runner|.
static std::unique_ptr<VideoDecoder> Create(
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
std::unique_ptr<MediaLog> media_log,
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb,
GetD3D11DeviceCB get_d3d11_device_cb,
SupportedConfigs supported_configs);
// VideoDecoder implementation:
std::string GetDisplayName() const override;
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 closure) override;
bool NeedsBitstreamConversion() const override;
bool CanReadWithoutStalling() const override;
int GetMaxDecodeRequests() const override;
// D3D11VideoDecoderClient implementation.
D3D11PictureBuffer* GetPicture() override;
void OutputResult(const CodecPicture* picture,
D3D11PictureBuffer* picture_buffer) override;
static bool GetD3D11FeatureLevel(ComD3D11Device dev,
D3D_FEATURE_LEVEL* feature_level);
// Return the set of video decoder configs that we support.
static std::vector<SupportedVideoDecoderConfig>
GetSupportedVideoDecoderConfigs(
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
GetD3D11DeviceCB get_d3d11_device_cb);
protected:
// Owners should call Destroy(). This is automatic via
// std::default_delete<media::VideoDecoder> when held by a
// std::unique_ptr<media::VideoDecoder>.
~D3D11VideoDecoder() override;
private:
friend class D3D11VideoDecoderTest;
D3D11VideoDecoder(
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
std::unique_ptr<MediaLog> media_log,
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
std::unique_ptr<D3D11VideoDecoderImpl> impl,
base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()>
get_helper_cb,
GetD3D11DeviceCB get_d3d11_device_cb,
SupportedConfigs supported_configs);
// Receive |buffer|, that is now unused by the client.
void ReceivePictureBufferFromClient(scoped_refptr<D3D11PictureBuffer> buffer);
// Called when the gpu side of initialization is complete.
void OnGpuInitComplete(bool success);
// Run the decoder loop.
void DoDecode();
// instantiate |accelerated_video_decoder_| based on the video profile
HRESULT InitializeAcceleratedDecoder(const VideoDecoderConfig& config,
CdmProxyContext* proxy_context,
ComD3D11VideoDecoder video_decoder);
// Query the video device for a specific decoder ID.
bool DeviceHasDecoderID(GUID decoder_guid);
// Create new PictureBuffers. Currently, this completes synchronously, but
// really should have an async interface since it must do some work on the
// gpu main thread.
void CreatePictureBuffers();
enum class NotSupportedReason {
kVideoIsSupported = 0,
// D3D11 version 11.1 required.
kInsufficientD3D11FeatureLevel = 1,
// The video profile is not supported .
kProfileNotSupported = 2,
// GPU options: require zero copy.
kZeroCopyNv12Required = 3,
// GPU options: require zero copy.
kZeroCopyVideoRequired = 4,
// The video codec must be H264.
kCodecNotSupported = 5,
// The media was encrypted.
kEncryptedMedia = 6,
// Call to get the D3D11 device failed.
kCouldNotGetD3D11Device = 7,
// For UMA. Must be the last entry. It should be initialized to the
// numerically largest value above; if you add more entries, then please
// update this to the last one.
kMaxValue = kCouldNotGetD3D11Device
};
enum class D3D11LifetimeProgression {
kInitializeStarted = 0,
kInitializeSucceeded = 1,
kPlaybackSucceeded = 2,
// For UMA. Must be the last entry. It should be initialized to the
// numerically largest value above; if you add more entries, then please
// update this to the last one.
kMaxValue = kPlaybackSucceeded
};
// Log UMA progression state.
void AddLifetimeProgressionStage(D3D11LifetimeProgression stage);
std::unique_ptr<MediaLog> media_log_;
enum class State {
// Initializing resources required to create a codec.
kInitializing,
// Initialization has completed and we're running. This is the only state
// in which |codec_| might be non-null. If |codec_| is null, a codec
// creation is pending.
kRunning,
// The decoder cannot make progress because it doesn't have the key to
// decrypt the buffer. Waiting for a new key to be available.
// This should only be transitioned from kRunning, and should only
// transition to kRunning or kWaitingForReset.
kWaitingForNewKey,
// The decoder cannot make progress because it's waiting for a Reset(). This
// could happen as a result of CdmContext hardware context loss. This should
// only be transitioned from kRunning or kWaitingForNewKey, and should only
// transition to kRunning.
kWaitingForReset,
// A fatal error occurred. A terminal state.
kError,
};
// Callback to notify that new CdmContext event is available.
void OnCdmContextEvent(CdmContext::Event event);
// Enter the kError state. This will fail any pending |init_cb_| and / or
// pending decode as well.
void NotifyError(const char* reason);
// The implementation, which we trampoline to the impl thread.
// This must be freed on the impl thread.
std::unique_ptr<D3D11VideoDecoderImpl> impl_;
// Weak ptr to |impl_|, which we use for callbacks.
base::WeakPtr<D3D11VideoDecoderImpl> impl_weak_;
// Task runner for |impl_|. This must be the GPU main thread.
scoped_refptr<base::SequencedTaskRunner> impl_task_runner_;
// Set in initialize, and used to determine reinitializations.
bool already_initialized_;
gpu::GpuPreferences gpu_preferences_;
gpu::GpuDriverBugWorkarounds gpu_workarounds_;
// During init, these will be set.
VideoDecoderConfig config_;
InitCB init_cb_;
OutputCB output_cb_;
WaitingCB waiting_cb_;
// Right now, this is used both for the video decoder and for display. In
// the future, this should only be for the video decoder. We should use
// the ANGLE device for display (plus texture sharing, if needed).
GetD3D11DeviceCB get_d3d11_device_cb_;
ComD3D11Device device_;
ComD3D11DeviceContext device_context_;
ComD3D11VideoDevice video_device_;
// D3D11 version on this device.
D3D_FEATURE_LEVEL usable_feature_level_;
std::unique_ptr<AcceleratedVideoDecoder> accelerated_video_decoder_;
std::unique_ptr<TextureSelector> texture_selector_;
std::list<std::pair<scoped_refptr<DecoderBuffer>, DecodeCB>>
input_buffer_queue_;
scoped_refptr<DecoderBuffer> current_buffer_;
DecodeCB current_decode_cb_;
base::TimeDelta current_timestamp_;
// Callback registration to keep the new key callback registered.
std::unique_ptr<CallbackRegistration> new_key_callback_registration_;
// Must be called on the gpu main thread. So, don't call it from here,
// since we don't know what thread we're on.
base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb_;
// It would be nice to unique_ptr these, but we give a ref to the VideoFrame
// so that the texture is retained until the mailbox is opened.
std::vector<scoped_refptr<D3D11PictureBuffer>> picture_buffers_;
State state_ = State::kInitializing;
// Callback to get a command buffer helper. Must be called from the gpu
// main thread only.
base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()> get_helper_cb_;
// Entire class should be single-sequence.
SEQUENCE_CHECKER(sequence_checker_);
SupportedConfigs supported_configs_;
base::WeakPtrFactory<D3D11VideoDecoder> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(D3D11VideoDecoder);
};
} // namespace media
#endif // MEDIA_GPU_D3D11_VIDEO_DECODER_H_