Commit c647a3bd authored by Dale Curtis's avatar Dale Curtis Committed by Commit Bot

Implement VEA::Flush() for VTVideoEncodeAccelerator.

This is required for WebCodecs and others to actually be able to
get the last few frames out of the encoder.

Note: A followup CL will actually enable hardware encoding on
macOS once MojoVEA::Flush() support has landed.

R=eugene

Bug: 1110279
Test: Layout tests now pass.
Change-Id: I9e1c7fa94e69c3214dcb92ea21157ec4bc267873
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2536353
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
Reviewed-by: default avatarEugene Zemtsov <eugene@chromium.org>
Auto-Submit: Dale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#827541}
parent 84cf3e38
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include "base/logging.h" #include "base/logging.h"
#include "base/mac/mac_logging.h"
#include "base/memory/shared_memory_mapping.h" #include "base/memory/shared_memory_mapping.h"
#include "base/memory/unsafe_shared_memory_region.h" #include "base/memory/unsafe_shared_memory_region.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
...@@ -142,6 +143,9 @@ bool VTVideoEncodeAccelerator::Initialize(const Config& config, ...@@ -142,6 +143,9 @@ bool VTVideoEncodeAccelerator::Initialize(const Config& config,
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(client); DCHECK(client);
// Clients are expected to call Flush() before reinitializing the encoder.
DCHECK_EQ(pending_encodes_, 0);
if (config.input_format != PIXEL_FORMAT_I420 && if (config.input_format != PIXEL_FORMAT_I420 &&
config.input_format != PIXEL_FORMAT_NV12) { config.input_format != PIXEL_FORMAT_NV12) {
DLOG(ERROR) << "Input format not supported= " DLOG(ERROR) << "Input format not supported= "
...@@ -254,6 +258,19 @@ void VTVideoEncodeAccelerator::Destroy() { ...@@ -254,6 +258,19 @@ void VTVideoEncodeAccelerator::Destroy() {
delete this; delete this;
} }
void VTVideoEncodeAccelerator::Flush(FlushCallback flush_callback) {
DCHECK(thread_checker_.CalledOnValidThread());
encoder_thread_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&VTVideoEncodeAccelerator::FlushTask,
base::Unretained(this), std::move(flush_callback)));
}
bool VTVideoEncodeAccelerator::IsFlushSupported() {
return true;
}
void VTVideoEncodeAccelerator::EncodeTask(scoped_refptr<VideoFrame> frame, void VTVideoEncodeAccelerator::EncodeTask(scoped_refptr<VideoFrame> frame,
bool force_keyframe) { bool force_keyframe) {
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
...@@ -284,12 +301,13 @@ void VTVideoEncodeAccelerator::EncodeTask(scoped_refptr<VideoFrame> frame, ...@@ -284,12 +301,13 @@ void VTVideoEncodeAccelerator::EncodeTask(scoped_refptr<VideoFrame> frame,
// We can pass the ownership of |request| to the encode callback if // We can pass the ownership of |request| to the encode callback if
// successful. Otherwise let it fall out of scope. // successful. Otherwise let it fall out of scope.
OSStatus status = VTCompressionSessionEncodeFrame( OSStatus status = VTCompressionSessionEncodeFrame(
compression_session_, pixel_buffer, timestamp_cm, CMTime{0, 0, 0, 0}, compression_session_, pixel_buffer, timestamp_cm, kCMTimeInvalid,
frame_props, reinterpret_cast<void*>(request.get()), nullptr); frame_props, reinterpret_cast<void*>(request.get()), nullptr);
if (status != noErr) { if (status != noErr) {
DLOG(ERROR) << " VTCompressionSessionEncodeFrame failed: " << status; DLOG(ERROR) << " VTCompressionSessionEncodeFrame failed: " << status;
NotifyError(kPlatformFailureError); NotifyError(kPlatformFailureError);
} else { } else {
++pending_encodes_;
CHECK(request.release()); CHECK(request.release());
} }
} }
...@@ -360,8 +378,6 @@ void VTVideoEncodeAccelerator::DestroyTask() { ...@@ -360,8 +378,6 @@ void VTVideoEncodeAccelerator::DestroyTask() {
// Cancel all encoder thread callbacks. // Cancel all encoder thread callbacks.
encoder_task_weak_factory_.InvalidateWeakPtrs(); encoder_task_weak_factory_.InvalidateWeakPtrs();
// This call blocks until all pending frames are flushed out.
DestroyCompressionSession(); DestroyCompressionSession();
} }
...@@ -408,6 +424,9 @@ void VTVideoEncodeAccelerator::CompressionCallbackTask( ...@@ -408,6 +424,9 @@ void VTVideoEncodeAccelerator::CompressionCallbackTask(
std::unique_ptr<EncodeOutput> encode_output) { std::unique_ptr<EncodeOutput> encode_output) {
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
--pending_encodes_;
DCHECK_GE(pending_encodes_, 0);
if (status != noErr) { if (status != noErr) {
DLOG(ERROR) << " encode failed: " << status; DLOG(ERROR) << " encode failed: " << status;
NotifyError(kPlatformFailureError); NotifyError(kPlatformFailureError);
...@@ -440,6 +459,7 @@ void VTVideoEncodeAccelerator::ReturnBitstreamBuffer( ...@@ -440,6 +459,7 @@ void VTVideoEncodeAccelerator::ReturnBitstreamBuffer(
base::BindOnce(&Client::BitstreamBufferReady, client_, buffer_ref->id, base::BindOnce(&Client::BitstreamBufferReady, client_, buffer_ref->id,
BitstreamBufferMetadata( BitstreamBufferMetadata(
0, false, encode_output->capture_timestamp))); 0, false, encode_output->capture_timestamp)));
MaybeRunFlushCallback();
return; return;
} }
...@@ -466,6 +486,7 @@ void VTVideoEncodeAccelerator::ReturnBitstreamBuffer( ...@@ -466,6 +486,7 @@ void VTVideoEncodeAccelerator::ReturnBitstreamBuffer(
&Client::BitstreamBufferReady, client_, buffer_ref->id, &Client::BitstreamBufferReady, client_, buffer_ref->id,
BitstreamBufferMetadata(used_buffer_size, keyframe, BitstreamBufferMetadata(used_buffer_size, keyframe,
encode_output->capture_timestamp))); encode_output->capture_timestamp)));
MaybeRunFlushCallback();
} }
bool VTVideoEncodeAccelerator::ResetCompressionSession() { bool VTVideoEncodeAccelerator::ResetCompressionSession() {
...@@ -557,4 +578,48 @@ void VTVideoEncodeAccelerator::DestroyCompressionSession() { ...@@ -557,4 +578,48 @@ void VTVideoEncodeAccelerator::DestroyCompressionSession() {
} }
} }
void VTVideoEncodeAccelerator::FlushTask(FlushCallback flush_callback) {
DVLOG(3) << __func__;
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
DCHECK(flush_callback);
if (!compression_session_) {
client_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(flush_callback), false));
return;
}
// Even though this will block until all frames are returned, the frames will
// be posted to the current task runner, so we can't run the flush callback
// at this time.
OSStatus status =
VTCompressionSessionCompleteFrames(compression_session_, kCMTimeInvalid);
if (status != noErr) {
OSSTATUS_DLOG(ERROR, status)
<< " VTCompressionSessionCompleteFrames failed: " << status;
client_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(flush_callback), /*success=*/false));
return;
}
pending_flush_cb_ = std::move(flush_callback);
MaybeRunFlushCallback();
}
void VTVideoEncodeAccelerator::MaybeRunFlushCallback() {
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
if (!pending_flush_cb_)
return;
if (pending_encodes_ || !encoder_output_queue_.empty())
return;
client_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(pending_flush_cb_), /*success=*/true));
}
} // namespace media } // namespace media
...@@ -38,6 +38,8 @@ class MEDIA_GPU_EXPORT VTVideoEncodeAccelerator ...@@ -38,6 +38,8 @@ class MEDIA_GPU_EXPORT VTVideoEncodeAccelerator
void RequestEncodingParametersChange(uint32_t bitrate, void RequestEncodingParametersChange(uint32_t bitrate,
uint32_t framerate) override; uint32_t framerate) override;
void Destroy() override; void Destroy() override;
void Flush(FlushCallback flush_callback) override;
bool IsFlushSupported() override;
private: private:
// Holds the associated data of a video frame being processed. // Holds the associated data of a video frame being processed.
...@@ -93,6 +95,11 @@ class MEDIA_GPU_EXPORT VTVideoEncodeAccelerator ...@@ -93,6 +95,11 @@ class MEDIA_GPU_EXPORT VTVideoEncodeAccelerator
// encoding work). // encoding work).
void DestroyCompressionSession(); void DestroyCompressionSession();
// Flushes the encoder. The flush callback won't be run until all pending
// encodes have been completed.
void FlushTask(FlushCallback flush_callback);
void MaybeRunFlushCallback();
base::ScopedCFTypeRef<VTCompressionSessionRef> compression_session_; base::ScopedCFTypeRef<VTCompressionSessionRef> compression_session_;
gfx::Size input_visible_size_; gfx::Size input_visible_size_;
...@@ -131,6 +138,11 @@ class MEDIA_GPU_EXPORT VTVideoEncodeAccelerator ...@@ -131,6 +138,11 @@ class MEDIA_GPU_EXPORT VTVideoEncodeAccelerator
base::Thread encoder_thread_; base::Thread encoder_thread_;
scoped_refptr<base::SingleThreadTaskRunner> encoder_thread_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> encoder_thread_task_runner_;
// Tracking information for ensuring flushes aren't completed until all
// pending encodes have been returned.
int pending_encodes_ = 0;
FlushCallback pending_flush_cb_;
// Declared last to ensure that all weak pointers are invalidated before // Declared last to ensure that all weak pointers are invalidated before
// other destructors run. // other destructors run.
base::WeakPtr<VTVideoEncodeAccelerator> encoder_weak_ptr_; base::WeakPtr<VTVideoEncodeAccelerator> encoder_weak_ptr_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment