blob: 8f9b00f7e5188cc2b2ff35aedb32e89ed32dc2c7 [file] [log] [blame]
// Copyright 2020 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.
#include "media/gpu/test/video_encoder/video_encoder.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
#include "media/base/video_bitrate_allocation.h"
#include "media/gpu/macros.h"
#include "media/gpu/test/bitstream_helpers.h"
#include "media/gpu/test/video.h"
#include "media/gpu/test/video_encoder/video_encoder_client.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace test {
namespace {
// Get the name of the specified video encoder |event|.
const char* EventName(VideoEncoder::EncoderEvent event) {
switch (event) {
case VideoEncoder::EncoderEvent::kInitialized:
return "Initialized";
case VideoEncoder::EncoderEvent::kFrameReleased:
return "FrameReleased";
case VideoEncoder::EncoderEvent::kBitstreamReady:
return "BitstreamReady";
case VideoEncoder::EncoderEvent::kFlushing:
return "Flushing";
case VideoEncoder::EncoderEvent::kFlushDone:
return "FlushDone";
case VideoEncoder::EncoderEvent::kKeyFrame:
return "KeyFrame";
default:
return "Unknown";
}
}
// Default timeout used when waiting for events.
constexpr base::TimeDelta kDefaultEventWaitTimeout = base::Seconds(30);
// Default initial size used for |video_encoder_events_|.
constexpr size_t kDefaultEventListSize = 512;
constexpr std::pair<VideoEncoder::EncoderEvent, size_t> kInvalidEncodeUntil{
VideoEncoder::kNumEvents, std::numeric_limits<size_t>::max()};
} // namespace
// static
std::unique_ptr<VideoEncoder> VideoEncoder::Create(
const VideoEncoderClientConfig& config,
gpu::GpuMemoryBufferFactory* const gpu_memory_buffer_factory,
std::vector<std::unique_ptr<BitstreamProcessor>> bitstream_processors) {
auto video_encoder = base::WrapUnique(new VideoEncoder());
if (!video_encoder->CreateEncoderClient(config, gpu_memory_buffer_factory,
std::move(bitstream_processors))) {
return nullptr;
}
return video_encoder;
}
VideoEncoder::VideoEncoder()
: event_timeout_(kDefaultEventWaitTimeout),
video_encoder_event_counts_{},
next_unprocessed_event_(0),
encode_until_(kInvalidEncodeUntil) {
video_encoder_events_.reserve(kDefaultEventListSize);
}
VideoEncoder::~VideoEncoder() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOGF(4);
encoder_client_.reset();
}
bool VideoEncoder::CreateEncoderClient(
const VideoEncoderClientConfig& config,
gpu::GpuMemoryBufferFactory* const gpu_memory_buffer_factory,
std::vector<std::unique_ptr<BitstreamProcessor>> bitstream_processors) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(video_encoder_state_.load(), EncoderState::kUninitialized);
DVLOGF(4);
// base::Unretained is safe here as we will never receive callbacks after
// destroying the video encoder, since the video encoder client will be
// destroyed first.
EventCallback event_cb =
base::BindRepeating(&VideoEncoder::NotifyEvent, base::Unretained(this));
encoder_client_ =
VideoEncoderClient::Create(event_cb, std::move(bitstream_processors),
gpu_memory_buffer_factory, config);
if (!encoder_client_) {
VLOGF(1) << "Failed to create video encoder client";
return false;
}
return true;
}
void VideoEncoder::SetEventWaitTimeout(base::TimeDelta timeout) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOGF(4);
event_timeout_ = timeout;
}
bool VideoEncoder::Initialize(const Video* video) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(video_encoder_state_ == EncoderState::kUninitialized ||
video_encoder_state_ == EncoderState::kIdle);
DCHECK(video);
DVLOGF(4);
if (!encoder_client_->Initialize(video))
return false;
// Wait until the video encoder is initialized.
if (!WaitForEvent(EncoderEvent::kInitialized)) {
LOG(ERROR) << "Timeout while initializing video encode accelerator";
return false;
}
video_ = video;
video_encoder_state_ = EncoderState::kIdle;
return true;
}
void VideoEncoder::Encode() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(video_encoder_state_.load(), EncoderState::kIdle);
DVLOGF(4);
// Encode until the end of the video.
EncodeUntil(EncoderEvent::kNumEvents, std::numeric_limits<size_t>::max());
}
void VideoEncoder::EncodeUntil(EncoderEvent event, size_t event_count) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(video_encoder_state_.load(), EncoderState::kIdle);
DCHECK(encode_until_ == kInvalidEncodeUntil);
DCHECK(video_);
DVLOGF(4);
// Start encoding the video.
encode_until_ = std::make_pair(event, event_count);
video_encoder_state_ = EncoderState::kEncoding;
encoder_client_->Encode();
}
void VideoEncoder::Flush() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOGF(4);
encoder_client_->Flush();
}
void VideoEncoder::UpdateBitrate(const VideoBitrateAllocation& bitrate,
uint32_t framerate) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOGF(4);
encoder_client_->UpdateBitrate(bitrate, framerate);
}
void VideoEncoder::ForceKeyFrame() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOGF(4);
encoder_client_->ForceKeyFrame();
}
VideoEncoder::EncoderState VideoEncoder::GetState() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return video_encoder_state_;
}
bool VideoEncoder::WaitForEvent(EncoderEvent event, size_t times) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOGF(4) << "Event ID: " << EventName(event);
if (times == 0)
return true;
base::TimeDelta time_waiting;
base::AutoLock auto_lock(event_lock_);
while (true) {
// Go through the list of events since last wait, looking for the event
// we're interested in.
while (next_unprocessed_event_ < video_encoder_events_.size()) {
if (video_encoder_events_[next_unprocessed_event_++] == event) {
if (--times == 0)
return true;
}
}
// Check whether we've exceeded the maximum time we're allowed to wait.
if (time_waiting >= event_timeout_) {
LOG(ERROR) << "Timeout while waiting for '" << EventName(event)
<< "' event";
return false;
}
const base::TimeTicks start_time = base::TimeTicks::Now();
event_cv_.TimedWait(event_timeout_ - time_waiting);
time_waiting += base::TimeTicks::Now() - start_time;
}
}
bool VideoEncoder::WaitUntilIdle() {
base::TimeDelta time_waiting;
base::AutoLock auto_lock(event_lock_);
while (true) {
if (video_encoder_state_.load() == EncoderState::kIdle)
return true;
// Check whether we've exceeded the maximum time we're allowed to wait.
if (time_waiting >= event_timeout_) {
LOG(ERROR) << "Timeout while waiting for EncodeUntil complete";
return false;
}
const base::TimeTicks start_time = base::TimeTicks::Now();
event_cv_.TimedWait(event_timeout_ - time_waiting);
time_waiting += base::TimeTicks::Now() - start_time;
}
}
bool VideoEncoder::WaitForFlushDone() {
return WaitForEvent(EncoderEvent::kFlushDone);
}
bool VideoEncoder::WaitForFrameReleased(size_t times) {
return WaitForEvent(EncoderEvent::kFrameReleased, times);
}
size_t VideoEncoder::GetEventCount(EncoderEvent event) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::AutoLock auto_lock(event_lock_);
return video_encoder_event_counts_[event];
}
bool VideoEncoder::WaitForBitstreamProcessors() {
return !encoder_client_ || encoder_client_->WaitForBitstreamProcessors();
}
VideoEncoderStats VideoEncoder::GetStats() const {
return !encoder_client_ ? VideoEncoderStats() : encoder_client_->GetStats();
}
void VideoEncoder::ResetStats() {
if (encoder_client_)
encoder_client_->ResetStats();
}
size_t VideoEncoder::GetFlushDoneCount() const {
return GetEventCount(EncoderEvent::kFlushDone);
}
size_t VideoEncoder::GetFrameReleasedCount() const {
return GetEventCount(EncoderEvent::kFrameReleased);
}
bool VideoEncoder::NotifyEvent(EncoderEvent event) {
base::AutoLock auto_lock(event_lock_);
if (event == EncoderEvent::kFlushDone) {
video_encoder_state_ = EncoderState::kIdle;
}
video_encoder_events_.push_back(event);
video_encoder_event_counts_[event]++;
bool should_continue_encoding = true;
// Check whether video encoding should be paused after this event.
if (encode_until_.first == event &&
encode_until_.second == video_encoder_event_counts_[event]) {
video_encoder_state_ = EncoderState::kIdle;
encode_until_ = kInvalidEncodeUntil;
should_continue_encoding = false;
}
event_cv_.Signal();
return should_continue_encoding;
}
} // namespace test
} // namespace media