Skip to content

Commit

Permalink
H264 HW encode using MediaFoundation
Browse files Browse the repository at this point in the history
This CL adds MediaFoundationVideoEncodeAccelerator which enables H264 encode support
using MediaFoundation on Windows 8.1+. Also, it includes a refactor of common
MediaFoundation classes under mf_helpers.*.

Note that, this is the first CL and H264 codec is still behind a flag.

Design Doc(with perf measurements): http://goo.gl/UCnwyA

BUG=590060
TEST= Tested AppRTC loopback with Chrome flag "--enable-webrtc-hw-h264-encoding" and
"--enable-mf-h264-encoding" on https://apprtc.appspot.com/?debug=loopback&vsc=h264
Also, added WIN specific sections at vea_unittests.

Review-Url: https://codereview.chromium.org/2058413003
Cr-Commit-Position: refs/heads/master@{#406876}
  • Loading branch information
uysalere authored and Commit bot committed Jul 21, 2016
1 parent 556252c commit e75bb44
Show file tree
Hide file tree
Showing 16 changed files with 980 additions and 127 deletions.
1 change: 1 addition & 0 deletions content/browser/gpu/gpu_process_host.cc
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ static const char* const kSwitchNames[] = {
#endif
#if defined(OS_WIN)
switches::kEnableAcceleratedVpxDecode,
switches::kEnableMFH264Encoding,
#endif
switches::kEnableHeapProfiling,
switches::kEnableLogging,
Expand Down
2 changes: 2 additions & 0 deletions content/gpu/gpu_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#include "base/win/scoped_com_initializer.h"
#include "base/win/windows_version.h"
#include "media/gpu/dxva_video_decode_accelerator_win.h"
#include "media/gpu/media_foundation_video_encode_accelerator_win.h"
#include "sandbox/win/src/sandbox.h"
#endif

Expand Down Expand Up @@ -489,6 +490,7 @@ bool WarmUpSandbox(const base::CommandLine& command_line) {

#if defined(OS_WIN)
media::DXVAVideoDecodeAccelerator::PreSandboxInitialization();
media::MediaFoundationVideoEncodeAccelerator::PreSandboxInitialization();
#endif
return true;
}
Expand Down
3 changes: 3 additions & 0 deletions media/base/media_switches.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ const char kUseGpuMemoryBuffersForCapture[] =
// for details.
const char kEnableExclusiveAudio[] = "enable-exclusive-audio";

// Enables H264 HW encode acceleration using Media Foundation for Windows.
const char kEnableMFH264Encoding[] = "enable-mf-h264-encoding";

// Force the use of MediaFoundation for video capture. This is only supported in
// Windows 7 and above. Used, like |kForceDirectShowVideoCapture|, to
// troubleshoot problems in Windows platforms.
Expand Down
1 change: 1 addition & 0 deletions media/base/media_switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ MEDIA_EXPORT extern const char kUseGpuMemoryBuffersForCapture[];

#if defined(OS_WIN)
MEDIA_EXPORT extern const char kEnableExclusiveAudio[];
MEDIA_EXPORT extern const char kEnableMFH264Encoding[];
MEDIA_EXPORT extern const char kForceMediaFoundationVideoCapture[];
MEDIA_EXPORT extern const char kForceWaveAudio[];
MEDIA_EXPORT extern const char kTrySupportedChannelLayouts[];
Expand Down
2 changes: 2 additions & 0 deletions media/base/win/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ component("win") {
defines = [ "MF_INITIALIZER_IMPLEMENTATION" ]
set_sources_assignment_filter([])
sources = [
"mf_helpers.cc",
"mf_helpers.h",
"mf_initializer.cc",
"mf_initializer.h",
"mf_initializer_export.h",
Expand Down
57 changes: 57 additions & 0 deletions media/base/win/mf_helpers.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2016 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/base/win/mf_helpers.h"

namespace media {

namespace mf {

void LogDXVAError(int line) {
LOG(ERROR) << "Error in dxva_video_decode_accelerator_win.cc on line "
<< line;
}

IMFSample* CreateEmptySampleWithBuffer(uint32_t buffer_length, int align) {
CHECK_GT(buffer_length, 0U);

base::win::ScopedComPtr<IMFSample> sample;
HRESULT hr = MFCreateSample(sample.Receive());
RETURN_ON_HR_FAILURE(hr, "MFCreateSample failed", NULL);

base::win::ScopedComPtr<IMFMediaBuffer> buffer;
if (align == 0) {
// Note that MFCreateMemoryBuffer is same as MFCreateAlignedMemoryBuffer
// with the align argument being 0.
hr = MFCreateMemoryBuffer(buffer_length, buffer.Receive());
} else {
hr =
MFCreateAlignedMemoryBuffer(buffer_length, align - 1, buffer.Receive());
}
RETURN_ON_HR_FAILURE(hr, "Failed to create memory buffer for sample", NULL);

hr = sample->AddBuffer(buffer.get());
RETURN_ON_HR_FAILURE(hr, "Failed to add buffer to sample", NULL);

buffer->SetCurrentLength(0);
return sample.Detach();
}

MediaBufferScopedPointer::MediaBufferScopedPointer(IMFMediaBuffer* media_buffer)
: media_buffer_(media_buffer),
buffer_(nullptr),
max_length_(0),
current_length_(0) {
HRESULT hr = media_buffer_->Lock(&buffer_, &max_length_, &current_length_);
CHECK(SUCCEEDED(hr));
}

MediaBufferScopedPointer::~MediaBufferScopedPointer() {
HRESULT hr = media_buffer_->Unlock();
CHECK(SUCCEEDED(hr));
}

} // namespace mf

} // namespace media
77 changes: 77 additions & 0 deletions media/base/win/mf_helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2016 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.

#ifndef MEDIA_BASE_WIN_MF_HELPERS_H_
#define MEDIA_BASE_WIN_MF_HELPERS_H_

#include <mfapi.h>
#include <stdint.h>

#include "base/win/scoped_comptr.h"
#include "media/base/win/mf_initializer_export.h"

namespace media {

namespace mf {

#define RETURN_ON_FAILURE(result, log, ret) \
do { \
if (!(result)) { \
DLOG(ERROR) << log; \
mf::LogDXVAError(__LINE__); \
return ret; \
} \
} while (0)

#define RETURN_ON_HR_FAILURE(result, log, ret) \
RETURN_ON_FAILURE(SUCCEEDED(result), \
log << ", HRESULT: 0x" << std::hex << result, ret);

#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \
do { \
if (!(result)) { \
DVLOG(1) << log; \
mf::LogDXVAError(__LINE__); \
StopOnError(error_code); \
return ret; \
} \
} while (0)

#define RETURN_AND_NOTIFY_ON_HR_FAILURE(result, log, error_code, ret) \
RETURN_AND_NOTIFY_ON_FAILURE(SUCCEEDED(result), \
log << ", HRESULT: 0x" << std::hex << result, \
error_code, ret);

MF_INITIALIZER_EXPORT void LogDXVAError(int line);

// Creates a Media Foundation sample with one buffer of length |buffer_length|
// on a |align|-byte boundary. Alignment must be a perfect power of 2 or 0.
MF_INITIALIZER_EXPORT IMFSample* CreateEmptySampleWithBuffer(
uint32_t buffer_length,
int align);

// Provides scoped access to the underlying buffer in an IMFMediaBuffer
// instance.
class MF_INITIALIZER_EXPORT MediaBufferScopedPointer {
public:
MediaBufferScopedPointer(IMFMediaBuffer* media_buffer);
~MediaBufferScopedPointer();

uint8_t* get() { return buffer_; }
DWORD current_length() const { return current_length_; }

private:
base::win::ScopedComPtr<IMFMediaBuffer> media_buffer_;
uint8_t* buffer_;
DWORD max_length_;
DWORD current_length_;

DISALLOW_COPY_AND_ASSIGN(MediaBufferScopedPointer);
};

} // namespace mf

} // namespace media

#endif // MEDIA_BASE_WIN_MF_HELPERS_H_
9 changes: 7 additions & 2 deletions media/gpu/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -337,14 +337,19 @@ component("gpu") {
"dxva_picture_buffer_win.h",
"dxva_video_decode_accelerator_win.cc",
"dxva_video_decode_accelerator_win.h",
"media_foundation_video_encode_accelerator_win.cc",
"media_foundation_video_encode_accelerator_win.h",
]
configs += [
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
"//build/config/compiler:no_size_t_to_int_warning",
"//third_party/khronos:khronos_headers",
]
public_deps += [ "//media/base/win" ]
deps += [ "//third_party/angle:includes" ]
deps += [
"//third_party/angle:includes",
"//third_party/libyuv",
]
libs += [
"d3d9.lib",
"d3d11.lib",
Expand Down Expand Up @@ -434,7 +439,7 @@ if (is_win || is_android || is_chromeos) {
}
}

if (is_chromeos || is_mac) {
if (is_chromeos || is_mac || is_win) {
test("video_encode_accelerator_unittest") {
deps = [
"//base",
Expand Down
104 changes: 3 additions & 101 deletions media/gpu/dxva_video_decode_accelerator_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "build/build_config.h"
#include "gpu/command_buffer/service/gpu_preferences.h"
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "media/base/win/mf_helpers.h"
#include "media/base/win/mf_initializer.h"
#include "media/gpu/dxva_picture_buffer_win.h"
#include "media/video/video_decode_accelerator.h"
Expand Down Expand Up @@ -187,42 +188,6 @@ static const DWORD g_IntelLegacyGPUList[] = {
0x102, 0x106, 0x116, 0x126,
};

// Provides scoped access to the underlying buffer in an IMFMediaBuffer
// instance.
class MediaBufferScopedPointer {
public:
explicit MediaBufferScopedPointer(IMFMediaBuffer* media_buffer)
: media_buffer_(media_buffer),
buffer_(nullptr),
max_length_(0),
current_length_(0) {
HRESULT hr = media_buffer_->Lock(&buffer_, &max_length_, &current_length_);
CHECK(SUCCEEDED(hr));
}

~MediaBufferScopedPointer() {
HRESULT hr = media_buffer_->Unlock();
CHECK(SUCCEEDED(hr));
}

uint8_t* get() { return buffer_; }

DWORD current_length() const { return current_length_; }

private:
base::win::ScopedComPtr<IMFMediaBuffer> media_buffer_;
uint8_t* buffer_;
DWORD max_length_;
DWORD current_length_;

DISALLOW_COPY_AND_ASSIGN(MediaBufferScopedPointer);
};

void LogDXVAError(int line) {
LOG(ERROR) << "Error in dxva_video_decode_accelerator_win.cc on line "
<< line;
}

} // namespace

namespace media {
Expand All @@ -235,34 +200,6 @@ static const VideoCodecProfile kSupportedProfiles[] = {
CreateDXGIDeviceManager
DXVAVideoDecodeAccelerator::create_dxgi_device_manager_ = NULL;

#define RETURN_ON_FAILURE(result, log, ret) \
do { \
if (!(result)) { \
DLOG(ERROR) << log; \
LogDXVAError(__LINE__); \
return ret; \
} \
} while (0)

#define RETURN_ON_HR_FAILURE(result, log, ret) \
RETURN_ON_FAILURE(SUCCEEDED(result), \
log << ", HRESULT: 0x" << std::hex << result, ret);

#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \
do { \
if (!(result)) { \
DVLOG(1) << log; \
LogDXVAError(__LINE__); \
StopOnError(error_code); \
return ret; \
} \
} while (0)

#define RETURN_AND_NOTIFY_ON_HR_FAILURE(result, log, error_code, ret) \
RETURN_AND_NOTIFY_ON_FAILURE(SUCCEEDED(result), \
log << ", HRESULT: 0x" << std::hex << result, \
error_code, ret);

enum {
// Maximum number of iterations we allow before aborting the attempt to flush
// the batched queries to the driver and allow torn/corrupt frames to be
Expand All @@ -282,41 +219,6 @@ enum {
kAcquireSyncWaitMs = 0,
};

static IMFSample* CreateEmptySample() {
base::win::ScopedComPtr<IMFSample> sample;
HRESULT hr = MFCreateSample(sample.Receive());
RETURN_ON_HR_FAILURE(hr, "MFCreateSample failed", NULL);
return sample.Detach();
}

// Creates a Media Foundation sample with one buffer of length |buffer_length|
// on a |align|-byte boundary. Alignment must be a perfect power of 2 or 0.
static IMFSample* CreateEmptySampleWithBuffer(uint32_t buffer_length,
int align) {
CHECK_GT(buffer_length, 0U);

base::win::ScopedComPtr<IMFSample> sample;
sample.Attach(CreateEmptySample());

base::win::ScopedComPtr<IMFMediaBuffer> buffer;
HRESULT hr = E_FAIL;
if (align == 0) {
// Note that MFCreateMemoryBuffer is same as MFCreateAlignedMemoryBuffer
// with the align argument being 0.
hr = MFCreateMemoryBuffer(buffer_length, buffer.Receive());
} else {
hr =
MFCreateAlignedMemoryBuffer(buffer_length, align - 1, buffer.Receive());
}
RETURN_ON_HR_FAILURE(hr, "Failed to create memory buffer for sample", NULL);

hr = sample->AddBuffer(buffer.get());
RETURN_ON_HR_FAILURE(hr, "Failed to add buffer to sample", NULL);

buffer->SetCurrentLength(0);
return sample.Detach();
}

// Creates a Media Foundation sample with one buffer containing a copy of the
// given Annex B stream data.
// If duration and sample time are not known, provide 0.
Expand All @@ -330,7 +232,7 @@ static IMFSample* CreateInputSample(const uint8_t* stream,
CHECK_GT(size, 0U);
base::win::ScopedComPtr<IMFSample> sample;
sample.Attach(
CreateEmptySampleWithBuffer(std::max(min_size, size), alignment));
mf::CreateEmptySampleWithBuffer(std::max(min_size, size), alignment));
RETURN_ON_FAILURE(sample.get(), "Failed to create empty sample", NULL);

base::win::ScopedComPtr<IMFMediaBuffer> buffer;
Expand Down Expand Up @@ -2751,7 +2653,7 @@ HRESULT DXVAVideoDecodeAccelerator::CheckConfigChanged(IMFSample* sample,
HRESULT hr = sample->GetBufferByIndex(0, buffer.Receive());
RETURN_ON_HR_FAILURE(hr, "Failed to get buffer from input sample", hr);

MediaBufferScopedPointer scoped_media_buffer(buffer.get());
mf::MediaBufferScopedPointer scoped_media_buffer(buffer.get());

if (!config_change_detector_->DetectConfig(
scoped_media_buffer.get(), scoped_media_buffer.current_length())) {
Expand Down
Loading

0 comments on commit e75bb44

Please sign in to comment.