blob: 1709672b4c3a86579ddef2f9927d92801cf50d16 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This file contains an implementation of VideoDecodeAccelerator
// that utilizes hardware video decoders, which expose Video4Linux 2 API
// (http://linuxtv.org/downloads/v4l-dvb-apis/).
#ifndef MEDIA_GPU_V4L2_V4L2_VIDEO_DECODE_ACCELERATOR_H_
#define MEDIA_GPU_V4L2_V4L2_VIDEO_DECODE_ACCELERATOR_H_
#include <stddef.h>
#include <stdint.h>
#include <map>
#include <memory>
#include <queue>
#include <utility>
#include <vector>
#include "base/cancelable_callback.h"
#include "base/containers/queue.h"
#include "base/functional/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread.h"
#include "base/trace_event/memory_dump_provider.h"
#include "media/base/limits.h"
#include "media/base/video_decoder_config.h"
#include "media/gpu/chromeos/fourcc.h"
#include "media/gpu/chromeos/image_processor.h"
#include "media/gpu/gpu_video_decode_accelerator_helpers.h"
#include "media/gpu/media_gpu_export.h"
#include "media/gpu/v4l2/v4l2_device.h"
#include "media/video/picture.h"
#include "media/video/video_decode_accelerator.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_fence_egl.h"
namespace gl {
class GLFenceEGL;
} // namespace gl
namespace media {
class V4L2StatefulWorkaround;
namespace v4l2_vda_helpers {
class InputBufferFragmentSplitter;
}
// This class handles video accelerators directly through a V4L2 device exported
// by the hardware blocks.
//
// The threading model of this class is driven by the fact that it needs to
// interface two fundamentally different event queues -- the one Chromium
// provides through MessageLoop, and the one driven by the V4L2 devices which
// is waited on with epoll(). There are three threads involved in this class:
//
// * The child thread, which is the main GPU process thread which calls the
// VideoDecodeAccelerator entry points. Calls from this thread
// generally do not block (with the exception of Initialize() and Destroy()).
// They post tasks to the decoder_thread_, which actually services the task
// and calls back when complete through the
// VideoDecodeAccelerator::Client interface.
// * The decoder_thread_, owned by this class. It services API tasks, through
// the *Task() routines, as well as V4L2 device events, through
// ServiceDeviceTask(). Almost all state modification is done on this thread
// (this doesn't include buffer (re)allocation sequence, see below).
// * The device_poll_thread_, owned by this class. All it does is epoll() on
// the V4L2 in DevicePollTask() and schedule a ServiceDeviceTask() on the
// decoder_thread_ when something interesting happens.
// TODO(sheu): replace this thread with an TYPE_IO decoder_thread_.
//
// Note that this class has (almost) no locks, apart from the pictures_assigned_
// WaitableEvent. Everything (apart from buffer (re)allocation) is serviced on
// the decoder_thread_, so there are no synchronization issues.
// ... well, there are, but it's a matter of getting messages posted in the
// right order, not fiddling with locks.
// Buffer creation is a two-step process that is serviced partially on the
// Child thread, because we need to wait for the client to provide textures
// for the buffers we allocate. We cannot keep the decoder thread running while
// the client allocates Pictures for us, because we need to REQBUFS first to get
// the required number of output buffers from the device and that cannot be done
// unless we free the previous set of buffers, leaving the decoding in a
// inoperable state for the duration of the wait for Pictures. So to prevent
// subtle races (esp. if we get Reset() in the meantime), we block the decoder
// thread while we wait for AssignPictureBuffers from the client.
//
// V4L2VideoDecodeAccelerator may use image processor to convert the output.
// There are three cases:
// Flush: V4L2VDA should wait until image processor returns all processed
// frames.
// Reset: V4L2VDA doesn't need to wait for image processor. When image processor
// returns an old frame, drop it.
// Resolution change: V4L2VDA destroy image processor when destroying output
// buffrers. We cannot drop any frame during resolution change. So V4L2VDA
// should destroy output buffers after image processor returns all the frames.
class MEDIA_GPU_EXPORT V4L2VideoDecodeAccelerator
: public VideoDecodeAccelerator,
public base::trace_event::MemoryDumpProvider {
public:
V4L2VideoDecodeAccelerator(
EGLDisplay egl_display,
const GetGLContextCallback& get_gl_context_cb,
const MakeGLContextCurrentCallback& make_context_current_cb,
scoped_refptr<V4L2Device> device);
V4L2VideoDecodeAccelerator(const V4L2VideoDecodeAccelerator&) = delete;
V4L2VideoDecodeAccelerator& operator=(const V4L2VideoDecodeAccelerator&) =
delete;
~V4L2VideoDecodeAccelerator() override;
// VideoDecodeAccelerator implementation.
// Note: Initialize() and Destroy() are synchronous.
bool Initialize(const Config& config, Client* client) override;
void Decode(BitstreamBuffer bitstream_buffer) override;
void Decode(scoped_refptr<DecoderBuffer> buffer,
int32_t bitstream_id) override;
void AssignPictureBuffers(const std::vector<PictureBuffer>& buffers) override;
void ImportBufferForPicture(
int32_t picture_buffer_id,
VideoPixelFormat pixel_format,
gfx::GpuMemoryBufferHandle gpu_memory_buffer_handles) override;
void ReusePictureBuffer(int32_t picture_buffer_id) override;
void Flush() override;
void Reset() override;
void Destroy() override;
bool TryToSetupDecodeOnSeparateSequence(
const base::WeakPtr<Client>& decode_client,
const scoped_refptr<base::SequencedTaskRunner>& decode_task_runner)
override;
static VideoDecodeAccelerator::SupportedProfiles GetSupportedProfiles();
// base::trace_event::MemoryDumpProvider implementation.
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override;
private:
// These are rather subjectively tuned.
enum {
kInputBufferCount = 8,
// TODO(posciak): determine input buffer size based on level limits.
// See http://crbug.com/255116.
// Input bitstream buffer size for up to 1080p streams.
kInputBufferMaxSizeFor1080p = 1024 * 1024,
// Input bitstream buffer size for up to 4k streams.
kInputBufferMaxSizeFor4k = 4 * kInputBufferMaxSizeFor1080p,
// Number of output buffers to use for each VDA stage above what's required
// by the decoder (e.g. DPB size, in H264). We need
// limits::kMaxVideoFrames to fill up the GpuVideoDecode pipeline,
// and +1 for a frame in transit.
kDpbOutputBufferExtraCount = limits::kMaxVideoFrames + 1,
// Number of extra output buffers if image processor is used.
kDpbOutputBufferExtraCountForImageProcessor = 1,
};
// Internal state of the decoder.
enum State {
kUninitialized, // Initialize() not yet called.
kInitialized, // Initialize() returned true; ready to start decoding.
kDecoding, // DecodeBufferInitial() successful; decoding frames.
kResetting, // Presently resetting.
// Performing resolution change and waiting for image processor to return
// all frames.
kChangingResolution,
// Requested new PictureBuffers via ProvidePictureBuffers(), awaiting
// AssignPictureBuffers().
kAwaitingPictureBuffers,
kError, // Error in kDecoding state.
kDestroying, // Destroying state, when shutting down the decoder.
};
enum BufferId {
kFlushBufferId = -2 // Buffer id for flush buffer, queued by FlushTask().
};
// Auto-destruction reference for BitstreamBuffer, for message-passing from
// Decode() to DecodeTask().
struct BitstreamBufferRef;
// Record for decoded pictures that can be sent to PictureReady.
struct PictureRecord {
PictureRecord(bool cleared, const Picture& picture);
~PictureRecord();
bool cleared; // Whether the texture is cleared and safe to render from.
Picture picture; // The decoded picture.
};
// Record for output buffers.
struct OutputRecord {
OutputRecord();
OutputRecord(OutputRecord&&);
~OutputRecord();
EGLImageKHR egl_image; // EGLImageKHR for the output buffer.
int32_t picture_id; // picture buffer id as returned to PictureReady().
GLuint texture_id;
bool cleared; // Whether the texture is cleared and safe to render
// from. See TextureManager for details.
// Output frame. Used only when OutputMode is IMPORT.
scoped_refptr<VideoFrame> output_frame;
};
//
// Decoding tasks, to be run on decode_thread_.
//
// Task to finish initialization on decoder_thread_.
void InitializeTask(const Config& config,
bool* result,
base::WaitableEvent* done);
bool CheckConfig(const Config& config);
// Enqueue a buffer to decode. This will enqueue a buffer to the
// decoder_input_queue_, then queue a DecodeBufferTask() to actually decode
// the buffer.
void DecodeTask(scoped_refptr<DecoderBuffer> buffer, int32_t bitstream_id);
// Decode from the buffers queued in decoder_input_queue_. Calls
// DecodeBufferInitial() or DecodeBufferContinue() as appropriate.
void DecodeBufferTask();
// Schedule another DecodeBufferTask() if we're behind.
void ScheduleDecodeBufferTaskIfNeeded();
// Return true if we should continue to schedule DecodeBufferTask()s after
// completion. Store the amount of input actually consumed in |endpos|.
bool DecodeBufferInitial(const void* data, size_t size, size_t* endpos);
bool DecodeBufferContinue(const void* data, size_t size);
// Accumulate data for the next frame to decode. May return false in
// non-error conditions; for example when pipeline is full and should be
// retried later.
bool AppendToInputFrame(const void* data, size_t size);
// Flush data for one decoded frame.
bool FlushInputFrame();
// Allocate V4L2 buffers and assign them to |buffers| provided by the client
// via AssignPictureBuffers() on decoder thread.
void AssignPictureBuffersTask(const std::vector<PictureBuffer>& buffers);
// Use buffer backed by |handle| for the OutputRecord associated with
// |picture_buffer_id|. |handle| does not need to be valid if we are in
// ALLOCATE mode and using an image processor.
void ImportBufferForPictureTask(int32_t picture_buffer_id,
gfx::NativePixmapHandle handle);
// Check |handle| is valid in import mode, besides ImportBufferForPicture.
void ImportBufferForPictureForImportTask(int32_t picture_buffer_id,
VideoPixelFormat pixel_format,
gfx::NativePixmapHandle handle);
// Create an EGLImage on |egl_device| for the buffer associated with V4L2
// |buffer_index| and |picture_buffer_id|, backed by |handle|.
// The buffer should be bound to |texture_id| and is of format described by
// |fourcc|. |visible_size| is the size in pixels that the EGL device will be
// able to see.
void CreateEGLImageFor(scoped_refptr<V4L2Device> egl_device,
size_t buffer_index,
int32_t picture_buffer_id,
gfx::NativePixmapHandle handle,
GLuint texture_id,
const gfx::Size& visible_size,
const Fourcc fourcc);
// Take the EGLImage |egl_image|, created for |picture_buffer_id|, and use it
// for OutputRecord at |buffer_index|.
void AssignEGLImage(size_t buffer_index,
int32_t picture_buffer_id,
EGLImageKHR egl_image);
// Service I/O on the V4L2 devices. This task should only be scheduled from
// DevicePollTask(). If |event_pending| is true, one or more events
// on file descriptor are pending.
void ServiceDeviceTask(bool event_pending);
// Release buffers awaiting for their fence to be signaled.
void CheckGLFences();
// Handle the various device queues.
void Enqueue();
void Dequeue();
// Dequeue one input buffer. Return true if success.
bool DequeueInputBuffer();
// Dequeue one output buffer. Return true if success.
bool DequeueOutputBuffer();
// Return true if there is a resolution change event pending.
bool DequeueResolutionChangeEvent();
// Enqueue a buffer on the corresponding queue.
bool EnqueueInputRecord(V4L2WritableBufferRef buffer);
bool EnqueueOutputRecord(V4L2WritableBufferRef buffer);
// Task to flag the specified picture buffer for reuse, executed on the
// decoder_thread_. The picture buffer can only be reused after the specified
// fence has been signaled.
void ReusePictureBufferTask(int32_t picture_buffer_id,
std::unique_ptr<gl::GLFenceEGL> egl_fence);
// Flush() task. Child thread should not submit any more buffers until it
// receives the NotifyFlushDone callback. This task will schedule an empty
// BitstreamBufferRef (with input_id == kFlushBufferId) to perform the flush.
void FlushTask();
// Notify the client of a flush completion, if required. This should be
// called any time a relevant queue could potentially be emptied: see
// function definition.
void NotifyFlushDoneIfNeeded();
// Notify the client of a flush completion.
void NotifyFlushDone();
// Returns true if VIDIOC_DECODER_CMD is supported.
bool IsDecoderCmdSupported();
// Send V4L2_DEC_CMD_STOP to the driver. Return true if success.
bool SendDecoderCmdStop();
// Reset() task. Drop all input buffers. If V4L2VDA is not doing resolution
// change or waiting picture buffers, call FinishReset.
void ResetTask();
// This will schedule a ResetDoneTask() that will send the NotifyResetDone
// callback, then set the decoder state to kResetting so that all intervening
// tasks will drain.
void FinishReset();
void ResetDoneTask();
// Device destruction task.
void DestroyTask();
// Start |device_poll_thread_|.
bool StartDevicePoll();
// Stop |device_poll_thread_|.
bool StopDevicePoll();
bool StopInputStream();
bool StopOutputStream();
void StartResolutionChange();
void FinishResolutionChange();
// Try to get output format and visible size, detected after parsing the
// beginning of the stream. Sets |again| to true if more parsing is needed.
// |visible_size| could be nullptr and ignored.
bool GetFormatInfo(struct v4l2_format* format,
gfx::Size* visible_size,
bool* again);
// Create output buffers for the given |format| and |visible_size|.
bool CreateBuffersForFormat(const struct v4l2_format& format,
const gfx::Size& visible_size);
// Try to get |visible_size|. Return visible size, or, if querying it is not
// supported or produces invalid size, return |coded_size| instead.
gfx::Size GetVisibleSize(const gfx::Size& coded_size);
//
// Device tasks, to be run on device_poll_thread_.
//
// The device task.
void DevicePollTask(bool poll_device);
//
// Safe from any thread.
//
// Check whether a destroy is scheduled.
bool IsDestroyPending();
// Error notification (using PostTask() to child thread, if necessary).
void NotifyError(Error error);
// Set the decoder_state_ to kError and notify the client (if necessary).
void SetErrorState(Error error);
//
// Other utility functions. Called on decoder_thread_, unless
// decoder_thread_ is not yet started, in which case the child thread can call
// these (e.g. in Initialize() or Destroy()).
//
// Create the buffers we need.
bool CreateInputBuffers();
bool CreateOutputBuffers();
// Destroy buffers.
void DestroyInputBuffers();
// In contrast to DestroyInputBuffers, which is called only on destruction,
// we call DestroyOutputBuffers also during playback, on resolution change.
// Even if anything fails along the way, we still want to go on and clean
// up as much as possible, so return false if this happens, so that the
// caller can error out on resolution change.
bool DestroyOutputBuffers();
// Set input and output formats before starting decode.
bool SetupFormats();
// Reset image processor and drop all processing frames.
bool ResetImageProcessor();
bool CreateImageProcessor();
// Send a frame to the image processor to process. The index of decoder
// output buffer is |output_buffer_index| and its id is |bitstream_buffer_id|.
bool ProcessFrame(int32_t bitstream_buffer_id, V4L2ReadableBufferRef buf);
// Send a buffer to the client.
// |buffer_index| is the output buffer index of the buffer to be sent.
// |bitstream_buffer_id| is the bitstream ID from which the buffer results.
// |vda_buffer| is the output VDA buffer containing the decoded frame.
// |frame| is the IP frame that will be sent to the client, if IP is used.
void SendBufferToClient(size_t buffer_index,
int32_t bitstream_buffer_id,
V4L2ReadableBufferRef vda_buffer,
scoped_refptr<VideoFrame> frame = nullptr);
//
// Methods run on child thread.
//
// Send decoded pictures to PictureReady.
void SendPictureReady();
// Callback that indicates a picture has been cleared.
void PictureCleared();
// Image processor returns a processed |frame|. Its id is
// |bitstream_buffer_id| and stored in |output_buffer_index| buffer of
// image processor.
void FrameProcessed(int32_t bitstream_buffer_id,
size_t output_buffer_index,
scoped_refptr<VideoFrame> frame);
// Image processor notifies an error.
void ImageProcessorError();
// TODO(crbug.com/1109312): some pages with lots of small videos are causing
// crashes, so limit the number of simultaneous decoder instances for now.
// |num_instances_| tracks the number of simultaneous decoders.
// |can_use_decoder_| is true iff we haven't reached the maximum number of
// instances at the time this decoder is created.
static constexpr int kMaxNumOfInstances = 10;
static base::AtomicRefCount num_instances_;
const bool can_use_decoder_;
// Our original calling task runner for the child thread.
scoped_refptr<base::SingleThreadTaskRunner> child_task_runner_;
// Task runner Decode() and PictureReady() run on.
scoped_refptr<base::SequencedTaskRunner> decode_task_runner_;
// WeakPtr<> pointing to |this| for use in posting tasks from the decoder or
// device worker threads back to the child thread. Because the worker threads
// are members of this class, any task running on those threads is guaranteed
// that this object is still alive. As a result, tasks posted from the child
// thread to the decoder or device thread should use base::Unretained(this),
// and tasks posted the other way should use |weak_this_|.
base::WeakPtr<V4L2VideoDecodeAccelerator> weak_this_;
// To expose client callbacks from VideoDecodeAccelerator.
// NOTE: all calls to these objects *MUST* be executed on
// child_task_runner_.
std::unique_ptr<base::WeakPtrFactory<Client>> client_ptr_factory_;
base::WeakPtr<Client> client_;
// Callbacks to |decode_client_| must be executed on |decode_task_runner_|.
base::WeakPtr<Client> decode_client_;
//
// Decoder state, owned and operated by decoder_thread_.
// Before decoder_thread_ has started, the decoder state is managed by
// the child (main) thread. After decoder_thread_ has started, the decoder
// thread should be the only one managing these.
//
// This thread services tasks posted from the VDA API entry points by the
// child thread and device service callbacks posted from the device thread.
base::Thread decoder_thread_;
// Decoder state machine state.
State decoder_state_;
// Cancelable callback for running ServiceDeviceTask(). Must only be accessed
// on |decoder_thread_|.
base::CancelableRepeatingCallback<void(bool)> cancelable_service_device_task_;
// Concrete callback from |cancelable_service_device_task_| that can be copied
// on |device_poll_thread_|. This exists because
// CancelableRepeatingCallback::callback() creates a WeakPtr internally, which
// must be created/destroyed from the same thread.
base::RepeatingCallback<void(bool)> cancelable_service_device_task_callback_;
// Waitable event signaled when the decoder is destroying.
base::WaitableEvent destroy_pending_;
Config::OutputMode output_mode_;
// BitstreamBuffer we're presently reading.
std::unique_ptr<BitstreamBufferRef> decoder_current_bitstream_buffer_;
// The V4L2Device this class is operating upon.
scoped_refptr<V4L2Device> device_;
// FlushTask() and ResetTask() should not affect buffers that have been
// queued afterwards. For flushing or resetting the pipeline then, we will
// delay these buffers until after the flush or reset completes.
int decoder_delay_bitstream_buffer_id_;
// We track the number of buffer decode tasks we have scheduled, since each
// task execution should complete one buffer. If we fall behind (due to
// resource backpressure, etc.), we'll have to schedule more to catch up.
int decoder_decode_buffer_tasks_scheduled_;
// Are we flushing?
bool decoder_flushing_;
// True if VIDIOC_DECODER_CMD is supported.
bool decoder_cmd_supported_;
// True if flushing is waiting for last output buffer. After
// VIDIOC_DECODER_CMD is sent to the driver, this flag will be set to true to
// wait for the last output buffer. When this flag is true, flush done will
// not be sent. After an output buffer that has the flag V4L2_BUF_FLAG_LAST is
// received, this is set to false.
bool flush_awaiting_last_output_buffer_;
// Got a reset request while we were performing resolution change or waiting
// picture buffers.
bool reset_pending_;
// Input queue for decoder_thread_: BitstreamBuffers in. Although the elements
// in |decoder_input_queue_| is push()/pop() in queue order, this needs to be
// base::circular_deque because we need to do random access in OnMemoryDump().
base::circular_deque<std::unique_ptr<BitstreamBufferRef>>
decoder_input_queue_;
// Used to split our input frames at the correct boundary. Only really useful
// for H.264 streams.
std::unique_ptr<v4l2_vda_helpers::InputBufferFragmentSplitter>
frame_splitter_;
// Workaround for V4L2VideoDecodeAccelerator. This is created only if some
// workaround is necessary for the V4L2VideoDecodeAccelerator.
std::vector<std::unique_ptr<V4L2StatefulWorkaround>> workarounds_;
// Color space passed in from Initialize().
VideoColorSpace container_color_space_;
//
// Hardware state and associated queues. Since decoder_thread_ services
// the hardware, decoder_thread_ owns these too.
// output_buffer_map_ is an exception during the buffer (re)allocation
// sequence, when the decoder_thread_ is blocked briefly while the Child
// thread manipulates them.
//
absl::optional<V4L2WritableBufferRef> current_input_buffer_;
scoped_refptr<V4L2Queue> input_queue_;
scoped_refptr<V4L2Queue> output_queue_;
// Input buffers ready to be queued.
base::queue<V4L2WritableBufferRef> input_ready_queue_;
// Buffers that have been allocated but are awaiting an ImportBuffer
// or AssignEGLImage event.
std::map<int32_t, V4L2WritableBufferRef> output_wait_map_;
// Bitstream IDs and VDA buffers currently being processed by the IP.
std::queue<std::pair<int32_t, V4L2ReadableBufferRef>> buffers_at_ip_;
// Keeps decoded buffers out of the free list until the client returns them.
// First element is the VDA buffer, second is the (optional) IP buffer.
std::map<int32_t, std::pair<V4L2ReadableBufferRef, scoped_refptr<VideoFrame>>>
buffers_at_client_;
// Queue of buffers that have been returned by the client, but which fence
// hasn't been signaled yet. Keeps both the VDA and (optional) IP buffer.
std::queue<
std::pair<std::unique_ptr<gl::GLFenceEGL>,
std::pair<V4L2ReadableBufferRef, scoped_refptr<VideoFrame>>>>
buffers_awaiting_fence_;
// Mapping of int index to output buffer record.
std::vector<OutputRecord> output_buffer_map_;
// Required size of DPB for decoding.
int output_dpb_size_;
// Pictures that are ready but not sent to PictureReady yet.
base::queue<PictureRecord> pending_picture_ready_;
// The number of pictures that are sent to PictureReady and will be cleared.
int picture_clearing_count_;
// Output picture coded size.
gfx::Size coded_size_;
// Output picture visible size.
gfx::Size visible_size_;
//
// The device polling thread handles notifications of V4L2 device changes.
//
// The thread.
base::Thread device_poll_thread_;
//
// Other state, held by the child (main) thread.
//
// EGL state
EGLDisplay egl_display_;
// Callback to get current GLContext.
GetGLContextCallback get_gl_context_cb_;
// Callback to set the correct gl context.
MakeGLContextCurrentCallback make_context_current_cb_;
// Chosen input format for the video profile we are decoding from.
uint32_t input_format_fourcc_;
// Chosen output format.
absl::optional<Fourcc> output_format_fourcc_;
// Image processor device, if one is in use.
scoped_refptr<V4L2Device> image_processor_device_;
// Image processor. Accessed on |decoder_thread_|.
std::unique_ptr<ImageProcessor> image_processor_;
// The format of EGLImage.
absl::optional<Fourcc> egl_image_format_fourcc_;
// The logical dimensions of EGLImage buffer in pixels.
gfx::Size egl_image_size_;
// Input format V4L2 fourccs this class supports.
static const uint32_t supported_input_fourccs_[];
// The WeakPtrFactory for |weak_this_|.
base::WeakPtrFactory<V4L2VideoDecodeAccelerator> weak_this_factory_;
};
} // namespace media
#endif // MEDIA_GPU_V4L2_V4L2_VIDEO_DECODE_ACCELERATOR_H_