910e62b5创建于 1月15日历史提交
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/gpu/test/video_player/decoder_wrapper.h"

#include <string>
#include <utility>

#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/single_thread_task_runner_thread_mode.h"
#include "base/task/thread_pool.h"
#include "build/build_config.h"
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "media/base/media_switches.h"
#include "media/base/media_util.h"
#include "media/base/waiting.h"
#include "media/gpu/macros.h"
#include "media/gpu/test/video_bitstream.h"
#include "media/gpu/test/video_frame_helpers.h"
#include "media/gpu/test/video_player/frame_renderer_dummy.h"
#include "media/gpu/test/video_player/mappable_video_frame_converter.h"
#include "media/gpu/test/video_player/test_vda_video_decoder.h"
#include "media/gpu/test/video_test_helpers.h"
#include "media/media_buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION)
#include "media/gpu/chromeos/platform_video_frame_pool.h"
#include "media/gpu/chromeos/video_decoder_pipeline.h"
#endif  // BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION)

namespace media {
namespace test {

namespace {
// Callbacks can be called from any thread, but WeakPtrs are not thread-safe.
// This helper thunk wraps a WeakPtr into an 'Optional' value, so the WeakPtr is
// only dereferenced after rescheduling the task on the specified task runner.
template <typename F, typename... Args>
void CallbackThunk(std::optional<base::WeakPtr<DecoderWrapper>> decoder_client,
                   scoped_refptr<base::SequencedTaskRunner> task_runner,
                   F f,
                   Args... args) {
  DCHECK(decoder_client);
  task_runner->PostTask(FROM_HERE, base::BindOnce(f, *decoder_client, args...));
}
}  // namespace

DecoderWrapper::DecoderWrapper(
    const DecoderListener::EventCallback& event_cb,
    std::unique_ptr<FrameRendererDummy> renderer,
    std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
    const DecoderWrapperConfig& config)
    : event_cb_(event_cb),
      frame_renderer_(std::move(renderer)),
      frame_processors_(std::move(frame_processors)),
      decoder_wrapper_config_(config),
      worker_task_runner_(base::ThreadPool::CreateSingleThreadTaskRunner(
          {base::TaskPriority::USER_BLOCKING, base::MayBlock(),
           base::WithBaseSyncPrimitives()},
          base::SingleThreadTaskRunnerThreadMode::DEDICATED)),
      state_(DecoderWrapperState::kUninitialized) {
  DCHECK(event_cb_);
  DCHECK(frame_renderer_);
  DETACH_FROM_SEQUENCE(worker_sequence_checker_);

  weak_this_ = weak_this_factory_.GetWeakPtr();
}

DecoderWrapper::~DecoderWrapper() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);

  base::WaitableEvent done;
  worker_task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&DecoderWrapper::DestroyDecoderTask, weak_this_, &done));
  done.Wait();

  // Wait until the renderer and frame processors are done before destroying
  // them. This needs to be done after destroying the decoder so no new frames
  // will be queued while waiting.
  WaitForRenderer();
  WaitForFrameProcessors();
  frame_renderer_ = nullptr;
  frame_processors_.clear();
}

// static
std::unique_ptr<DecoderWrapper> DecoderWrapper::Create(
    const DecoderListener::EventCallback& event_cb,
    std::unique_ptr<FrameRendererDummy> frame_renderer,
    std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
    const DecoderWrapperConfig& config) {
  auto wrapper =
      base::WrapUnique(new DecoderWrapper(event_cb, std::move(frame_renderer),
                                          std::move(frame_processors), config));
  wrapper->CreateDecoder();
  return wrapper;
}

void DecoderWrapper::CreateDecoder() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);

  base::WaitableEvent done;
  worker_task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&DecoderWrapper::CreateDecoderTask, weak_this_, &done));
  done.Wait();
}

bool DecoderWrapper::WaitForFrameProcessors() {
  bool success = true;
  for (auto& frame_processor : frame_processors_)
    success &= frame_processor->WaitUntilDone();
  return success;
}

void DecoderWrapper::WaitForRenderer() {
  ASSERT_TRUE(frame_renderer_);
  frame_renderer_->WaitUntilRenderingDone();
}

void DecoderWrapper::Initialize(const VideoBitstream* video) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);
  DCHECK(video);

  base::WaitableEvent done;
  worker_task_runner_->PostTask(
      FROM_HERE, base::BindOnce(&DecoderWrapper::InitializeTask, weak_this_,
                                video, &done));
  done.Wait();
}

