Commit 2f702ab1 authored by Chunbo Hua's avatar Chunbo Hua Committed by Commit Bot

Enable H.264 hardware MFT encoder on Windows

Currently Microsoft H.264 encoder MFT is always used as WebRTC external
encoder without hardware acceleration. This change enables H.264
hardware MFT encoder on Windows.

Bug: 982799
Change-Id: Ia33812508034daa99dd3dc1fb64b83cb1d7c8465
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1777521
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: default avatarTommi <tommi@chromium.org>
Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#745539}
parent 7a742ea7
......@@ -187,6 +187,7 @@ Chris Tserng <tserng@amazon.com>
Chris Vasselli <clindsay@gmail.com>
Christophe Dumez <ch.dumez@samsung.com>
Christopher Dale <chrelad@gmail.com>
Chunbo Hua <chunbo.hua@intel.com>
Claudio DeSouza <claudiomdsjr@gmail.com>
Clemens Fruhwirth <clemens@endorphin.org>
Clement Scheelfeldt Skau <clementskau@gmail.com>
......
......@@ -40,9 +40,8 @@ const size_t kMaxResolutionWidth = 1920;
const size_t kMaxResolutionHeight = 1088;
const size_t kNumInputBuffers = 3;
// Media Foundation uses 100 nanosecond units for time, see
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms697282(v=vs.85).aspx
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms697282(v=vs.85).aspx.
const size_t kOneMicrosecondInMFSampleTimeUnits = 10;
const size_t kOutputSampleBufferSizeRatio = 4;
constexpr const wchar_t* const kMediaFoundationVideoEncoderDLLs[] = {
L"mf.dll", L"mfplat.dll",
......@@ -108,8 +107,10 @@ struct MediaFoundationVideoEncodeAccelerator::BitstreamBufferRef {
MediaFoundationVideoEncodeAccelerator::MediaFoundationVideoEncodeAccelerator(
bool compatible_with_win7)
: compatible_with_win7_(compatible_with_win7),
input_required_(false),
main_client_task_runner_(base::ThreadTaskRunnerHandle::Get()),
encoder_thread_("MFEncoderThread") {}
encoder_thread_("MFEncoderThread"),
encoder_task_weak_factory_(this) {}
MediaFoundationVideoEncodeAccelerator::
~MediaFoundationVideoEncodeAccelerator() {
......@@ -132,18 +133,20 @@ MediaFoundationVideoEncodeAccelerator::GetSupportedProfiles() {
frame_rate_ = kMaxFrameRateNumerator / kMaxFrameRateDenominator;
input_visible_size_ = gfx::Size(kMaxResolutionWidth, kMaxResolutionHeight);
if (!CreateHardwareEncoderMFT() || !SetEncoderModes() ||
!InitializeInputOutputSamples(H264PROFILE_BASELINE)) {
!InitializeInputOutputParameters(H264PROFILE_BASELINE)) {
ReleaseEncoderResources();
DVLOG(1)
<< "Hardware encode acceleration is not available on this platform.";
return profiles;
}
gfx::Size highest_supported_resolution = input_visible_size_;
for (const auto& resolution : kOptionalMaxResolutions) {
DCHECK_GT(resolution.GetArea(), highest_supported_resolution.GetArea());
if (!IsResolutionSupported(resolution))
if (!IsResolutionSupported(resolution)) {
break;
}
highest_supported_resolution = resolution;
}
ReleaseEncoderResources();
......@@ -200,27 +203,13 @@ bool MediaFoundationVideoEncodeAccelerator::Initialize(const Config& config,
frame_rate_ = kMaxFrameRateNumerator / kMaxFrameRateDenominator;
target_bitrate_ = config.initial_bitrate;
bitstream_buffer_size_ = config.input_visible_size.GetArea();
u_plane_offset_ =
VideoFrame::PlaneSize(PIXEL_FORMAT_I420, VideoFrame::kYPlane,
input_visible_size_)
.GetArea();
v_plane_offset_ = u_plane_offset_ + VideoFrame::PlaneSize(PIXEL_FORMAT_I420,
VideoFrame::kUPlane,
input_visible_size_)
.GetArea();
y_stride_ = VideoFrame::RowBytes(VideoFrame::kYPlane, PIXEL_FORMAT_I420,
input_visible_size_.width());
u_stride_ = VideoFrame::RowBytes(VideoFrame::kUPlane, PIXEL_FORMAT_I420,
input_visible_size_.width());
v_stride_ = VideoFrame::RowBytes(VideoFrame::kVPlane, PIXEL_FORMAT_I420,
input_visible_size_.width());
if (!SetEncoderModes()) {
DLOG(ERROR) << "Failed setting encoder parameters.";
return false;
}
if (!InitializeInputOutputSamples(config.output_profile)) {
if (!InitializeInputOutputParameters(config.output_profile)) {
DLOG(ERROR) << "Failed initializing input-output samples.";
return false;
}
......@@ -232,25 +221,28 @@ bool MediaFoundationVideoEncodeAccelerator::Initialize(const Config& config,
input_sample_ = CreateEmptySampleWithBuffer(
input_stream_info.cbSize
? input_stream_info.cbSize
: VideoFrame::AllocationSize(PIXEL_FORMAT_I420, input_visible_size_),
: VideoFrame::AllocationSize(PIXEL_FORMAT_NV12, input_visible_size_),
input_stream_info.cbAlignment);
MFT_OUTPUT_STREAM_INFO output_stream_info;
hr = encoder_->GetOutputStreamInfo(output_stream_id_, &output_stream_info);
RETURN_ON_HR_FAILURE(hr, "Couldn't get output stream info", false);
output_sample_ = CreateEmptySampleWithBuffer(
output_stream_info.cbSize
? output_stream_info.cbSize
: bitstream_buffer_size_ * kOutputSampleBufferSizeRatio,
output_stream_info.cbAlignment);
hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL);
RETURN_ON_HR_FAILURE(hr, "Couldn't set ProcessMessage", false);
main_client_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&Client::RequireBitstreamBuffers, main_client_,
kNumInputBuffers, input_visible_size_,
bitstream_buffer_size_));
hr = encoder_->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
RETURN_ON_HR_FAILURE(
hr, "Couldn't set ProcessMessage MFT_MESSAGE_COMMAND_FLUSH", false);
hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
RETURN_ON_HR_FAILURE(
hr, "Couldn't set ProcessMessage MFT_MESSAGE_NOTIFY_BEGIN_STREAMING",
false);
hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
RETURN_ON_HR_FAILURE(
hr, "Couldn't set ProcessMessage MFT_MESSAGE_NOTIFY_START_OF_STREAM",
false);
hr = encoder_->QueryInterface(IID_PPV_ARGS(&event_generator_));
RETURN_ON_HR_FAILURE(hr, "Couldn't get event generator", false);
return SUCCEEDED(hr);
}
......@@ -359,8 +351,9 @@ bool MediaFoundationVideoEncodeAccelerator::CreateHardwareEncoderMFT() {
}
}
if (!(session_ = InitializeMediaFoundation()))
if (!(session_ = InitializeMediaFoundation())) {
return false;
}
uint32_t flags = MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_SORTANDFILTER;
MFT_REGISTER_TYPE_INFO input_info;
......@@ -370,31 +363,96 @@ bool MediaFoundationVideoEncodeAccelerator::CreateHardwareEncoderMFT() {
output_info.guidMajorType = MFMediaType_Video;
output_info.guidSubtype = MFVideoFormat_H264;
base::win::ScopedCoMem<CLSID> CLSIDs;
uint32_t count = 0;
HRESULT hr = MFTEnum(MFT_CATEGORY_VIDEO_ENCODER, flags, &input_info,
&output_info, NULL, &CLSIDs, &count);
base::win::ScopedCoMem<IMFActivate*> pp_activate;
HRESULT hr = MFTEnumEx(MFT_CATEGORY_VIDEO_ENCODER, flags, &input_info,
&output_info, &pp_activate, &count);
RETURN_ON_HR_FAILURE(hr, "Couldn't enumerate hardware encoder", false);
RETURN_ON_FAILURE((count > 0), "No HW encoder found", false);
DVLOG(3) << "HW encoder(s) found: " << count;
hr = ::CoCreateInstance(CLSIDs[0], nullptr, CLSCTX_ALL,
IID_PPV_ARGS(&encoder_));
RETURN_ON_FAILURE((count > 0), "No hardware encoder found", false);
DVLOG(3) << "Hardware encoder(s) found: " << count;
// Try to create the encoder with priority according to merit value.
hr = E_FAIL;
for (UINT32 i = 0; i < count; i++) {
if (FAILED(hr)) {
DCHECK(!encoder_);
DCHECK(!activate_);
hr = pp_activate[i]->ActivateObject(IID_PPV_ARGS(&encoder_));
if (encoder_.Get() != nullptr) {
DCHECK(SUCCEEDED(hr));
activate_ = pp_activate[i];
pp_activate[i] = nullptr;
// Print the friendly name.
base::win::ScopedCoMem<WCHAR> friendly_name;
UINT32 name_length;
activate_->GetAllocatedString(MFT_FRIENDLY_NAME_Attribute,
&friendly_name, &name_length);
DVLOG(3) << "Selected hardware encoder's friendly name: "
<< friendly_name;
} else {
DCHECK(FAILED(hr));
// The component that calls ActivateObject is
// responsible for calling ShutdownObject,
// https://docs.microsoft.com/en-us/windows/win32/api/mfobjects/nf-mfobjects-imfactivate-shutdownobject.
pp_activate[i]->ShutdownObject();
}
}
// Release the enumerated instances. According to Windows Dev Center,
// https://docs.microsoft.com/en-us/windows/win32/api/mfapi/nf-mfapi-mftenumex
// The caller must release the pointers.
if (pp_activate[i]) {
pp_activate[i]->Release();
pp_activate[i] = nullptr;
}
}
RETURN_ON_HR_FAILURE(hr, "Couldn't activate hardware encoder", false);
RETURN_ON_FAILURE((encoder_.Get() != nullptr),
"No HW encoder instance created", false);
"No hardware encoder instance created", false);
Microsoft::WRL::ComPtr<IMFAttributes> all_attributes;
hr = encoder_->GetAttributes(&all_attributes);
if (SUCCEEDED(hr)) {
// An asynchronous MFT must support dynamic format changes,
// https://docs.microsoft.com/en-us/windows/win32/medfound/asynchronous-mfts#format-changes.
UINT32 dynamic = FALSE;
hr = all_attributes->GetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, &dynamic);
if (!dynamic) {
DLOG(ERROR) << "Couldn't support dynamic format change.";
return false;
}
// Unlock the selected asynchronous MFTs,
// https://docs.microsoft.com/en-us/windows/win32/medfound/asynchronous-mfts#unlocking-asynchronous-mfts.
UINT32 async = FALSE;
hr = all_attributes->GetUINT32(MF_TRANSFORM_ASYNC, &async);
if (!async) {
DLOG(ERROR) << "MFT encoder is not asynchronous.";
return false;
}
hr = all_attributes->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
RETURN_ON_HR_FAILURE(hr, "Couldn't unlock transform async", false);
}
return true;
}
bool MediaFoundationVideoEncodeAccelerator::InitializeInputOutputSamples(
bool MediaFoundationVideoEncodeAccelerator::InitializeInputOutputParameters(
VideoCodecProfile output_profile) {
DCHECK(main_client_task_runner_->BelongsToCurrentThread());
DCHECK(encoder_);
DWORD input_count = 0;
DWORD output_count = 0;
HRESULT hr = encoder_->GetStreamCount(&input_count, &output_count);
RETURN_ON_HR_FAILURE(hr, "Couldn't get stream count", false);
if (input_count < 1 || output_count < 1) {
LOG(ERROR) << "Stream count too few: input " << input_count << ", output "
DLOG(ERROR) << "Stream count too few: input " << input_count << ", output "
<< output_count;
return false;
}
......@@ -410,13 +468,13 @@ bool MediaFoundationVideoEncodeAccelerator::InitializeInputOutputSamples(
input_stream_id_ = 0;
output_stream_id_ = 0;
} else {
LOG(ERROR) << "Couldn't find stream ids.";
DLOG(ERROR) << "Couldn't find stream ids from hardware encoder.";
return false;
}
// Initialize output parameters.
hr = MFCreateMediaType(&imf_output_media_type_);
RETURN_ON_HR_FAILURE(hr, "Couldn't create media type", false);
RETURN_ON_HR_FAILURE(hr, "Couldn't create output media type", false);
hr = imf_output_media_type_->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
RETURN_ON_HR_FAILURE(hr, "Couldn't set media type", false);
hr = imf_output_media_type_->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
......@@ -442,10 +500,10 @@ bool MediaFoundationVideoEncodeAccelerator::InitializeInputOutputSamples(
// Initialize input parameters.
hr = MFCreateMediaType(&imf_input_media_type_);
RETURN_ON_HR_FAILURE(hr, "Couldn't create media type", false);
RETURN_ON_HR_FAILURE(hr, "Couldn't create input media type", false);
hr = imf_input_media_type_->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
RETURN_ON_HR_FAILURE(hr, "Couldn't set media type", false);
hr = imf_input_media_type_->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YV12);
hr = imf_input_media_type_->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12);
RETURN_ON_HR_FAILURE(hr, "Couldn't set video format", false);
hr = MFSetAttributeRatio(imf_input_media_type_.Get(), MF_MT_FRAME_RATE,
frame_rate_, 1);
......@@ -465,11 +523,11 @@ bool MediaFoundationVideoEncodeAccelerator::InitializeInputOutputSamples(
bool MediaFoundationVideoEncodeAccelerator::SetEncoderModes() {
DCHECK(main_client_task_runner_->BelongsToCurrentThread());
RETURN_ON_FAILURE((encoder_.Get() != nullptr),
"No HW encoder instance created", false);
DCHECK(encoder_);
HRESULT hr = encoder_.As(&codec_api_);
RETURN_ON_HR_FAILURE(hr, "Couldn't get ICodecAPI", false);
VARIANT var;
var.vt = VT_UI4;
var.ulVal = eAVEncCommonRateControlMode_CBR;
......@@ -481,21 +539,37 @@ bool MediaFoundationVideoEncodeAccelerator::SetEncoderModes() {
// setting it on Windows 7 returns error.
RETURN_ON_HR_FAILURE(hr, "Couldn't set CommonRateControlMode", false);
}
if (S_OK ==
codec_api_->IsModifiable(&CODECAPI_AVEncVideoTemporalLayerCount)) {
var.ulVal = 1;
hr = codec_api_->SetValue(&CODECAPI_AVEncVideoTemporalLayerCount, &var);
if (!compatible_with_win7_) {
RETURN_ON_HR_FAILURE(hr, "Couldn't set temporal layer count", false);
}
}
var.ulVal = target_bitrate_;
hr = codec_api_->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var);
if (!compatible_with_win7_) {
RETURN_ON_HR_FAILURE(hr, "Couldn't set bitrate", false);
}
if (S_OK == codec_api_->IsModifiable(&CODECAPI_AVEncAdaptiveMode)) {
var.ulVal = eAVEncAdaptiveMode_Resolution;
hr = codec_api_->SetValue(&CODECAPI_AVEncAdaptiveMode, &var);
if (!compatible_with_win7_) {
RETURN_ON_HR_FAILURE(hr, "Couldn't set FrameRate", false);
RETURN_ON_HR_FAILURE(hr, "Couldn't set adaptive mode", false);
}
}
if (S_OK == codec_api_->IsModifiable(&CODECAPI_AVLowLatencyMode)) {
var.vt = VT_BOOL;
var.boolVal = VARIANT_TRUE;
hr = codec_api_->SetValue(&CODECAPI_AVLowLatencyMode, &var);
if (!compatible_with_win7_) {
RETURN_ON_HR_FAILURE(hr, "Couldn't set LowLatencyMode", false);
RETURN_ON_HR_FAILURE(hr, "Couldn't set low latency mode", false);
}
}
return true;
......@@ -538,27 +612,79 @@ void MediaFoundationVideoEncodeAccelerator::EncodeTask(
DVLOG(3) << __func__;
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
bool input_delivered = false;
if (input_required_) {
// HMFT is waiting for this coming input.
ProcessInput(frame, force_keyframe);
input_delivered = true;
input_required_ = false;
} else {
Microsoft::WRL::ComPtr<IMFMediaEvent> media_event;
HRESULT hr =
event_generator_->GetEvent(MF_EVENT_FLAG_NO_WAIT, &media_event);
if (FAILED(hr)) {
DLOG(WARNING) << "Abandoned input frame for video encoder.";
return;
}
MediaEventType event_type;
hr = media_event->GetType(&event_type);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to get the type of media event.";
return;
}
// Always deliver the current input into HMFT.
if (event_type == METransformNeedInput) {
ProcessInput(frame, force_keyframe);
input_delivered = true;
} else if (event_type == METransformHaveOutput) {
ProcessOutput();
input_delivered =
TryToDeliverInputFrame(std::move(frame), force_keyframe);
}
}
if (!input_delivered) {
DLOG(ERROR) << "Failed to deliver input frame to video encoder";
return;
}
TryToReturnBitstreamBuffer();
}
void MediaFoundationVideoEncodeAccelerator::ProcessInput(
scoped_refptr<VideoFrame> frame,
bool force_keyframe) {
DVLOG(3) << __func__;
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
// Convert I420 to NV12 as input.
Microsoft::WRL::ComPtr<IMFMediaBuffer> input_buffer;
input_sample_->GetBufferByIndex(0, &input_buffer);
{
MediaBufferScopedPointer scoped_buffer(input_buffer.Get());
DCHECK(scoped_buffer.get());
libyuv::I420Copy(frame->visible_data(VideoFrame::kYPlane),
int dst_stride_y = frame->stride(VideoFrame::kYPlane);
uint8_t* dst_uv =
scoped_buffer.get() +
frame->stride(VideoFrame::kYPlane) * frame->rows(VideoFrame::kYPlane);
int dst_stride_uv = frame->stride(VideoFrame::kUPlane) * 2;
libyuv::I420ToNV12(frame->visible_data(VideoFrame::kYPlane),
frame->stride(VideoFrame::kYPlane),
frame->visible_data(VideoFrame::kVPlane),
frame->stride(VideoFrame::kVPlane),
frame->visible_data(VideoFrame::kUPlane),
frame->stride(VideoFrame::kUPlane), scoped_buffer.get(),
y_stride_, scoped_buffer.get() + u_plane_offset_,
u_stride_, scoped_buffer.get() + v_plane_offset_,
v_stride_, input_visible_size_.width(),
frame->stride(VideoFrame::kUPlane),
frame->visible_data(VideoFrame::kVPlane),
frame->stride(VideoFrame::kVPlane), scoped_buffer.get(),
dst_stride_y, dst_uv, dst_stride_uv,
input_visible_size_.width(),
input_visible_size_.height());
}
input_sample_->SetSampleTime(frame->timestamp().InMicroseconds() *
kOneMicrosecondInMFSampleTimeUnits);
UINT64 sample_duration = 1;
UINT64 sample_duration = 0;
HRESULT hr =
MFFrameRateToAverageTimePerFrame(frame_rate_, 1, &sample_duration);
RETURN_ON_HR_FAILURE(hr, "Couldn't calculate sample duration", );
......@@ -572,80 +698,70 @@ void MediaFoundationVideoEncodeAccelerator::EncodeTask(
var.vt = VT_UI4;
var.ulVal = 1;
hr = codec_api_->SetValue(&CODECAPI_AVEncVideoForceKeyFrame, &var);
if (!compatible_with_win7_ && !SUCCEEDED(hr)) {
if (!compatible_with_win7_ && FAILED(hr)) {
LOG(WARNING) << "Failed to set CODECAPI_AVEncVideoForceKeyFrame, "
"HRESULT: 0x" << std::hex << hr;
}
}
hr = encoder_->ProcessInput(input_stream_id_, input_sample_.Get(), 0);
// According to MSDN, if encoder returns MF_E_NOTACCEPTING, we need to try
// processing the output. This error indicates that encoder does not accept
// any more input data.
if (hr == MF_E_NOTACCEPTING) {
DVLOG(3) << "MF_E_NOTACCEPTING";
ProcessOutput();
hr = encoder_->ProcessInput(input_stream_id_, input_sample_.Get(), 0);
if (!SUCCEEDED(hr)) {
NotifyError(kPlatformFailureError);
RETURN_ON_HR_FAILURE(hr, "Couldn't encode", );
}
} else if (!SUCCEEDED(hr)) {
if (FAILED(hr)) {
NotifyError(kPlatformFailureError);
RETURN_ON_HR_FAILURE(hr, "Couldn't encode", );
}
DVLOG(3) << "Sent for encode " << hr;
ProcessOutput();
DVLOG(3) << "Sent for encode " << hr;
}
void MediaFoundationVideoEncodeAccelerator::ProcessOutput() {
DVLOG(3) << __func__;
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
DWORD output_status = 0;
HRESULT hr = encoder_->GetOutputStatus(&output_status);
RETURN_ON_HR_FAILURE(hr, "Couldn't get output status", );
if (output_status != MFT_OUTPUT_STATUS_SAMPLE_READY) {
DVLOG(3) << "Output isnt ready";
return;
}
MFT_OUTPUT_DATA_BUFFER output_data_buffer = {0};
output_data_buffer.dwStreamID = 0;
output_data_buffer.dwStreamID = output_stream_id_;
output_data_buffer.dwStatus = 0;
output_data_buffer.pEvents = NULL;
output_data_buffer.pSample = output_sample_.Get();
output_data_buffer.pEvents = nullptr;
output_data_buffer.pSample = nullptr;
DWORD status = 0;
hr = encoder_->ProcessOutput(output_stream_id_, 1, &output_data_buffer,
&status);
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
DVLOG(3) << "MF_E_TRANSFORM_NEED_MORE_INPUT" << status;
HRESULT hr = encoder_->ProcessOutput(0, 1, &output_data_buffer, &status);
if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
hr = S_OK;
Microsoft::WRL::ComPtr<IMFMediaType> media_type;
for (DWORD type_index = 0; SUCCEEDED(hr); ++type_index) {
hr = encoder_->GetOutputAvailableType(output_stream_id_, type_index,
&media_type);
if (SUCCEEDED(hr)) {
break;
}
}
hr = encoder_->SetOutputType(output_stream_id_, media_type.Get(), 0);
return;
}
RETURN_ON_HR_FAILURE(hr, "Couldn't get encoded data", );
DVLOG(3) << "Got encoded data " << hr;
Microsoft::WRL::ComPtr<IMFMediaBuffer> output_buffer;
hr = output_sample_->GetBufferByIndex(0, &output_buffer);
hr = output_data_buffer.pSample->GetBufferByIndex(0, &output_buffer);
RETURN_ON_HR_FAILURE(hr, "Couldn't get buffer by index", );
DWORD size = 0;
hr = output_buffer->GetCurrentLength(&size);
RETURN_ON_HR_FAILURE(hr, "Couldn't get buffer length", );
base::TimeDelta timestamp;
LONGLONG sample_time;
hr = output_sample_->GetSampleTime(&sample_time);
hr = output_data_buffer.pSample->GetSampleTime(&sample_time);
if (SUCCEEDED(hr)) {
timestamp = base::TimeDelta::FromMicroseconds(
sample_time / kOneMicrosecondInMFSampleTimeUnits);
}
const bool keyframe = MFGetAttributeUINT32(
output_sample_.Get(), MFSampleExtension_CleanPoint, false);
DVLOG(3) << "We HAVE encoded data with size:" << size << " keyframe "
<< keyframe;
output_data_buffer.pSample, MFSampleExtension_CleanPoint, false);
DVLOG(3) << "Encoded data with size:" << size << " keyframe " << keyframe;
// If no bit stream buffer presents, queue the output first.
if (bitstream_buffer_queue_.empty()) {
DVLOG(3) << "No bitstream buffers.";
// We need to copy the output so that encoding can continue.
......@@ -656,9 +772,12 @@ void MediaFoundationVideoEncodeAccelerator::ProcessOutput() {
memcpy(encode_output->memory(), scoped_buffer.get(), size);
}
encoder_output_queue_.push_back(std::move(encode_output));
output_data_buffer.pSample->Release();
output_data_buffer.pSample = nullptr;
return;
}
// Immediately return encoded buffer with BitstreamBuffer to client.
std::unique_ptr<MediaFoundationVideoEncodeAccelerator::BitstreamBufferRef>
buffer_ref = std::move(bitstream_buffer_queue_.front());
bitstream_buffer_queue_.pop_front();
......@@ -668,15 +787,88 @@ void MediaFoundationVideoEncodeAccelerator::ProcessOutput() {
memcpy(buffer_ref->mapping.memory(), scoped_buffer.get(), size);
}
output_data_buffer.pSample->Release();
output_data_buffer.pSample = nullptr;
main_client_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&Client::BitstreamBufferReady, main_client_,
buffer_ref->id,
BitstreamBufferMetadata(size, keyframe, timestamp)));
}
bool MediaFoundationVideoEncodeAccelerator::TryToDeliverInputFrame(
scoped_refptr<VideoFrame> frame,
bool force_keyframe) {
bool input_delivered = false;
Microsoft::WRL::ComPtr<IMFMediaEvent> media_event;
MediaEventType event_type;
do {
HRESULT hr =
event_generator_->GetEvent(MF_EVENT_FLAG_NO_WAIT, &media_event);
if (FAILED(hr)) {
break;
}
hr = media_event->GetType(&event_type);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to get the type of media event.";
break;
}
switch (event_type) {
case METransformHaveOutput: {
ProcessOutput();
continue;
}
case METransformNeedInput: {
ProcessInput(frame, force_keyframe);
return true;
}
default:
break;
}
} while (true);
return input_delivered;
}
// Keep calling ProcessOutput recursively until MF_E_TRANSFORM_NEED_MORE_INPUT
// is returned to flush out all the output.
void MediaFoundationVideoEncodeAccelerator::TryToReturnBitstreamBuffer() {
// Try to fetch the encoded frame in time.
bool output_processed = false;
do {
Microsoft::WRL::ComPtr<IMFMediaEvent> media_event;
MediaEventType event_type;
HRESULT hr =
event_generator_->GetEvent(MF_EVENT_FLAG_NO_WAIT, &media_event);
if (FAILED(hr)) {
if (!output_processed) {
continue;
} else {
break;
}
}
hr = media_event->GetType(&event_type);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to get the type of media event.";
break;
}
switch (event_type) {
case METransformHaveOutput: {
ProcessOutput();
output_processed = true;
break;
}
case METransformNeedInput: {
input_required_ = true;
continue;
}
default:
break;
}
} while (true);
}
void MediaFoundationVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
......@@ -689,22 +881,9 @@ void MediaFoundationVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
std::unique_ptr<MediaFoundationVideoEncodeAccelerator::EncodeOutput>
encode_output = std::move(encoder_output_queue_.front());
encoder_output_queue_.pop_front();
ReturnBitstreamBuffer(std::move(encode_output), std::move(buffer_ref));
return;
}
bitstream_buffer_queue_.push_back(std::move(buffer_ref));
}
void MediaFoundationVideoEncodeAccelerator::ReturnBitstreamBuffer(
std::unique_ptr<EncodeOutput> encode_output,
std::unique_ptr<MediaFoundationVideoEncodeAccelerator::BitstreamBufferRef>
buffer_ref) {
DVLOG(3) << __func__;
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
memcpy(buffer_ref->mapping.memory(), encode_output->memory(),
encode_output->size());
main_client_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&Client::BitstreamBufferReady, main_client_,
......@@ -712,6 +891,10 @@ void MediaFoundationVideoEncodeAccelerator::ReturnBitstreamBuffer(
BitstreamBufferMetadata(
encode_output->size(), encode_output->keyframe,
encode_output->capture_timestamp)));
return;
}
bitstream_buffer_queue_.push_back(std::move(buffer_ref));
}
void MediaFoundationVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
......@@ -732,7 +915,7 @@ void MediaFoundationVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
var.ulVal = target_bitrate_;
HRESULT hr = codec_api_->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var);
if (!compatible_with_win7_) {
RETURN_ON_HR_FAILURE(hr, "Couldn't set bitrate", );
RETURN_ON_HR_FAILURE(hr, "Couldn't update bitrate", );
}
}
}
......@@ -748,12 +931,22 @@ void MediaFoundationVideoEncodeAccelerator::DestroyTask() {
}
void MediaFoundationVideoEncodeAccelerator::ReleaseEncoderResources() {
while (!bitstream_buffer_queue_.empty())
bitstream_buffer_queue_.pop_front();
while (!encoder_output_queue_.empty())
encoder_output_queue_.pop_front();
if (activate_.Get() != nullptr) {
activate_->ShutdownObject();
activate_->Release();
activate_.Reset();
}
encoder_.Reset();
codec_api_.Reset();
event_generator_.Reset();
imf_input_media_type_.Reset();
imf_output_media_type_.Reset();
input_sample_.Reset();
output_sample_.Reset();
}
} // namespace content
} // namespace media
......@@ -65,8 +65,8 @@ class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator
// Creates an hardware encoder backed IMFTransform instance on |encoder_|.
bool CreateHardwareEncoderMFT();
// Initializes and allocates memory for input and output samples.
bool InitializeInputOutputSamples(VideoCodecProfile output_profile);
// Initializes and allocates memory for input and output parameters.
bool InitializeInputOutputParameters(VideoCodecProfile output_profile);
// Initializes encoder parameters for real-time use.
bool SetEncoderModes();
......@@ -82,20 +82,23 @@ class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator
// Encoding tasks to be run on |encoder_thread_|.
void EncodeTask(scoped_refptr<VideoFrame> frame, bool force_keyframe);
// Processes the input video frame for the encoder.
void ProcessInput(scoped_refptr<VideoFrame> frame, bool force_keyframe);
// Checks for and copies encoded output on |encoder_thread_|.
void ProcessOutput();
// Tries to deliver the input frame to the encoder.
bool TryToDeliverInputFrame(scoped_refptr<VideoFrame> frame,
bool force_keyframe);
// Tries to return a bitstream buffer to the client.
void TryToReturnBitstreamBuffer();
// Inserts the output buffers for reuse on |encoder_thread_|.
void UseOutputBitstreamBufferTask(
std::unique_ptr<BitstreamBufferRef> buffer_ref);
// Copies EncodeOutput into a BitstreamBuffer and returns it to the
// |main_client_|.
void ReturnBitstreamBuffer(
std::unique_ptr<EncodeOutput> encode_output,
std::unique_ptr<MediaFoundationVideoEncodeAccelerator::BitstreamBufferRef>
buffer_ref);
// Changes encode parameters on |encoder_thread_|.
void RequestEncodingParametersChangeTask(uint32_t bitrate,
uint32_t framerate);
......@@ -119,14 +122,11 @@ class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator
size_t bitstream_buffer_size_;
uint32_t frame_rate_;
uint32_t target_bitrate_;
size_t u_plane_offset_;
size_t v_plane_offset_;
size_t y_stride_;
size_t u_stride_;
size_t v_stride_;
Microsoft::WRL::ComPtr<IMFActivate> activate_;
Microsoft::WRL::ComPtr<IMFTransform> encoder_;
Microsoft::WRL::ComPtr<ICodecAPI> codec_api_;
Microsoft::WRL::ComPtr<IMFMediaEventGenerator> event_generator_;
DWORD input_stream_id_;
DWORD output_stream_id_;
......@@ -134,8 +134,8 @@ class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator
Microsoft::WRL::ComPtr<IMFMediaType> imf_input_media_type_;
Microsoft::WRL::ComPtr<IMFMediaType> imf_output_media_type_;
bool input_required_;
Microsoft::WRL::ComPtr<IMFSample> input_sample_;
Microsoft::WRL::ComPtr<IMFSample> output_sample_;
// MediaFoundation session.
MFSessionLifetime session_;
......@@ -155,7 +155,7 @@ class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator
// Declared last to ensure that all weak pointers are invalidated before
// other destructors run.
base::WeakPtrFactory<MediaFoundationVideoEncodeAccelerator>
encoder_task_weak_factory_{this};
encoder_task_weak_factory_;
DISALLOW_COPY_AND_ASSIGN(MediaFoundationVideoEncodeAccelerator);
};
......
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