Skip to content

Commit

Permalink
Revert "media/gpu/v4l2vd: Defer decoder hardware initialization"
Browse files Browse the repository at this point in the history
This reverts commit 926103b.

Reason for revert: break video_decode_accelerator_tests

Original change's description:
> media/gpu/v4l2vd: Defer decoder hardware initialization
>
> In order to preserve hardware decode contexts the
> context should not be held until it is certain that
> this decoder will be used. This is done by splitting
> the backend initialization out of Initialize(), into
> InitializeBackend(), where the context is created.
>
> BUG=b:180448929
> TEST=./video_decode_accelerator_tests --use_vd on trogdor and kukui
>
> Change-Id: I7b3e13357def06c91f44827cfadd0edfbc7b9909
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2713053
> Reviewed-by: Andres Calderon Jaramillo <andrescj@chromium.org>
> Reviewed-by: Alexandre Courbot <acourbot@chromium.org>
> Commit-Queue: Fritz Koenig <frkoenig@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#858351}

TBR=frkoenig@chromium.org

Bug: b:180448929
Change-Id: I22653c238f79464852efe23739a3be7bf98b0afd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2726019
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Commit-Queue: Hirokazu Honda <hiroh@chromium.org>
Commit-Queue: David Staessens <dstaessens@chromium.org>
Auto-Submit: Hirokazu Honda <hiroh@chromium.org>
Reviewed-by: David Staessens <dstaessens@chromium.org>
Cr-Commit-Position: refs/heads/master@{#858487}
  • Loading branch information
Hirokazu Honda authored and Chromium LUCI CQ committed Mar 1, 2021
1 parent 9f5f917 commit f0702f0
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 114 deletions.
149 changes: 49 additions & 100 deletions media/gpu/v4l2/v4l2_video_decoder.cc
Expand Up @@ -83,6 +83,7 @@ V4L2VideoDecoder::V4L2VideoDecoder(
base::WeakPtr<DecoderInterface::Client> client,
scoped_refptr<V4L2Device> device)
: DecoderInterface(std::move(decoder_task_runner), std::move(client)),
can_use_decoder_(num_instances_.Increment() < kMaxNumOfInstances),
device_(std::move(device)),
weak_this_factory_(this) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
Expand Down Expand Up @@ -113,9 +114,7 @@ V4L2VideoDecoder::~V4L2VideoDecoder() {
}

weak_this_factory_.InvalidateWeakPtrs();

if (can_use_decoder_)
num_instances_.Decrement();
num_instances_.Decrement();
}

void V4L2VideoDecoder::Initialize(const VideoDecoderConfig& config,
Expand All @@ -125,21 +124,24 @@ void V4L2VideoDecoder::Initialize(const VideoDecoderConfig& config,
const WaitingCB& /*waiting_cb*/) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(config.IsValidConfig());
DCHECK(state_ == State::kUninitialized || state_ == State::kInitialized ||
state_ == State::kDecoding);
DCHECK(state_ == State::kUninitialized || state_ == State::kDecoding);
DVLOGF(3);

if (!can_use_decoder_) {
VLOGF(1) << "Reached maximum number of decoder instances ("
<< kMaxNumOfInstances << ")";
std::move(init_cb).Run(StatusCode::kDecoderCreationFailed);
return;
}

if (cdm_context || config.is_encrypted()) {
VLOGF(1) << "V4L2 decoder does not support encrypted stream";
std::move(init_cb).Run(StatusCode::kEncryptedContentUnsupported);
return;
}

// Stop and reset the queues if we're currently decoding but want to
// re-initialize the decoder. This is not needed if the decoder is in the
// |kInitialized| state because the queues should still be stopped in that
// case.
if (state_ == State::kDecoding) {
// Reset V4L2 device and queue if reinitializing decoder.
if (state_ != State::kUninitialized) {
if (!StopStreamV4L2Queue(true)) {
std::move(init_cb).Run(StatusCode::kV4l2FailedToStopStreamQueue);
return;
Expand All @@ -150,64 +152,27 @@ void V4L2VideoDecoder::Initialize(const VideoDecoderConfig& config,
input_queue_ = nullptr;
output_queue_ = nullptr;

if (can_use_decoder_) {
num_instances_.Decrement();
can_use_decoder_ = false;
}

continue_change_resolution_cb_.Reset();

device_ = V4L2Device::Create();
if (!device_) {
VLOGF(1) << "Failed to create V4L2 device.";
SetState(State::kError);
std::move(init_cb).Run(StatusCode::kV4l2NoDevice);
return;
}

if (backend_)
backend_ = nullptr;
}

DCHECK(!input_queue_);
DCHECK(!output_queue_);

profile_ = config.profile();
pixel_aspect_ratio_ = config.GetPixelAspectRatio();

if (profile_ == VIDEO_CODEC_PROFILE_UNKNOWN) {
VLOGF(1) << "Unknown profile.";
SetState(State::kError);
std::move(init_cb).Run(StatusCode::kV4l2NoDecoder);
return;
}

// Call init_cb
output_cb_ = std::move(output_cb);
SetState(State::kInitialized);
std::move(init_cb).Run(::media::OkStatus());
}

StatusCode V4L2VideoDecoder::InitializeBackend() {
DVLOGF(3);
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(state_ == State::kInitialized);

can_use_decoder_ = num_instances_.Increment() < kMaxNumOfInstances;
if (!can_use_decoder_) {
VLOGF(1) << "Reached maximum number of decoder instances ("
<< kMaxNumOfInstances << ")";
return StatusCode::kDecoderCreationFailed;
SetState(State::kUninitialized);
}

const VideoCodecProfile profile = config.profile();
constexpr bool kStateful = false;
constexpr bool kStateless = true;
base::Optional<std::pair<bool, uint32_t>> api_and_format;
// Try both kStateful and kStateless APIs via |fourcc| and select the first
// combination where Open()ing the |device_| works.
for (const auto api : {kStateful, kStateless}) {
const auto fourcc =
V4L2Device::VideoCodecProfileToV4L2PixFmt(profile_, api);
const auto fourcc = V4L2Device::VideoCodecProfileToV4L2PixFmt(profile, api);
constexpr uint32_t kInvalidV4L2PixFmt = 0;
if (fourcc == kInvalidV4L2PixFmt ||
!device_->Open(V4L2Device::Type::kDecoder, fourcc)) {
Expand All @@ -218,10 +183,9 @@ StatusCode V4L2VideoDecoder::InitializeBackend() {
}

if (!api_and_format.has_value()) {
num_instances_.Decrement();
can_use_decoder_ = false;
VLOGF(1) << "No V4L2 API found for profile: " << GetProfileName(profile_);
return StatusCode::kV4l2NoDecoder;
VLOGF(1) << "No V4L2 API found for profile: " << GetProfileName(profile);
std::move(init_cb).Run(StatusCode::kV4l2NoDecoder);
return;
}

struct v4l2_capability caps;
Expand All @@ -230,62 +194,71 @@ StatusCode V4L2VideoDecoder::InitializeBackend() {
(caps.capabilities & kCapsRequired) != kCapsRequired) {
VLOGF(1) << "ioctl() failed: VIDIOC_QUERYCAP, "
<< "caps check failed: 0x" << std::hex << caps.capabilities;
return StatusCode::kV4l2FailedFileCapabilitiesCheck;
std::move(init_cb).Run(StatusCode::kV4l2FailedFileCapabilitiesCheck);
return;
}

pixel_aspect_ratio_ = config.GetPixelAspectRatio();

// Create Input/Output V4L2Queue
input_queue_ = device_->GetQueue(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
output_queue_ = device_->GetQueue(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (!input_queue_ || !output_queue_) {
VLOGF(1) << "Failed to create V4L2 queue.";
return StatusCode::kV4l2FailedResourceAllocation;
std::move(init_cb).Run(StatusCode::kV4l2FailedResourceAllocation);
return;
}

const auto preferred_api_and_format = api_and_format.value();
const uint32_t input_format_fourcc = preferred_api_and_format.second;
if (preferred_api_and_format.first == kStateful) {
VLOGF(1) << "Using a stateful API for profile: " << GetProfileName(profile_)
VLOGF(1) << "Using a stateful API for profile: " << GetProfileName(profile)
<< " and fourcc: " << FourccToString(input_format_fourcc);
backend_ = std::make_unique<V4L2StatefulVideoDecoderBackend>(
this, device_, profile_, decoder_task_runner_);
this, device_, profile, decoder_task_runner_);
} else {
DCHECK_EQ(preferred_api_and_format.first, kStateless);
VLOGF(1) << "Using a stateless API for profile: "
<< GetProfileName(profile_)
VLOGF(1) << "Using a stateless API for profile: " << GetProfileName(profile)
<< " and fourcc: " << FourccToString(input_format_fourcc);
backend_ = std::make_unique<V4L2StatelessVideoDecoderBackend>(
this, device_, profile_, decoder_task_runner_);
this, device_, profile, decoder_task_runner_);
}

if (!backend_->Initialize()) {
VLOGF(1) << "Failed to initialize backend.";
return StatusCode::kV4l2FailedResourceAllocation;
std::move(init_cb).Run(StatusCode::kV4l2FailedResourceAllocation);
return;
}

if (!SetupInputFormat(input_format_fourcc)) {
VLOGF(1) << "Failed to setup input format.";
return StatusCode::kV4l2BadFormat;
std::move(init_cb).Run(StatusCode::kV4l2BadFormat);
return;
}

if (input_queue_->AllocateBuffers(kNumInputBuffers, V4L2_MEMORY_MMAP) == 0) {
VLOGF(1) << "Failed to allocate input buffer.";
return StatusCode::kV4l2FailedResourceAllocation;
std::move(init_cb).Run(StatusCode::kV4l2FailedResourceAllocation);
return;
}

// Start streaming input queue and polling. This is required for the stateful
// decoder, and doesn't hurt for the stateless one.
if (!StartStreamV4L2Queue(false)) {
VLOGF(1) << "Failed to start streaming.";
return StatusCode::kV4L2FailedToStartStreamQueue;
std::move(init_cb).Run(StatusCode::kV4L2FailedToStartStreamQueue);
return;
}

// Call init_cb
output_cb_ = output_cb;
SetState(State::kDecoding);
return StatusCode::kOk;
std::move(init_cb).Run(::media::OkStatus());
}

bool V4L2VideoDecoder::SetupInputFormat(uint32_t input_format_fourcc) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK_EQ(state_, State::kInitialized);
DCHECK_EQ(state_, State::kUninitialized);

// Check if the format is supported.
std::vector<uint32_t> formats = device_->EnumerateSupportedPixelformats(
Expand Down Expand Up @@ -421,11 +394,6 @@ void V4L2VideoDecoder::Reset(base::OnceClosure closure) {
// flushed after reset.
continue_change_resolution_cb_.Reset();

if (state_ == State::kInitialized) {
std::move(closure).Run();
return;
}

// Call all pending decode callback.
backend_->ClearPendingRequests(DecodeStatus::ABORTED);

Expand Down Expand Up @@ -459,15 +427,6 @@ void V4L2VideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
return;
}

if (state_ == State::kInitialized) {
const StatusCode status = InitializeBackend();
if (status != StatusCode::kOk) {
SetState(State::kError);
std::move(decode_cb).Run(status);
return;
}
}

const int32_t bitstream_id = bitstream_id_generator_.GetNextBitstreamId();
backend_->EnqueueDecodeTask(std::move(buffer), std::move(decode_cb),
bitstream_id);
Expand Down Expand Up @@ -626,20 +585,16 @@ void V4L2VideoDecoder::ContinueChangeResolution(

void V4L2VideoDecoder::ServiceDeviceTask(bool event) {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DVLOGF(3) << "Number of queued input buffers: "
<< input_queue_->QueuedBuffersCount()
<< ", Number of queued output buffers: "
<< output_queue_->QueuedBuffersCount();

if (input_queue_ && output_queue_) {
DVLOGF(3) << "Number of queued input buffers: "
<< input_queue_->QueuedBuffersCount()
<< ", Number of queued output buffers: "
<< output_queue_->QueuedBuffersCount();
}

if (backend_)
backend_->OnServiceDeviceTask(event);
backend_->OnServiceDeviceTask(event);

// Dequeue V4L2 output buffer first to reduce output latency.
bool success;
while (output_queue_ && output_queue_->QueuedBuffersCount() > 0) {
while (output_queue_->QueuedBuffersCount() > 0) {
V4L2ReadableBufferRef dequeued_buffer;

std::tie(success, dequeued_buffer) = output_queue_->DequeueBuffer();
Expand All @@ -654,7 +609,7 @@ void V4L2VideoDecoder::ServiceDeviceTask(bool event) {
}

// Dequeue V4L2 input buffer.
while (input_queue_ && input_queue_->QueuedBuffersCount() > 0) {
while (input_queue_->QueuedBuffersCount() > 0) {
V4L2ReadableBufferRef dequeued_buffer;

std::tie(success, dequeued_buffer) = input_queue_->DequeueBuffer();
Expand Down Expand Up @@ -716,14 +671,8 @@ void V4L2VideoDecoder::SetState(State new_state) {
// Check if the state transition is valid.
switch (new_state) {
case State::kUninitialized:
VLOGF(1) << "Should not set to kUninitialized.";
new_state = State::kError;
break;

case State::kInitialized:
if ((state_ != State::kUninitialized) && (state_ != State::kDecoding)) {
VLOGF(1) << "Can only transition to kInitialized from kUninitialized "
"or kDecoding";
if (state_ != State::kDecoding) {
VLOGF(1) << "Should not set to kUninitialized.";
new_state = State::kError;
}
break;
Expand Down
16 changes: 2 additions & 14 deletions media/gpu/v4l2/v4l2_video_decoder.h
Expand Up @@ -82,13 +82,9 @@ class MEDIA_GPU_EXPORT V4L2VideoDecoder
~V4L2VideoDecoder() override;

enum class State {
// Initial state. Transitions to |kInitialized| if Initialize() is
// successful,
// Initial state. Transitions to |kDecoding| if Initialize() is successful,
// |kError| otherwise.
kUninitialized,
// Transitions to |kDecoding| when an input buffer has arrived that
// allows creation of hardware contexts. |kError| on error.
kInitialized,
// Transitions to |kFlushing| when flushing or changing resolution,
// |kError| if any unexpected error occurs.
kDecoding,
Expand Down Expand Up @@ -140,10 +136,6 @@ class MEDIA_GPU_EXPORT V4L2VideoDecoder
// Change the state and check the state transition is valid.
void SetState(State new_state);

// Continue backend initialization. Decoder will not take a hardware context
// until InitializeBackend() is called.
StatusCode InitializeBackend();

// Pages with multiple V4L2VideoDecoder instances might run out of memory
// (e.g. b/170870476) or crash (e.g. crbug.com/1109312). To avoid that and
// while the investigation goes on, limit the maximum number of simultaneous
Expand All @@ -152,7 +144,7 @@ class MEDIA_GPU_EXPORT V4L2VideoDecoder
// the maximum number of instances at the time this decoder is created.
static constexpr int kMaxNumOfInstances = 32;
static base::AtomicRefCount num_instances_;
bool can_use_decoder_ = false;
const bool can_use_decoder_;

// The V4L2 backend, i.e. the part of the decoder that sends
// decoding jobs to the kernel.
Expand All @@ -177,10 +169,6 @@ class MEDIA_GPU_EXPORT V4L2VideoDecoder
// Callbacks passed from Initialize().
OutputCB output_cb_;

// Hold onto profile passed in from Initialize() so that
// it is available for InitializeBackend().
VideoCodecProfile profile_ = VIDEO_CODEC_PROFILE_UNKNOWN;

// V4L2 input and output queue.
scoped_refptr<V4L2Queue> input_queue_;
scoped_refptr<V4L2Queue> output_queue_;
Expand Down

0 comments on commit f0702f0

Please sign in to comment.