void DecoderWrapper::Play() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);

  worker_task_runner_->PostTask(
      FROM_HERE, base::BindOnce(&DecoderWrapper::PlayTask, weak_this_));
}

void DecoderWrapper::Flush() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);

  worker_task_runner_->PostTask(
      FROM_HERE, base::BindOnce(&DecoderWrapper::FlushTask, weak_this_));
}

void DecoderWrapper::Reset() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(parent_sequence_checker_);

  worker_task_runner_->PostTask(
      FROM_HERE, base::BindOnce(&DecoderWrapper::ResetTask, weak_this_));
}

void DecoderWrapper::CreateDecoderTask(base::WaitableEvent* done) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);
  DCHECK_EQ(state_, DecoderWrapperState::kUninitialized);
  ASSERT_TRUE(!decoder_) << "Can't create decoder: already created";

  switch (decoder_wrapper_config_.implementation) {
    case DecoderImplementation::kVD:
#if BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION)
      decoder_ = VideoDecoderPipeline::CreateForTesting(
          base::SingleThreadTaskRunner::GetCurrentDefault(),
          MappableVideoFrameConverter::CreateForTesting(),
          std::make_unique<NullMediaLog>(),
          decoder_wrapper_config_.ignore_resolution_changes_to_smaller_vp9);
#endif  // BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION)
      break;
    case DecoderImplementation::kVDA:
    case DecoderImplementation::kVDVDA:
      // The video decoder client expects decoders to use the VD interface. We
      // can use the TestVDAVideoDecoder wrapper here to test VDA-based video
      // decoders.
      decoder_ = std::make_unique<TestVDAVideoDecoder>(
          decoder_wrapper_config_.implementation ==
              DecoderImplementation::kVDVDA,
          // base::Unretained(this) is safe because |decoder_| is owned by
          // |*this|. The lifetime of |decoder_| must be shorter than |*this|.
          base::BindRepeating(&DecoderWrapper::OnResolutionChangedTask,
                              base::Unretained(this)),
          gfx::ColorSpace(), frame_renderer_.get(),
          decoder_wrapper_config_.linear_output);
      break;
  }

  done->Signal();
}

void DecoderWrapper::InitializeTask(const VideoBitstream* video,
                                    base::WaitableEvent* done) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);
  DCHECK(state_ == DecoderWrapperState::kUninitialized ||
         state_ == DecoderWrapperState::kIdle);
  ASSERT_TRUE(decoder_) << "Can't initialize decoder: not created yet";
  ASSERT_TRUE(video);

  encoded_data_helper_ =
      EncodedDataHelper::Create(video->Data(), video->Codec());

  // (Re-)initialize the decoder.
  VideoDecoderConfig config(
      video->Codec(), video->Profile(),
      VideoDecoderConfig::AlphaMode::kIsOpaque, VideoColorSpace(),
      kNoTransformation, video->Resolution(), gfx::Rect(video->Resolution()),
      video->Resolution(), std::vector<uint8_t>(0), EncryptionScheme());
  input_video_codec_ = video->Codec();
  input_video_profile_ = video->Profile();

  VideoDecoder::InitCB init_cb = base::BindOnce(
      CallbackThunk<decltype(&DecoderWrapper::OnDecoderInitializedTask),
                    DecoderStatus>,
      weak_this_, worker_task_runner_,
      &DecoderWrapper::OnDecoderInitializedTask);
  VideoDecoder::OutputCB output_cb = base::BindRepeating(
      CallbackThunk<decltype(&DecoderWrapper::OnFrameReadyTask),
                    scoped_refptr<VideoFrame>>,
      weak_this_, worker_task_runner_, &DecoderWrapper::OnFrameReadyTask);

  decoder_->Initialize(config, false, nullptr, std::move(init_cb), output_cb,
                       WaitingCB());

  DCHECK_LE(decoder_wrapper_config_.max_outstanding_decode_requests,
            static_cast<size_t>(decoder_->GetMaxDecodeRequests()));

  done->Signal();
}

