diff --git a/media/gpu/v4l2/v4l2_video_decoder.cc b/media/gpu/v4l2/v4l2_video_decoder.cc index 34c2fc6846380..0f95090cd4241 100644 --- a/media/gpu/v4l2/v4l2_video_decoder.cc +++ b/media/gpu/v4l2/v4l2_video_decoder.cc @@ -83,6 +83,7 @@ V4L2VideoDecoder::V4L2VideoDecoder( base::WeakPtr client, scoped_refptr 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_); @@ -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, @@ -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; @@ -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> 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)) { @@ -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; @@ -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( - 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( - 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 formats = device_->EnumerateSupportedPixelformats( @@ -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); @@ -459,15 +427,6 @@ void V4L2VideoDecoder::Decode(scoped_refptr 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); @@ -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(); @@ -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(); @@ -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; diff --git a/media/gpu/v4l2/v4l2_video_decoder.h b/media/gpu/v4l2/v4l2_video_decoder.h index 094465cd09475..37d12910c221e 100644 --- a/media/gpu/v4l2/v4l2_video_decoder.h +++ b/media/gpu/v4l2/v4l2_video_decoder.h @@ -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, @@ -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 @@ -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. @@ -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 input_queue_; scoped_refptr output_queue_;