void DecoderWrapper::DestroyDecoderTask(base::WaitableEvent* done) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);
  LOG_IF(WARNING, 0u != num_outstanding_decode_requests_)
      << "There is/are " << num_outstanding_decode_requests_
      << " Decode() requests that have not been acknowledged by |decoder_|. "
         "This might be fine or a problem depending on whether the calling "
         "test needed to have processed the full input bitstream or not.";
  DVLOGF(4);

  // Invalidate all scheduled tasks.
  weak_this_factory_.InvalidateWeakPtrs();

  decoder_.reset();

  state_ = DecoderWrapperState::kUninitialized;
  done->Signal();
}

void DecoderWrapper::PlayTask() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);
  DVLOGF(4);

  // This method should only be called when the decoder client is idle. If
  // called e.g. while flushing, the behavior is undefined.
  ASSERT_EQ(state_, DecoderWrapperState::kIdle);

  // Start decoding the first fragments. While in the decoding state new
  // fragments will automatically be fed to the decoder, when the decoder
  // notifies us it reached the end of a bitstream buffer.
  state_ = DecoderWrapperState::kDecoding;
  for (size_t i = 0;
       i < decoder_wrapper_config_.max_outstanding_decode_requests; ++i) {
    DecodeNextFragmentTask();
  }
}

void DecoderWrapper::DecodeNextFragmentTask() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);
  DVLOGF(4);

  // Stop decoding fragments if we're no longer in the decoding state.
  if (state_ != DecoderWrapperState::kDecoding)
    return;

  // Flush immediately when we reached the end of the stream. This changes the
  // state to kFlushing so further decode tasks will be aborted.
  if (encoded_data_helper_->ReachEndOfStream()) {
    FlushTask();
    return;
  }

  scoped_refptr<DecoderBuffer> bitstream_buffer =
      encoded_data_helper_->GetNextBuffer();
  if (!bitstream_buffer) {
    LOG(ERROR) << "Failed to get next video stream data";
    return;
  }
  bitstream_buffer->set_timestamp(base::TimeTicks::Now().since_origin());

  bool has_config_info = false;
  if (input_video_codec_ == media::VideoCodec::kH264 ||
      input_video_codec_ == media::VideoCodec::kHEVC) {
    has_config_info = media::test::EncodedDataHelper::HasConfigInfo(
        *bitstream_buffer, input_video_codec_);
  }

  VideoDecoder::DecodeCB decode_cb = base::BindOnce(
      CallbackThunk<decltype(&DecoderWrapper::OnDecodeDoneTask), DecoderStatus>,
      weak_this_, worker_task_runner_, &DecoderWrapper::OnDecodeDoneTask);
  decoder_->Decode(std::move(bitstream_buffer), std::move(decode_cb));

  num_outstanding_decode_requests_++;

  // Throw event when we encounter a config info in a H.264/HEVC stream.
  if (has_config_info)
    FireEvent(DecoderListener::Event::kConfigInfo);
}

void DecoderWrapper::FlushTask() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);
  DVLOGF(4);

  // Changing the state to flushing will abort any pending decodes.
  state_ = DecoderWrapperState::kFlushing;

  VideoDecoder::DecodeCB flush_done_cb = base::BindOnce(
      CallbackThunk<decltype(&DecoderWrapper::OnFlushDoneTask), DecoderStatus>,
      weak_this_, worker_task_runner_, &DecoderWrapper::OnFlushDoneTask);
  decoder_->Decode(DecoderBuffer::CreateEOSBuffer(), std::move(flush_done_cb));

  FireEvent(DecoderListener::Event::kFlushing);
}

void DecoderWrapper::ResetTask() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);
  DVLOGF(4);

  // Changing the state to resetting will abort any pending decodes.
  state_ = DecoderWrapperState::kResetting;
  // TODO(dstaessens@) Allow resetting to any point in the stream.
  encoded_data_helper_->Rewind();

  decoder_->Reset(base::BindOnce(
      CallbackThunk<decltype(&DecoderWrapper::OnResetDoneTask)>, weak_this_,
      worker_task_runner_, &DecoderWrapper::OnResetDoneTask));
  FireEvent(DecoderListener::Event::kResetting);
}

void DecoderWrapper::OnDecoderInitializedTask(DecoderStatus status) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);
  DCHECK(state_ == DecoderWrapperState::kUninitialized ||
         state_ == DecoderWrapperState::kIdle);

  if (!status.is_ok()) {
    state_ = DecoderWrapperState::kUninitialized;
    FireEvent(DecoderListener::Event::kFailure);
  } else {
    state_ = DecoderWrapperState::kIdle;
    FireEvent(DecoderListener::Event::kInitialized);
  }
}

void DecoderWrapper::OnDecodeDoneTask(DecoderStatus status) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);
  DCHECK_NE(DecoderWrapperState::kIdle, state_);
  ASSERT_TRUE(status != DecoderStatus::Codes::kAborted ||
              state_ == DecoderWrapperState::kResetting);
  DVLOGF(4);

  num_outstanding_decode_requests_--;

  base::TimeDelta delay = base::Milliseconds(0);
  // Queue the next fragment to be decoded.
  // TODO(mcasas): Introduce a minor delay here to avoid overrunning the driver;
  // this is a provision for Mediatek devices and for the erroneous behaviour
  // of feeding more encoded chunk here (the driver has likely not seen any
  // encoded chunk enqueued at this point) and not in OnFrameReadyTask as it
  // should (naively moving this task there doesn't work because it prevents the
  // V4L2VideoDecoder backend from polling the device driver).
#if BUILDFLAG(USE_V4L2_CODEC)
  delay = base::Milliseconds(1);
  static bool log_delay_message = true;
  if (log_delay_message) {
    LOG(INFO) << "Using a delay of " << delay
              << " between sending encoded chunks to accommodate "
                 "MTK stateful V4L2 drivers";
    log_delay_message = false;
  }
#endif

  worker_task_runner_->PostDelayedTask(
      FROM_HERE,
      base::BindOnce(&DecoderWrapper::DecodeNextFragmentTask, weak_this_),
      delay);
  FireEvent(DecoderListener::Event::kDecoderBufferAccepted);
}

void DecoderWrapper::OnFrameReadyTask(scoped_refptr<VideoFrame> video_frame) {
  DVLOGF(4) << current_frame_index_;
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);
  DCHECK(video_frame->metadata().power_efficient);

  // Technically VideoDecoder clients shouldn't care about |video_frame|'s'
  // timestamps but we do because we feed non-zeros in DecodeNextFragmentTask().
  // Note that we cannot enforce non-strictly monotonically increasing time
  // deltas because the feeding order might not be the same as the output order
  // (e.g. in H.264 with B-frames the output order would be the "presentation"
  // order and not the "decode" or "transmission" order).
  DCHECK_NE(video_frame->timestamp(), base::TimeDelta());

  frame_renderer_->RenderFrame(video_frame);

  for (auto& frame_processor : frame_processors_)
    frame_processor->ProcessVideoFrame(video_frame, current_frame_index_);

  // Notify the test a frame has been decoded. We should only do this after
  // scheduling the frame to be processed, so calling WaitForFrameProcessors()
  // after receiving this event will always guarantee the frame to be processed.
  FireEvent(DecoderListener::Event::kFrameDecoded);

  current_frame_index_++;
}

void DecoderWrapper::OnFlushDoneTask(DecoderStatus status) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);
  DCHECK_EQ(0u, num_outstanding_decode_requests_);

  // Send an EOS frame to the renderer, so it can reset any internal state it
  // might keep in preparation of the next stream of video frames.
  frame_renderer_->RenderFrame(VideoFrame::CreateEOSFrame());
  state_ = DecoderWrapperState::kIdle;
  FireEvent(DecoderListener::Event::kFlushDone);
}

void DecoderWrapper::OnResetDoneTask() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);
  DCHECK_EQ(0u, num_outstanding_decode_requests_);

  // We finished resetting to a different point in the stream, so we should
  // update the frame index. Currently only resetting to the start of the stream
  // is supported, so we can set the frame index to zero here.
  current_frame_index_ = 0;

  frame_renderer_->RenderFrame(VideoFrame::CreateEOSFrame());
  state_ = DecoderWrapperState::kIdle;
  FireEvent(DecoderListener::Event::kResetDone);
}

bool DecoderWrapper::OnResolutionChangedTask() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);

  return FireEvent(DecoderListener::Event::kNewBuffersRequested);
}

bool DecoderWrapper::FireEvent(DecoderListener::Event event) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(worker_sequence_checker_);

  bool continue_decoding = event_cb_.Run(event);
  if (!continue_decoding) {
    // Changing the state to idle will abort any pending decodes.
    state_ = DecoderWrapperState::kIdle;
  }
  return continue_decoding;
}

}  // namespace test
}  // namespace media