summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorposciak@chromium.org <posciak@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-26 08:13:25 +0000
committerposciak@chromium.org <posciak@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-26 08:13:25 +0000
commit036bcb03ec872b5053d90695207b234dae4ec913 (patch)
tree726b2ba1c833559416c504dd8a9ba60c69625e60
parent597a1ab3ff262272a861c46cb98ce88902d12534 (diff)
downloadchromium_src-036bcb03ec872b5053d90695207b234dae4ec913.zip
chromium_src-036bcb03ec872b5053d90695207b234dae4ec913.tar.gz
chromium_src-036bcb03ec872b5053d90695207b234dae4ec913.tar.bz2
Revert "Revert 279650 "Add VaapiVideoEncodeAccelerator for HW-accelerate...""
Fixing the cros_trunk builder compilation failure in h264_bitstream_buffer_unittest, for which this was reverted. > Revert 279650 "Add VaapiVideoEncodeAccelerator for HW-accelerate..." > > Broke the Chrome-on-Chrome OS compile > > http://master.chrome.corp.google.com:8011/builders/cros%20trunk/builds/21417 > > > Add VaapiVideoEncodeAccelerator for HW-accelerated video encode. > > > > Add an implementation of VideoEncodeAccelerator utilizing VA-API for > > hardware encode on Intel-based ChromeOS platforms. > > > > BUG=378962 > > TEST=video_encode_accelerator_unittest > > > > Review URL: https://codereview.chromium.org/333253002 > > TBR=posciak@chromium.org > > Review URL: https://codereview.chromium.org/350413002 > > git-svn-id: svn://svn.chromium.org/chrome/trunk/src@279733 0039d316-1c4b-4281-b951-d872f2087c98 TBR=jamescook@chromium.org BUG=388778,378962 Review URL: https://codereview.chromium.org/356903002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@279958 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/login/chrome_restart_request.cc1
-rw-r--r--content/browser/gpu/gpu_process_host.cc3
-rw-r--r--content/browser/renderer_host/render_process_host_impl.cc3
-rw-r--r--content/common/gpu/media/gpu_video_encode_accelerator.cc25
-rw-r--r--content/common/gpu/media/h264_dpb.h1
-rw-r--r--content/common/gpu/media/va.sigs2
-rw-r--r--content/common/gpu/media/vaapi_h264_decoder.cc4
-rw-r--r--content/common/gpu/media/vaapi_h264_decoder_unittest.cc3
-rw-r--r--content/common/gpu/media/vaapi_video_decode_accelerator.cc4
-rw-r--r--content/common/gpu/media/vaapi_video_encode_accelerator.cc1061
-rw-r--r--content/common/gpu/media/vaapi_video_encode_accelerator.h264
-rw-r--r--content/common/gpu/media/vaapi_wrapper.cc313
-rw-r--r--content/common/gpu/media/vaapi_wrapper.h88
-rw-r--r--content/common/gpu/media/video_encode_accelerator_unittest.cc66
-rw-r--r--content/common/sandbox_linux/bpf_gpu_policy_linux.cc14
-rw-r--r--content/content_common.gypi7
-rw-r--r--content/content_tests.gypi13
-rw-r--r--content/public/common/content_switches.cc4
-rw-r--r--content/public/common/content_switches.h1
-rw-r--r--media/filters/h264_bitstream_buffer.cc152
-rw-r--r--media/filters/h264_bitstream_buffer.h120
-rw-r--r--media/filters/h264_bitstream_buffer_unittest.cc56
-rw-r--r--media/filters/h264_parser.cc6
-rw-r--r--media/filters/h264_parser.h39
-rw-r--r--media/media.gyp12
-rw-r--r--tools/metrics/histograms/histograms.xml11
26 files changed, 2186 insertions, 87 deletions
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index 74c93b6..7492b44 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -158,6 +158,7 @@ std::string DeriveCommandLine(const GURL& start_url,
::switches::kDisableWebRtcHWEncoding,
::switches::kEnableWebRtcHWVp8Encoding,
#endif
+ ::switches::kEnableVaapiAcceleratedVideoEncode,
#if defined(USE_OZONE)
::switches::kOzonePlatform,
#endif
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 96a233e..69d7320 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -981,6 +981,9 @@ bool GpuProcessHost::LaunchGpuProcess(const std::string& channel_id) {
#endif
switches::kEnableLogging,
switches::kEnableShareGroupAsyncTextureUpload,
+#if defined(OS_CHROMEOS)
+ switches::kEnableVaapiAcceleratedVideoEncode,
+#endif
switches::kGpuStartupDialog,
switches::kGpuSandboxAllowSysVShm,
switches::kGpuSandboxFailuresFatal,
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index a7738f6..1a1baa8 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1256,6 +1256,9 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
switches::kDisableDirectWrite,
switches::kEnableHighResolutionTime,
#endif
+#if defined(OS_CHROMEOS)
+ switches::kEnableVaapiAcceleratedVideoEncode,
+#endif
};
renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames,
arraysize(kSwitchNames));
diff --git a/content/common/gpu/media/gpu_video_encode_accelerator.cc b/content/common/gpu/media/gpu_video_encode_accelerator.cc
index 12c7b10..52ecab8 100644
--- a/content/common/gpu/media/gpu_video_encode_accelerator.cc
+++ b/content/common/gpu/media/gpu_video_encode_accelerator.cc
@@ -5,18 +5,27 @@
#include "content/common/gpu/media/gpu_video_encode_accelerator.h"
#include "base/callback.h"
+#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/shared_memory.h"
#include "base/message_loop/message_loop_proxy.h"
#include "build/build_config.h"
#include "content/common/gpu/gpu_channel.h"
#include "content/common/gpu/gpu_messages.h"
+#include "content/public/common/content_switches.h"
#include "ipc/ipc_message_macros.h"
#include "media/base/limits.h"
#include "media/base/video_frame.h"
-#if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL) && defined(USE_X11)
+#if defined(OS_CHROMEOS) && defined(USE_X11)
+
+#if defined(ARCH_CPU_ARMEL)
#include "content/common/gpu/media/v4l2_video_encode_accelerator.h"
+#elif defined(ARCH_CPU_X86_FAMILY)
+#include "content/common/gpu/media/vaapi_video_encode_accelerator.h"
+#include "ui/gfx/x/x11_types.h"
+#endif
+
#elif defined(OS_ANDROID) && defined(ENABLE_WEBRTC)
#include "content/common/gpu/media/android_video_encode_accelerator.h"
#endif
@@ -158,8 +167,12 @@ std::vector<media::VideoEncodeAccelerator::SupportedProfile>
GpuVideoEncodeAccelerator::GetSupportedProfiles() {
std::vector<media::VideoEncodeAccelerator::SupportedProfile> profiles;
-#if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL) && defined(USE_X11)
+#if defined(OS_CHROMEOS) && defined(USE_X11)
+#if defined(ARCH_CPU_ARMEL)
profiles = V4L2VideoEncodeAccelerator::GetSupportedProfiles();
+#elif defined(ARCH_CPU_X86_FAMILY)
+ profiles = VaapiVideoEncodeAccelerator::GetSupportedProfiles();
+#endif
#elif defined(OS_ANDROID) && defined(ENABLE_WEBRTC)
profiles = AndroidVideoEncodeAccelerator::GetSupportedProfiles();
#endif
@@ -170,12 +183,18 @@ GpuVideoEncodeAccelerator::GetSupportedProfiles() {
void GpuVideoEncodeAccelerator::CreateEncoder() {
DCHECK(!encoder_);
-#if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL) && defined(USE_X11)
+#if defined(OS_CHROMEOS) && defined(USE_X11)
+#if defined(ARCH_CPU_ARMEL)
scoped_ptr<V4L2Device> device = V4L2Device::Create(V4L2Device::kEncoder);
if (!device.get())
return;
encoder_.reset(new V4L2VideoEncodeAccelerator(device.Pass()));
+#elif defined(ARCH_CPU_X86_FAMILY)
+ const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
+ if (cmd_line->HasSwitch(switches::kEnableVaapiAcceleratedVideoEncode))
+ encoder_.reset(new VaapiVideoEncodeAccelerator(gfx::GetXDisplay()));
+#endif
#elif defined(OS_ANDROID) && defined(ENABLE_WEBRTC)
encoder_.reset(new AndroidVideoEncodeAccelerator());
#endif
diff --git a/content/common/gpu/media/h264_dpb.h b/content/common/gpu/media/h264_dpb.h
index 29be0bc..366c47a 100644
--- a/content/common/gpu/media/h264_dpb.h
+++ b/content/common/gpu/media/h264_dpb.h
@@ -41,6 +41,7 @@ struct H264Picture {
int frame_num_wrap;
int long_term_frame_idx;
+ media::H264SliceHeader::Type type;
bool idr; // IDR picture?
bool ref; // reference picture?
bool long_term; // long term reference picture?
diff --git a/content/common/gpu/media/va.sigs b/content/common/gpu/media/va.sigs
index b3cea38..9906f9f 100644
--- a/content/common/gpu/media/va.sigs
+++ b/content/common/gpu/media/va.sigs
@@ -22,7 +22,9 @@ const char *vaErrorStr(VAStatus error_status);
VAStatus vaGetConfigAttributes(VADisplay dpy, VAProfile profile, VAEntrypoint entrypoint, VAConfigAttrib *attrib_list, int num_attribs);
VAStatus vaInitialize(VADisplay dpy, int *major_version, int *minor_version);
VAStatus vaMapBuffer(VADisplay dpy, VABufferID buf_id, void **pbuf);
+int vaMaxNumEntrypoints (VADisplay dpy);
int vaMaxNumProfiles(VADisplay dpy);
+VAStatus vaQueryConfigEntrypoints (VADisplay dpy, VAProfile profile, VAEntrypoint *entrypoint_list, int *num_entrypoints);
VAStatus vaQueryConfigProfiles(VADisplay dpy, VAProfile *profile_list, int *num_profiles);
VAStatus vaRenderPicture(VADisplay dpy, VAContextID context, VABufferID *buffers, int num_buffers);
VAStatus vaSetDisplayAttributes(VADisplay dpy, VADisplayAttribute *attr_list, int num_attributes);
diff --git a/content/common/gpu/media/vaapi_h264_decoder.cc b/content/common/gpu/media/vaapi_h264_decoder.cc
index 9654a3c..91e76af 100644
--- a/content/common/gpu/media/vaapi_h264_decoder.cc
+++ b/content/common/gpu/media/vaapi_h264_decoder.cc
@@ -488,8 +488,8 @@ bool VaapiH264Decoder::DecodePicture() {
return false;
}
- if (!vaapi_wrapper_->DecodeAndDestroyPendingBuffers(
- dec_surface->va_surface()->id())) {
+ if (!vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(
+ dec_surface->va_surface()->id())) {
DVLOG(1) << "Failed decoding picture";
return false;
}
diff --git a/content/common/gpu/media/vaapi_h264_decoder_unittest.cc b/content/common/gpu/media/vaapi_h264_decoder_unittest.cc
index 6bd873e..b14c50b 100644
--- a/content/common/gpu/media/vaapi_h264_decoder_unittest.cc
+++ b/content/common/gpu/media/vaapi_h264_decoder_unittest.cc
@@ -127,7 +127,8 @@ bool VaapiH264DecoderLoop::Initialize(base::FilePath input_file,
media::VideoCodecProfile profile = media::H264PROFILE_BASELINE;
base::Closure report_error_cb =
base::Bind(&LogOnError, VaapiH264Decoder::VAAPI_ERROR);
- wrapper_ = VaapiWrapper::Create(profile, x_display_, report_error_cb);
+ wrapper_ = VaapiWrapper::Create(
+ VaapiWrapper::kDecode, profile, x_display_, report_error_cb);
if (!wrapper_.get()) {
LOG(ERROR) << "Can't create vaapi wrapper";
return false;
diff --git a/content/common/gpu/media/vaapi_video_decode_accelerator.cc b/content/common/gpu/media/vaapi_video_decode_accelerator.cc
index b68f5d4..afcfc8a 100644
--- a/content/common/gpu/media/vaapi_video_decode_accelerator.cc
+++ b/content/common/gpu/media/vaapi_video_decode_accelerator.cc
@@ -300,7 +300,9 @@ bool VaapiVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile,
}
vaapi_wrapper_ = VaapiWrapper::Create(
- profile, x_display_,
+ VaapiWrapper::kDecode,
+ profile,
+ x_display_,
base::Bind(&ReportToUMA, content::VaapiH264Decoder::VAAPI_ERROR));
if (!vaapi_wrapper_.get()) {
diff --git a/content/common/gpu/media/vaapi_video_encode_accelerator.cc b/content/common/gpu/media/vaapi_video_encode_accelerator.cc
new file mode 100644
index 0000000..6cfdd9d
--- /dev/null
+++ b/content/common/gpu/media/vaapi_video_encode_accelerator.cc
@@ -0,0 +1,1061 @@
+// Copyright 2014 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 "content/common/gpu/media/vaapi_video_encode_accelerator.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/metrics/histogram.h"
+#include "base/numerics/safe_conversions.h"
+#include "content/common/gpu/media/h264_dpb.h"
+#include "content/public/common/content_switches.h"
+#include "media/base/bind_to_current_loop.h"
+#include "third_party/libva/va/va_enc_h264.h"
+
+#define DVLOGF(level) DVLOG(level) << __FUNCTION__ << "(): "
+
+#define NOTIFY_ERROR(error, msg) \
+ do { \
+ SetState(kError); \
+ DVLOGF(1) << msg; \
+ DVLOGF(1) << "Calling NotifyError(" << error << ")"; \
+ NotifyError(error); \
+ } while (0)
+
+namespace content {
+
+namespace {
+// Need 2 surfaces for each frame: one for input data and one for
+// reconstructed picture, which is later used for reference.
+const size_t kMinSurfacesToEncode = 2;
+
+// Subjectively chosen.
+const size_t kNumInputBuffers = 4;
+const size_t kMaxNumReferenceFrames = 4;
+
+// We need up to kMaxNumReferenceFrames surfaces for reference, plus one
+// for input and one for encode (which will be added to the set of reference
+// frames for subsequent frames). Actual execution of HW encode is done
+// in parallel, and we want to process more frames in the meantime.
+// To have kNumInputBuffers in flight, we need a full set of reference +
+// encode surfaces (i.e. kMaxNumReferenceFrames + kMinSurfacesToEncode), and
+// (kNumInputBuffers - 1) of kMinSurfacesToEncode for the remaining frames
+// in flight.
+const size_t kNumSurfaces = kMaxNumReferenceFrames + kMinSurfacesToEncode +
+ kMinSurfacesToEncode * (kNumInputBuffers - 1);
+
+// An IDR every 128 frames, an I frame every 30 and no B frames.
+const int kIDRPeriod = 128;
+const int kIPeriod = 30;
+const int kIPPeriod = 1;
+
+const int kDefaultFramerate = 30;
+
+// HRD parameters (ch. E.2.2 in spec).
+const int kBitRateScale = 0; // bit_rate_scale for SPS HRD parameters.
+const int kCPBSizeScale = 0; // cpb_size_scale for SPS HRD parameters.
+
+const int kDefaultQP = 26;
+// All Intel codecs can do at least 4.1.
+const int kDefaultLevelIDC = 41;
+const int kChromaFormatIDC = 1; // 4:2:0
+
+// Arbitrarily chosen bitrate window size for rate control, in ms.
+const int kCPBWindowSizeMs = 1500;
+
+// UMA errors that the VaapiVideoEncodeAccelerator class reports.
+enum VAVEAEncoderFailure {
+ VAAPI_ERROR = 0,
+ VAVEA_ENCODER_FAILURES_MAX,
+};
+
+}
+
+// Round |value| up to |alignment|, which must be a power of 2.
+static inline size_t RoundUpToPowerOf2(size_t value, size_t alignment) {
+ // Check that |alignment| is a power of 2.
+ DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1)));
+ return ((value + (alignment - 1)) & ~(alignment - 1));
+}
+
+static void ReportToUMA(VAVEAEncoderFailure failure) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Media.VAVEA.EncoderFailure",
+ failure,
+ VAVEA_ENCODER_FAILURES_MAX);
+}
+
+struct VaapiVideoEncodeAccelerator::InputFrameRef {
+ InputFrameRef(const scoped_refptr<media::VideoFrame>& frame,
+ bool force_keyframe)
+ : frame(frame), force_keyframe(force_keyframe) {}
+ const scoped_refptr<media::VideoFrame> frame;
+ const bool force_keyframe;
+};
+
+struct VaapiVideoEncodeAccelerator::BitstreamBufferRef {
+ BitstreamBufferRef(int32 id, scoped_ptr<base::SharedMemory> shm, size_t size)
+ : id(id), shm(shm.Pass()), size(size) {}
+ const int32 id;
+ const scoped_ptr<base::SharedMemory> shm;
+ const size_t size;
+};
+
+// static
+std::vector<media::VideoEncodeAccelerator::SupportedProfile>
+VaapiVideoEncodeAccelerator::GetSupportedProfiles() {
+ std::vector<SupportedProfile> profiles;
+
+ const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
+ if (!cmd_line->HasSwitch(switches::kEnableVaapiAcceleratedVideoEncode))
+ return profiles;
+
+ SupportedProfile profile;
+ profile.profile = media::H264PROFILE_MAIN;
+ profile.max_resolution.SetSize(1920, 1088);
+ profile.max_framerate.numerator = kDefaultFramerate;
+ profile.max_framerate.denominator = 1;
+ profiles.push_back(profile);
+
+ // This is actually only constrained (see crbug.com/345569).
+ profile.profile = media::H264PROFILE_BASELINE;
+ profiles.push_back(profile);
+
+ profile.profile = media::H264PROFILE_HIGH;
+ profiles.push_back(profile);
+
+ return profiles;
+}
+
+static unsigned int Log2OfPowerOf2(unsigned int x) {
+ CHECK_GT(x, 0u);
+ DCHECK_EQ(x & (x - 1), 0u);
+
+ int log = 0;
+ while (x) {
+ x >>= 1;
+ ++log;
+ }
+ return log;
+}
+
+VaapiVideoEncodeAccelerator::VaapiVideoEncodeAccelerator(Display* x_display)
+ : profile_(media::VIDEO_CODEC_PROFILE_UNKNOWN),
+ mb_width_(0),
+ mb_height_(0),
+ output_buffer_byte_size_(0),
+ x_display_(x_display),
+ state_(kUninitialized),
+ frame_num_(0),
+ last_idr_frame_num_(0),
+ bitrate_(0),
+ framerate_(0),
+ cpb_size_(0),
+ encoding_parameters_changed_(false),
+ encoder_thread_("VAVEAEncoderThread"),
+ child_message_loop_proxy_(base::MessageLoopProxy::current()),
+ weak_this_ptr_factory_(this) {
+ DVLOGF(4);
+ weak_this_ = weak_this_ptr_factory_.GetWeakPtr();
+
+ max_ref_idx_l0_size_ = kMaxNumReferenceFrames;
+ qp_ = kDefaultQP;
+ idr_period_ = kIDRPeriod;
+ i_period_ = kIPeriod;
+ ip_period_ = kIPPeriod;
+}
+
+VaapiVideoEncodeAccelerator::~VaapiVideoEncodeAccelerator() {
+ DVLOGF(4);
+ DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(!encoder_thread_.IsRunning());
+}
+
+bool VaapiVideoEncodeAccelerator::Initialize(
+ media::VideoFrame::Format format,
+ const gfx::Size& input_visible_size,
+ media::VideoCodecProfile output_profile,
+ uint32 initial_bitrate,
+ Client* client) {
+ DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(!encoder_thread_.IsRunning());
+ DCHECK_EQ(state_, kUninitialized);
+
+ DVLOGF(1) << "Initializing VAVEA, input_format: "
+ << media::VideoFrame::FormatToString(format)
+ << ", input_visible_size: " << input_visible_size.ToString()
+ << ", output_profile: " << output_profile
+ << ", initial_bitrate: " << initial_bitrate;
+
+ client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client));
+ client_ = client_ptr_factory_->GetWeakPtr();
+
+ if (output_profile < media::H264PROFILE_BASELINE ||
+ output_profile > media::H264PROFILE_MAIN) {
+ DVLOGF(1) << "Unsupported output profile: " << output_profile;
+ return false;
+ }
+
+ if (format != media::VideoFrame::I420) {
+ DVLOGF(1) << "Unsupported input format: "
+ << media::VideoFrame::FormatToString(format);
+ return false;
+ }
+
+ profile_ = output_profile;
+ visible_size_ = input_visible_size;
+ // 4:2:0 format has to be 2-aligned.
+ DCHECK_EQ(visible_size_.width() % 2, 0);
+ DCHECK_EQ(visible_size_.height() % 2, 0);
+ coded_size_ = gfx::Size(RoundUpToPowerOf2(visible_size_.width(), 16),
+ RoundUpToPowerOf2(visible_size_.height(), 16));
+ mb_width_ = coded_size_.width() / 16;
+ mb_height_ = coded_size_.height() / 16;
+ output_buffer_byte_size_ = coded_size_.GetArea();
+
+ UpdateRates(initial_bitrate, kDefaultFramerate);
+
+ vaapi_wrapper_ = VaapiWrapper::Create(VaapiWrapper::kEncode,
+ output_profile,
+ x_display_,
+ base::Bind(&ReportToUMA, VAAPI_ERROR));
+ if (!vaapi_wrapper_) {
+ DVLOGF(1) << "Failed initializing VAAPI";
+ return false;
+ }
+
+ if (!encoder_thread_.Start()) {
+ DVLOGF(1) << "Failed to start encoder thread";
+ return false;
+ }
+ encoder_thread_proxy_ = encoder_thread_.message_loop_proxy();
+
+ // Finish the remaining initialization on the encoder thread.
+ encoder_thread_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&VaapiVideoEncodeAccelerator::InitializeTask,
+ base::Unretained(this)));
+
+ return true;
+}
+
+void VaapiVideoEncodeAccelerator::InitializeTask() {
+ DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
+ DCHECK_EQ(state_, kUninitialized);
+ DVLOGF(4);
+
+ va_surface_release_cb_ = media::BindToCurrentLoop(
+ base::Bind(&VaapiVideoEncodeAccelerator::RecycleVASurfaceID,
+ base::Unretained(this)));
+
+ if (!vaapi_wrapper_->CreateSurfaces(
+ coded_size_, kNumSurfaces, &available_va_surface_ids_)) {
+ NOTIFY_ERROR(kPlatformFailureError, "Failed creating VASurfaces");
+ return;
+ }
+
+ UpdateSPS();
+ GeneratePackedSPS();
+
+ UpdatePPS();
+ GeneratePackedPPS();
+
+ child_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&Client::RequireBitstreamBuffers,
+ client_,
+ kNumInputBuffers,
+ coded_size_,
+ output_buffer_byte_size_));
+
+ SetState(kEncoding);
+}
+
+void VaapiVideoEncodeAccelerator::RecycleVASurfaceID(
+ VASurfaceID va_surface_id) {
+ DVLOGF(4) << "va_surface_id: " << va_surface_id;
+ DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
+
+ available_va_surface_ids_.push_back(va_surface_id);
+ EncodeFrameTask();
+}
+
+void VaapiVideoEncodeAccelerator::BeginFrame(bool force_keyframe) {
+ memset(&current_pic_, 0, sizeof(current_pic_));
+
+ current_pic_.frame_num = frame_num_++;
+ frame_num_ %= idr_period_;
+
+ if (current_pic_.frame_num % i_period_ == 0 || force_keyframe)
+ current_pic_.type = media::H264SliceHeader::kISlice;
+ else
+ current_pic_.type = media::H264SliceHeader::kPSlice;
+
+ if (current_pic_.frame_num % idr_period_ == 0) {
+ current_pic_.idr = true;
+ last_idr_frame_num_ = current_pic_.frame_num;
+ ref_pic_list0_.clear();
+ }
+
+ if (current_pic_.type != media::H264SliceHeader::kBSlice)
+ current_pic_.ref = true;
+
+ current_pic_.pic_order_cnt = current_pic_.frame_num * 2;
+ current_pic_.top_field_order_cnt = current_pic_.pic_order_cnt;
+ current_pic_.pic_order_cnt_lsb = current_pic_.pic_order_cnt;
+
+ current_encode_job_->keyframe =
+ (current_pic_.type == media::H264SliceHeader::kISlice);
+
+ DVLOGF(4) << "Starting a new frame, type: " << current_pic_.type
+ << (force_keyframe ? " (forced keyframe)" : "")
+ << " frame_num: " << current_pic_.frame_num
+ << " POC: " << current_pic_.pic_order_cnt;
+}
+
+void VaapiVideoEncodeAccelerator::EndFrame() {
+ // Store the picture on the list of reference pictures and keep the list
+ // below maximum size, dropping oldest references.
+ if (current_pic_.ref)
+ ref_pic_list0_.push_front(current_encode_job_->recon_surface);
+ size_t max_num_ref_frames =
+ base::checked_cast<size_t>(current_sps_.max_num_ref_frames);
+ while (ref_pic_list0_.size() > max_num_ref_frames)
+ ref_pic_list0_.pop_back();
+
+ submitted_encode_jobs_.push(make_linked_ptr(current_encode_job_.release()));
+}
+
+static void InitVAPicture(VAPictureH264* va_pic) {
+ memset(va_pic, 0, sizeof(*va_pic));
+ va_pic->picture_id = VA_INVALID_ID;
+ va_pic->flags = VA_PICTURE_H264_INVALID;
+}
+
+bool VaapiVideoEncodeAccelerator::SubmitFrameParameters() {
+ VAEncSequenceParameterBufferH264 seq_param;
+ memset(&seq_param, 0, sizeof(seq_param));
+
+#define SPS_TO_SP(a) seq_param.a = current_sps_.a;
+ SPS_TO_SP(seq_parameter_set_id);
+ SPS_TO_SP(level_idc);
+
+ seq_param.intra_period = i_period_;
+ seq_param.intra_idr_period = idr_period_;
+ seq_param.ip_period = ip_period_;
+ seq_param.bits_per_second = bitrate_;
+
+ SPS_TO_SP(max_num_ref_frames);
+ seq_param.picture_width_in_mbs = mb_width_;
+ seq_param.picture_height_in_mbs = mb_height_;
+
+#define SPS_TO_SP_FS(a) seq_param.seq_fields.bits.a = current_sps_.a;
+ SPS_TO_SP_FS(chroma_format_idc);
+ SPS_TO_SP_FS(frame_mbs_only_flag);
+ SPS_TO_SP_FS(log2_max_frame_num_minus4);
+ SPS_TO_SP_FS(pic_order_cnt_type);
+ SPS_TO_SP_FS(log2_max_pic_order_cnt_lsb_minus4);
+#undef SPS_TO_SP_FS
+
+ SPS_TO_SP(bit_depth_luma_minus8);
+ SPS_TO_SP(bit_depth_chroma_minus8);
+
+ SPS_TO_SP(frame_cropping_flag);
+ if (current_sps_.frame_cropping_flag) {
+ SPS_TO_SP(frame_crop_left_offset);
+ SPS_TO_SP(frame_crop_right_offset);
+ SPS_TO_SP(frame_crop_top_offset);
+ SPS_TO_SP(frame_crop_bottom_offset);
+ }
+
+ SPS_TO_SP(vui_parameters_present_flag);
+#define SPS_TO_SP_VF(a) seq_param.vui_fields.bits.a = current_sps_.a;
+ SPS_TO_SP_VF(timing_info_present_flag);
+#undef SPS_TO_SP_VF
+ SPS_TO_SP(num_units_in_tick);
+ SPS_TO_SP(time_scale);
+#undef SPS_TO_SP
+
+ if (!vaapi_wrapper_->SubmitBuffer(VAEncSequenceParameterBufferType,
+ sizeof(seq_param),
+ &seq_param))
+ return false;
+
+ VAEncPictureParameterBufferH264 pic_param;
+ memset(&pic_param, 0, sizeof(pic_param));
+
+ pic_param.CurrPic.picture_id = current_encode_job_->recon_surface->id();
+ pic_param.CurrPic.TopFieldOrderCnt = current_pic_.top_field_order_cnt;
+ pic_param.CurrPic.BottomFieldOrderCnt = current_pic_.bottom_field_order_cnt;
+ pic_param.CurrPic.flags = 0;
+
+ for (size_t i = 0; i < arraysize(pic_param.ReferenceFrames); ++i)
+ InitVAPicture(&pic_param.ReferenceFrames[i]);
+
+ DCHECK_LE(ref_pic_list0_.size(), arraysize(pic_param.ReferenceFrames));
+ RefPicList::const_iterator iter = ref_pic_list0_.begin();
+ for (size_t i = 0;
+ i < arraysize(pic_param.ReferenceFrames) && iter != ref_pic_list0_.end();
+ ++iter, ++i) {
+ pic_param.ReferenceFrames[i].picture_id = (*iter)->id();
+ pic_param.ReferenceFrames[i].flags = 0;
+ }
+
+ pic_param.coded_buf = current_encode_job_->coded_buffer;
+ pic_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id;
+ pic_param.seq_parameter_set_id = current_pps_.seq_parameter_set_id;
+ pic_param.frame_num = current_pic_.frame_num;
+ pic_param.pic_init_qp = qp_;
+ pic_param.num_ref_idx_l0_active_minus1 = max_ref_idx_l0_size_ - 1;
+ pic_param.pic_fields.bits.idr_pic_flag = current_pic_.idr;
+ pic_param.pic_fields.bits.reference_pic_flag = current_pic_.ref;
+#define PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = current_pps_.a;
+ PPS_TO_PP_PF(entropy_coding_mode_flag);
+ PPS_TO_PP_PF(transform_8x8_mode_flag);
+ PPS_TO_PP_PF(deblocking_filter_control_present_flag);
+#undef PPS_TO_PP_PF
+
+ if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType,
+ sizeof(pic_param),
+ &pic_param))
+ return false;
+
+ VAEncSliceParameterBufferH264 slice_param;
+ memset(&slice_param, 0, sizeof(slice_param));
+
+ slice_param.num_macroblocks = mb_width_ * mb_height_;
+ slice_param.macroblock_info = VA_INVALID_ID;
+ slice_param.slice_type = current_pic_.type;
+ slice_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id;
+ slice_param.idr_pic_id = last_idr_frame_num_;
+ slice_param.pic_order_cnt_lsb = current_pic_.pic_order_cnt_lsb;
+ slice_param.num_ref_idx_active_override_flag = true;
+
+ for (size_t i = 0; i < arraysize(slice_param.RefPicList0); ++i)
+ InitVAPicture(&slice_param.RefPicList0[i]);
+
+ for (size_t i = 0; i < arraysize(slice_param.RefPicList1); ++i)
+ InitVAPicture(&slice_param.RefPicList1[i]);
+
+ DCHECK_LE(ref_pic_list0_.size(), arraysize(slice_param.RefPicList0));
+ iter = ref_pic_list0_.begin();
+ for (size_t i = 0;
+ i < arraysize(slice_param.RefPicList0) && iter != ref_pic_list0_.end();
+ ++iter, ++i) {
+ InitVAPicture(&slice_param.RefPicList0[i]);
+ slice_param.RefPicList0[i].picture_id = (*iter)->id();
+ slice_param.RefPicList0[i].flags = 0;
+ }
+
+ if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType,
+ sizeof(slice_param),
+ &slice_param))
+ return false;
+
+ VAEncMiscParameterRateControl rate_control_param;
+ memset(&rate_control_param, 0, sizeof(rate_control_param));
+ rate_control_param.bits_per_second = bitrate_;
+ rate_control_param.target_percentage = 90;
+ rate_control_param.window_size = kCPBWindowSizeMs;
+ rate_control_param.initial_qp = qp_;
+ rate_control_param.rc_flags.bits.disable_frame_skip = true;
+
+ if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(
+ VAEncMiscParameterTypeRateControl,
+ sizeof(rate_control_param),
+ &rate_control_param))
+ return false;
+
+ VAEncMiscParameterFrameRate framerate_param;
+ memset(&framerate_param, 0, sizeof(framerate_param));
+ framerate_param.framerate = framerate_;
+ if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(
+ VAEncMiscParameterTypeFrameRate,
+ sizeof(framerate_param),
+ &framerate_param))
+ return false;
+
+ VAEncMiscParameterHRD hrd_param;
+ memset(&hrd_param, 0, sizeof(hrd_param));
+ hrd_param.buffer_size = cpb_size_;
+ hrd_param.initial_buffer_fullness = cpb_size_ / 2;
+ if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer(VAEncMiscParameterTypeHRD,
+ sizeof(hrd_param),
+ &hrd_param))
+ return false;
+
+ return true;
+}
+
+bool VaapiVideoEncodeAccelerator::SubmitHeadersIfNeeded() {
+ if (current_pic_.type != media::H264SliceHeader::kISlice)
+ return true;
+
+ // Submit PPS.
+ VAEncPackedHeaderParameterBuffer par_buffer;
+ memset(&par_buffer, 0, sizeof(par_buffer));
+ par_buffer.type = VAEncPackedHeaderSequence;
+ par_buffer.bit_length = packed_sps_.BytesInBuffer() * 8;
+
+ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType,
+ sizeof(par_buffer),
+ &par_buffer))
+ return false;
+
+ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType,
+ packed_sps_.BytesInBuffer(),
+ packed_sps_.data()))
+ return false;
+
+ // Submit PPS.
+ memset(&par_buffer, 0, sizeof(par_buffer));
+ par_buffer.type = VAEncPackedHeaderPicture;
+ par_buffer.bit_length = packed_pps_.BytesInBuffer() * 8;
+
+ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType,
+ sizeof(par_buffer),
+ &par_buffer))
+ return false;
+
+ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType,
+ packed_pps_.BytesInBuffer(),
+ packed_pps_.data()))
+ return false;
+
+ return true;
+}
+
+bool VaapiVideoEncodeAccelerator::ExecuteEncode() {
+ DVLOGF(3) << "Encoding frame_num: " << current_pic_.frame_num;
+ return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(
+ current_encode_job_->input_surface->id());
+}
+
+bool VaapiVideoEncodeAccelerator::UploadFrame(
+ const scoped_refptr<media::VideoFrame>& frame) {
+ return vaapi_wrapper_->UploadVideoFrameToSurface(
+ frame, current_encode_job_->input_surface->id());
+}
+
+void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffer() {
+ DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
+
+ if (state_ != kEncoding)
+ return;
+
+ if (submitted_encode_jobs_.empty() || available_bitstream_buffers_.empty())
+ return;
+
+ linked_ptr<BitstreamBufferRef> buffer = available_bitstream_buffers_.front();
+ available_bitstream_buffers_.pop();
+
+ uint8* target_data = reinterpret_cast<uint8*>(buffer->shm->memory());
+
+ linked_ptr<EncodeJob> encode_job = submitted_encode_jobs_.front();
+ submitted_encode_jobs_.pop();
+
+ size_t data_size = 0;
+ if (!vaapi_wrapper_->DownloadAndDestroyCodedBuffer(
+ encode_job->coded_buffer,
+ encode_job->input_surface->id(),
+ target_data,
+ buffer->size,
+ &data_size)) {
+ NOTIFY_ERROR(kPlatformFailureError, "Failed downloading coded buffer");
+ return;
+ }
+
+ DVLOGF(3) << "Returning bitstream buffer "
+ << (encode_job->keyframe ? "(keyframe)" : "")
+ << " id: " << buffer->id << " size: " << data_size;
+
+ child_message_loop_proxy_->PostTask(FROM_HERE,
+ base::Bind(&Client::BitstreamBufferReady,
+ client_,
+ buffer->id,
+ data_size,
+ encode_job->keyframe));
+}
+
+void VaapiVideoEncodeAccelerator::Encode(
+ const scoped_refptr<media::VideoFrame>& frame,
+ bool force_keyframe) {
+ DVLOGF(3) << "Frame timestamp: " << frame->timestamp().InMilliseconds()
+ << " force_keyframe: " << force_keyframe;
+ DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
+
+ encoder_thread_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&VaapiVideoEncodeAccelerator::EncodeTask,
+ base::Unretained(this),
+ frame,
+ force_keyframe));
+}
+
+bool VaapiVideoEncodeAccelerator::PrepareNextJob() {
+ if (available_va_surface_ids_.size() < kMinSurfacesToEncode)
+ return false;
+
+ DCHECK(!current_encode_job_);
+ current_encode_job_.reset(new EncodeJob());
+
+ if (!vaapi_wrapper_->CreateCodedBuffer(output_buffer_byte_size_,
+ &current_encode_job_->coded_buffer)) {
+ NOTIFY_ERROR(kPlatformFailureError, "Failed creating coded buffer");
+ return false;
+ }
+
+ current_encode_job_->input_surface =
+ new VASurface(available_va_surface_ids_.back(), va_surface_release_cb_);
+ available_va_surface_ids_.pop_back();
+
+ current_encode_job_->recon_surface =
+ new VASurface(available_va_surface_ids_.back(), va_surface_release_cb_);
+ available_va_surface_ids_.pop_back();
+
+ // Reference surfaces are needed until the job is done, but they get
+ // removed from ref_pic_list0_ when it's full at the end of job submission.
+ // Keep refs to them along with the job and only release after sync.
+ current_encode_job_->reference_surfaces = ref_pic_list0_;
+
+ return true;
+}
+
+void VaapiVideoEncodeAccelerator::EncodeTask(
+ const scoped_refptr<media::VideoFrame>& frame,
+ bool force_keyframe) {
+ DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
+ DCHECK_NE(state_, kUninitialized);
+
+ encoder_input_queue_.push(
+ make_linked_ptr(new InputFrameRef(frame, force_keyframe)));
+ EncodeFrameTask();
+}
+
+void VaapiVideoEncodeAccelerator::EncodeFrameTask() {
+ DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
+
+ if (state_ != kEncoding || encoder_input_queue_.empty())
+ return;
+
+ if (!PrepareNextJob()) {
+ DVLOGF(4) << "Not ready for next frame yet";
+ return;
+ }
+
+ linked_ptr<InputFrameRef> frame_ref = encoder_input_queue_.front();
+ encoder_input_queue_.pop();
+
+ if (!UploadFrame(frame_ref->frame)) {
+ NOTIFY_ERROR(kPlatformFailureError, "Failed uploading source frame to HW.");
+ return;
+ }
+
+ BeginFrame(frame_ref->force_keyframe || encoding_parameters_changed_);
+ encoding_parameters_changed_ = false;
+
+ if (!SubmitFrameParameters()) {
+ NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame parameters.");
+ return;
+ }
+
+ if (!SubmitHeadersIfNeeded()) {
+ NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame headers.");
+ return;
+ }
+
+ if (!ExecuteEncode()) {
+ NOTIFY_ERROR(kPlatformFailureError, "Failed submitting encode job to HW.");
+ return;
+ }
+
+ EndFrame();
+ TryToReturnBitstreamBuffer();
+}
+
+void VaapiVideoEncodeAccelerator::UseOutputBitstreamBuffer(
+ const media::BitstreamBuffer& buffer) {
+ DVLOGF(4) << "id: " << buffer.id();
+ DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
+
+ if (buffer.size() < output_buffer_byte_size_) {
+ NOTIFY_ERROR(kInvalidArgumentError, "Provided bitstream buffer too small");
+ return;
+ }
+
+ scoped_ptr<base::SharedMemory> shm(
+ new base::SharedMemory(buffer.handle(), false));
+ if (!shm->Map(buffer.size())) {
+ NOTIFY_ERROR(kPlatformFailureError, "Failed mapping shared memory.");
+ return;
+ }
+
+ scoped_ptr<BitstreamBufferRef> buffer_ref(
+ new BitstreamBufferRef(buffer.id(), shm.Pass(), buffer.size()));
+
+ encoder_thread_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask,
+ base::Unretained(this),
+ base::Passed(&buffer_ref)));
+}
+
+void VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
+ scoped_ptr<BitstreamBufferRef> buffer_ref) {
+ DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
+ DCHECK_NE(state_, kUninitialized);
+
+ available_bitstream_buffers_.push(make_linked_ptr(buffer_ref.release()));
+ TryToReturnBitstreamBuffer();
+}
+
+void VaapiVideoEncodeAccelerator::RequestEncodingParametersChange(
+ uint32 bitrate,
+ uint32 framerate) {
+ DVLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate;
+ DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
+
+ encoder_thread_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask,
+ base::Unretained(this),
+ bitrate,
+ framerate));
+}
+
+void VaapiVideoEncodeAccelerator::UpdateRates(uint32 bitrate,
+ uint32 framerate) {
+ if (encoder_thread_.IsRunning())
+ DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
+ DCHECK_NE(bitrate, 0u);
+ DCHECK_NE(framerate, 0u);
+ bitrate_ = bitrate;
+ framerate_ = framerate;
+ cpb_size_ = bitrate_ * kCPBWindowSizeMs / 1000;
+}
+
+void VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
+ uint32 bitrate,
+ uint32 framerate) {
+ DVLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate;
+ DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
+ DCHECK_NE(state_, kUninitialized);
+
+ UpdateRates(bitrate, framerate);
+
+ UpdateSPS();
+ GeneratePackedSPS();
+
+ // Submit new parameters along with next frame that will be processed.
+ encoding_parameters_changed_ = true;
+}
+
+void VaapiVideoEncodeAccelerator::Destroy() {
+ DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
+
+ // Can't call client anymore after Destroy() returns.
+ client_ptr_factory_.reset();
+ weak_this_ptr_factory_.InvalidateWeakPtrs();
+
+ // Early-exit encoder tasks if they are running and join the thread.
+ if (encoder_thread_.IsRunning()) {
+ encoder_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&VaapiVideoEncodeAccelerator::DestroyTask,
+ base::Unretained(this)));
+ encoder_thread_.Stop();
+ }
+
+ delete this;
+}
+
+void VaapiVideoEncodeAccelerator::DestroyTask() {
+ DVLOGF(2);
+ DCHECK(encoder_thread_proxy_->BelongsToCurrentThread());
+ SetState(kError);
+}
+
+void VaapiVideoEncodeAccelerator::UpdateSPS() {
+ memset(&current_sps_, 0, sizeof(media::H264SPS));
+
+ // Spec A.2 and A.3.
+ switch (profile_) {
+ case media::H264PROFILE_BASELINE:
+ // Due to crbug.com/345569, we don't distinguish between constrained
+ // and non-constrained baseline profiles. Since many codecs can't do
+ // non-constrained, and constrained is usually what we mean (and it's a
+ // subset of non-constrained), default to it.
+ current_sps_.profile_idc = media::H264SPS::kProfileIDCBaseline;
+ current_sps_.constraint_set0_flag = true;
+ break;
+ case media::H264PROFILE_MAIN:
+ current_sps_.profile_idc = media::H264SPS::kProfileIDCMain;
+ current_sps_.constraint_set1_flag = true;
+ break;
+ case media::H264PROFILE_HIGH:
+ current_sps_.profile_idc = media::H264SPS::kProfileIDCHigh;
+ break;
+ default:
+ NOTIMPLEMENTED();
+ return;
+ }
+
+ current_sps_.level_idc = kDefaultLevelIDC;
+ current_sps_.seq_parameter_set_id = 0;
+ current_sps_.chroma_format_idc = kChromaFormatIDC;
+
+ DCHECK_GE(idr_period_, 1u << 4);
+ current_sps_.log2_max_frame_num_minus4 = Log2OfPowerOf2(idr_period_) - 4;
+ current_sps_.pic_order_cnt_type = 0;
+ current_sps_.log2_max_pic_order_cnt_lsb_minus4 =
+ Log2OfPowerOf2(idr_period_ * 2) - 4;
+ current_sps_.max_num_ref_frames = max_ref_idx_l0_size_;
+
+ current_sps_.frame_mbs_only_flag = true;
+
+ DCHECK_GT(mb_width_, 0u);
+ DCHECK_GT(mb_height_, 0u);
+ current_sps_.pic_width_in_mbs_minus1 = mb_width_ - 1;
+ DCHECK(current_sps_.frame_mbs_only_flag);
+ current_sps_.pic_height_in_map_units_minus1 = mb_height_ - 1;
+
+ if (visible_size_ != coded_size_) {
+ // Visible size differs from coded size, fill crop information.
+ current_sps_.frame_cropping_flag = true;
+ DCHECK(!current_sps_.separate_colour_plane_flag);
+ // Spec table 6-1. Only 4:2:0 for now.
+ DCHECK_EQ(current_sps_.chroma_format_idc, 1);
+ // Spec 7.4.2.1.1. Crop is in crop units, which is 2 pixels for 4:2:0.
+ const unsigned int crop_unit_x = 2;
+ const unsigned int crop_unit_y = 2 * (2 - current_sps_.frame_mbs_only_flag);
+ current_sps_.frame_crop_left_offset = 0;
+ current_sps_.frame_crop_right_offset =
+ (coded_size_.width() - visible_size_.width()) / crop_unit_x;
+ current_sps_.frame_crop_top_offset = 0;
+ current_sps_.frame_crop_bottom_offset =
+ (coded_size_.height() - visible_size_.height()) / crop_unit_y;
+ }
+
+ current_sps_.vui_parameters_present_flag = true;
+ current_sps_.timing_info_present_flag = true;
+ current_sps_.num_units_in_tick = 1;
+ current_sps_.time_scale = framerate_ * 2; // See equation D-2 in spec.
+ current_sps_.fixed_frame_rate_flag = true;
+
+ current_sps_.nal_hrd_parameters_present_flag = true;
+ // H.264 spec ch. E.2.2.
+ current_sps_.cpb_cnt_minus1 = 0;
+ current_sps_.bit_rate_scale = kBitRateScale;
+ current_sps_.cpb_size_scale = kCPBSizeScale;
+ current_sps_.bit_rate_value_minus1[0] =
+ (bitrate_ >>
+ (kBitRateScale + media::H264SPS::kBitRateScaleConstantTerm)) - 1;
+ current_sps_.cpb_size_value_minus1[0] =
+ (cpb_size_ >>
+ (kCPBSizeScale + media::H264SPS::kCPBSizeScaleConstantTerm)) - 1;
+ current_sps_.cbr_flag[0] = true;
+ current_sps_.initial_cpb_removal_delay_length_minus_1 =
+ media::H264SPS::kDefaultInitialCPBRemovalDelayLength - 1;
+ current_sps_.cpb_removal_delay_length_minus1 =
+ media::H264SPS::kDefaultInitialCPBRemovalDelayLength - 1;
+ current_sps_.dpb_output_delay_length_minus1 =
+ media::H264SPS::kDefaultDPBOutputDelayLength - 1;
+ current_sps_.time_offset_length = media::H264SPS::kDefaultTimeOffsetLength;
+ current_sps_.low_delay_hrd_flag = false;
+}
+
+void VaapiVideoEncodeAccelerator::GeneratePackedSPS() {
+ packed_sps_.Reset();
+
+ packed_sps_.BeginNALU(media::H264NALU::kSPS, 3);
+
+ packed_sps_.AppendBits(8, current_sps_.profile_idc);
+ packed_sps_.AppendBool(current_sps_.constraint_set0_flag);
+ packed_sps_.AppendBool(current_sps_.constraint_set1_flag);
+ packed_sps_.AppendBool(current_sps_.constraint_set2_flag);
+ packed_sps_.AppendBool(current_sps_.constraint_set3_flag);
+ packed_sps_.AppendBool(current_sps_.constraint_set4_flag);
+ packed_sps_.AppendBool(current_sps_.constraint_set5_flag);
+ packed_sps_.AppendBits(2, 0); // reserved_zero_2bits
+ packed_sps_.AppendBits(8, current_sps_.level_idc);
+ packed_sps_.AppendUE(current_sps_.seq_parameter_set_id);
+
+ if (current_sps_.profile_idc == media::H264SPS::kProfileIDCHigh) {
+ packed_sps_.AppendUE(current_sps_.chroma_format_idc);
+ if (current_sps_.chroma_format_idc == 3)
+ packed_sps_.AppendBool(current_sps_.separate_colour_plane_flag);
+ packed_sps_.AppendUE(current_sps_.bit_depth_luma_minus8);
+ packed_sps_.AppendUE(current_sps_.bit_depth_chroma_minus8);
+ packed_sps_.AppendBool(current_sps_.qpprime_y_zero_transform_bypass_flag);
+ packed_sps_.AppendBool(current_sps_.seq_scaling_matrix_present_flag);
+ CHECK(!current_sps_.seq_scaling_matrix_present_flag);
+ }
+
+ packed_sps_.AppendUE(current_sps_.log2_max_frame_num_minus4);
+ packed_sps_.AppendUE(current_sps_.pic_order_cnt_type);
+ if (current_sps_.pic_order_cnt_type == 0)
+ packed_sps_.AppendUE(current_sps_.log2_max_pic_order_cnt_lsb_minus4);
+ else if (current_sps_.pic_order_cnt_type == 1) {
+ CHECK(1);
+ }
+
+ packed_sps_.AppendUE(current_sps_.max_num_ref_frames);
+ packed_sps_.AppendBool(current_sps_.gaps_in_frame_num_value_allowed_flag);
+ packed_sps_.AppendUE(current_sps_.pic_width_in_mbs_minus1);
+ packed_sps_.AppendUE(current_sps_.pic_height_in_map_units_minus1);
+
+ packed_sps_.AppendBool(current_sps_.frame_mbs_only_flag);
+ if (!current_sps_.frame_mbs_only_flag)
+ packed_sps_.AppendBool(current_sps_.mb_adaptive_frame_field_flag);
+
+ packed_sps_.AppendBool(current_sps_.direct_8x8_inference_flag);
+
+ packed_sps_.AppendBool(current_sps_.frame_cropping_flag);
+ if (current_sps_.frame_cropping_flag) {
+ packed_sps_.AppendUE(current_sps_.frame_crop_left_offset);
+ packed_sps_.AppendUE(current_sps_.frame_crop_right_offset);
+ packed_sps_.AppendUE(current_sps_.frame_crop_top_offset);
+ packed_sps_.AppendUE(current_sps_.frame_crop_bottom_offset);
+ }
+
+ packed_sps_.AppendBool(current_sps_.vui_parameters_present_flag);
+ if (current_sps_.vui_parameters_present_flag) {
+ packed_sps_.AppendBool(false); // aspect_ratio_info_present_flag
+ packed_sps_.AppendBool(false); // overscan_info_present_flag
+ packed_sps_.AppendBool(false); // video_signal_type_present_flag
+ packed_sps_.AppendBool(false); // chroma_loc_info_present_flag
+
+ packed_sps_.AppendBool(current_sps_.timing_info_present_flag);
+ if (current_sps_.timing_info_present_flag) {
+ packed_sps_.AppendBits(32, current_sps_.num_units_in_tick);
+ packed_sps_.AppendBits(32, current_sps_.time_scale);
+ packed_sps_.AppendBool(current_sps_.fixed_frame_rate_flag);
+ }
+
+ packed_sps_.AppendBool(current_sps_.nal_hrd_parameters_present_flag);
+ if (current_sps_.nal_hrd_parameters_present_flag) {
+ packed_sps_.AppendUE(current_sps_.cpb_cnt_minus1);
+ packed_sps_.AppendBits(4, current_sps_.bit_rate_scale);
+ packed_sps_.AppendBits(4, current_sps_.cpb_size_scale);
+ CHECK_LT(base::checked_cast<size_t>(current_sps_.cpb_cnt_minus1),
+ arraysize(current_sps_.bit_rate_value_minus1));
+ for (int i = 0; i <= current_sps_.cpb_cnt_minus1; ++i) {
+ packed_sps_.AppendUE(current_sps_.bit_rate_value_minus1[i]);
+ packed_sps_.AppendUE(current_sps_.cpb_size_value_minus1[i]);
+ packed_sps_.AppendBool(current_sps_.cbr_flag[i]);
+ }
+ packed_sps_.AppendBits(
+ 5, current_sps_.initial_cpb_removal_delay_length_minus_1);
+ packed_sps_.AppendBits(5, current_sps_.cpb_removal_delay_length_minus1);
+ packed_sps_.AppendBits(5, current_sps_.dpb_output_delay_length_minus1);
+ packed_sps_.AppendBits(5, current_sps_.time_offset_length);
+ }
+
+ packed_sps_.AppendBool(false); // vcl_hrd_parameters_flag
+ if (current_sps_.nal_hrd_parameters_present_flag)
+ packed_sps_.AppendBool(current_sps_.low_delay_hrd_flag);
+
+ packed_sps_.AppendBool(false); // pic_struct_present_flag
+ packed_sps_.AppendBool(false); // bitstream_restriction_flag
+ }
+
+ packed_sps_.FinishNALU();
+}
+
+void VaapiVideoEncodeAccelerator::UpdatePPS() {
+ memset(&current_pps_, 0, sizeof(media::H264PPS));
+
+ current_pps_.seq_parameter_set_id = current_sps_.seq_parameter_set_id;
+ current_pps_.pic_parameter_set_id = 0;
+
+ current_pps_.entropy_coding_mode_flag =
+ current_sps_.profile_idc >= media::H264SPS::kProfileIDCMain;
+
+ CHECK_GT(max_ref_idx_l0_size_, 0u);
+ current_pps_.num_ref_idx_l0_default_active_minus1 = max_ref_idx_l0_size_ - 1;
+ current_pps_.num_ref_idx_l1_default_active_minus1 = 0;
+ DCHECK_LE(qp_, 51u);
+ current_pps_.pic_init_qp_minus26 = qp_ - 26;
+ current_pps_.deblocking_filter_control_present_flag = true;
+ current_pps_.transform_8x8_mode_flag =
+ (current_sps_.profile_idc == media::H264SPS::kProfileIDCHigh);
+}
+
+void VaapiVideoEncodeAccelerator::GeneratePackedPPS() {
+ packed_pps_.Reset();
+
+ packed_pps_.BeginNALU(media::H264NALU::kPPS, 3);
+
+ packed_pps_.AppendUE(current_pps_.pic_parameter_set_id);
+ packed_pps_.AppendUE(current_pps_.seq_parameter_set_id);
+ packed_pps_.AppendBool(current_pps_.entropy_coding_mode_flag);
+ packed_pps_.AppendBool(
+ current_pps_.bottom_field_pic_order_in_frame_present_flag);
+ CHECK_EQ(current_pps_.num_slice_groups_minus1, 0);
+ packed_pps_.AppendUE(current_pps_.num_slice_groups_minus1);
+
+ packed_pps_.AppendUE(current_pps_.num_ref_idx_l0_default_active_minus1);
+ packed_pps_.AppendUE(current_pps_.num_ref_idx_l1_default_active_minus1);
+
+ packed_pps_.AppendBool(current_pps_.weighted_pred_flag);
+ packed_pps_.AppendBits(2, current_pps_.weighted_bipred_idc);
+
+ packed_pps_.AppendSE(current_pps_.pic_init_qp_minus26);
+ packed_pps_.AppendSE(current_pps_.pic_init_qs_minus26);
+ packed_pps_.AppendSE(current_pps_.chroma_qp_index_offset);
+
+ packed_pps_.AppendBool(current_pps_.deblocking_filter_control_present_flag);
+ packed_pps_.AppendBool(current_pps_.constrained_intra_pred_flag);
+ packed_pps_.AppendBool(current_pps_.redundant_pic_cnt_present_flag);
+
+ packed_pps_.AppendBool(current_pps_.transform_8x8_mode_flag);
+ packed_pps_.AppendBool(current_pps_.pic_scaling_matrix_present_flag);
+ DCHECK(!current_pps_.pic_scaling_matrix_present_flag);
+ packed_pps_.AppendSE(current_pps_.second_chroma_qp_index_offset);
+
+ packed_pps_.FinishNALU();
+}
+
+void VaapiVideoEncodeAccelerator::SetState(State state) {
+ // Only touch state on encoder thread, unless it's not running.
+ if (encoder_thread_.IsRunning() &&
+ !encoder_thread_proxy_->BelongsToCurrentThread()) {
+ encoder_thread_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&VaapiVideoEncodeAccelerator::SetState,
+ base::Unretained(this),
+ state));
+ return;
+ }
+
+ DVLOGF(1) << "setting state to: " << state;
+ state_ = state;
+}
+
+void VaapiVideoEncodeAccelerator::NotifyError(Error error) {
+ if (!child_message_loop_proxy_->BelongsToCurrentThread()) {
+ child_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &VaapiVideoEncodeAccelerator::NotifyError, weak_this_, error));
+ return;
+ }
+
+ if (client_) {
+ client_->NotifyError(error);
+ client_ptr_factory_.reset();
+ }
+}
+
+VaapiVideoEncodeAccelerator::EncodeJob::EncodeJob()
+ : coded_buffer(VA_INVALID_ID), keyframe(false) {
+}
+
+VaapiVideoEncodeAccelerator::EncodeJob::~EncodeJob() {
+}
+
+} // namespace content
diff --git a/content/common/gpu/media/vaapi_video_encode_accelerator.h b/content/common/gpu/media/vaapi_video_encode_accelerator.h
new file mode 100644
index 0000000..8a7811a
--- /dev/null
+++ b/content/common/gpu/media/vaapi_video_encode_accelerator.h
@@ -0,0 +1,264 @@
+// Copyright 2014 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 CONTENT_COMMON_GPU_MEDIA_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_
+#define CONTENT_COMMON_GPU_MEDIA_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_
+
+#include <list>
+#include <queue>
+
+#include "base/memory/linked_ptr.h"
+#include "base/threading/thread.h"
+#include "content/common/content_export.h"
+#include "content/common/gpu/media/h264_dpb.h"
+#include "content/common/gpu/media/va_surface.h"
+#include "content/common/gpu/media/vaapi_wrapper.h"
+#include "media/filters/h264_bitstream_buffer.h"
+#include "media/video/video_encode_accelerator.h"
+
+namespace content {
+
+// A VideoEncodeAccelerator implementation that uses VA-API
+// (http://www.freedesktop.org/wiki/Software/vaapi) for HW-accelerated
+// video encode.
+class CONTENT_EXPORT VaapiVideoEncodeAccelerator
+ : public media::VideoEncodeAccelerator {
+ public:
+ explicit VaapiVideoEncodeAccelerator(Display* x_display);
+ virtual ~VaapiVideoEncodeAccelerator();
+
+ // media::VideoEncodeAccelerator implementation.
+ virtual bool Initialize(media::VideoFrame::Format format,
+ const gfx::Size& input_visible_size,
+ media::VideoCodecProfile output_profile,
+ uint32 initial_bitrate,
+ Client* client) OVERRIDE;
+ virtual void Encode(const scoped_refptr<media::VideoFrame>& frame,
+ bool force_keyframe) OVERRIDE;
+ virtual void UseOutputBitstreamBuffer(
+ const media::BitstreamBuffer& buffer) OVERRIDE;
+ virtual void RequestEncodingParametersChange(uint32 bitrate,
+ uint32 framerate) OVERRIDE;
+ virtual void Destroy() OVERRIDE;
+
+ static std::vector<media::VideoEncodeAccelerator::SupportedProfile>
+ GetSupportedProfiles();
+
+ private:
+ // Reference picture list.
+ typedef std::list<scoped_refptr<VASurface> > RefPicList;
+
+ // Encode job for one frame. Created when an input frame is awaiting and
+ // enough resources are available to proceed. Once the job is prepared and
+ // submitted to the hardware, it awaits on the submitted_encode_jobs_ queue
+ // for an output bitstream buffer to become available. Once one is ready,
+ // the encoded bytes are downloaded to it and job resources are released
+ // and become available for reuse.
+ struct EncodeJob {
+ // Input surface for video frame data.
+ scoped_refptr<VASurface> input_surface;
+ // Surface for a reconstructed picture, which is used for reference
+ // for subsequent frames.
+ scoped_refptr<VASurface> recon_surface;
+ // Buffer that will contain output bitstream for this frame.
+ VABufferID coded_buffer;
+ // Reference surfaces required to encode this picture. We keep references
+ // to them here, because we may discard some of them from ref_pic_list*
+ // before the HW job is done.
+ RefPicList reference_surfaces;
+ // True if this job will produce a keyframe. Used to report
+ // to BitstreamBufferReady().
+ bool keyframe;
+
+ EncodeJob();
+ ~EncodeJob();
+ };
+
+ // Encoder state.
+ enum State {
+ kUninitialized,
+ kEncoding,
+ kError,
+ };
+
+ // Holds input frames coming from the client ready to be encoded.
+ struct InputFrameRef;
+ // Holds output buffers coming from the client ready to be filled.
+ struct BitstreamBufferRef;
+
+ // Tasks for each of the VEA interface calls to be executed on the
+ // encoder thread.
+ void InitializeTask();
+ void EncodeTask(const scoped_refptr<media::VideoFrame>& frame,
+ bool force_keyframe);
+ void UseOutputBitstreamBufferTask(scoped_ptr<BitstreamBufferRef> buffer_ref);
+ void RequestEncodingParametersChangeTask(uint32 bitrate, uint32 framerate);
+ void DestroyTask();
+
+ // Prepare and schedule an encode job if we have an input to encode
+ // and enough resources to proceed.
+ void EncodeFrameTask();
+
+ // Fill current_sps_/current_pps_ with current values.
+ void UpdateSPS();
+ void UpdatePPS();
+ void UpdateRates(uint32 bitrate, uint32 framerate);
+
+ // Generate packed SPS and PPS in packed_sps_/packed_pps_, using
+ // values in current_sps_/current_pps_.
+ void GeneratePackedSPS();
+ void GeneratePackedPPS();
+
+ // Check if we have sufficient resources for a new encode job, claim them and
+ // fill current_encode_job_ with them.
+ // Return false if we cannot start a new job yet, true otherwise.
+ bool PrepareNextJob();
+
+ // Begin a new frame, making it a keyframe if |force_keyframe| is true,
+ // updating current_pic_.
+ void BeginFrame(bool force_keyframe);
+
+ // End current frame, updating reference picture lists and storing current
+ // job in the jobs awaiting completion on submitted_encode_jobs_.
+ void EndFrame();
+
+ // Submit parameters for the current frame to the hardware.
+ bool SubmitFrameParameters();
+ // Submit keyframe headers to the hardware if the current frame is a keyframe.
+ bool SubmitHeadersIfNeeded();
+
+ // Upload image data from |frame| to the input surface for current job.
+ bool UploadFrame(const scoped_refptr<media::VideoFrame>& frame);
+
+ // Execute encode in hardware. This does not block and will return before
+ // the job is finished.
+ bool ExecuteEncode();
+
+ // Callback that returns a no longer used VASurfaceID to
+ // available_va_surface_ids_ for reuse.
+ void RecycleVASurfaceID(VASurfaceID va_surface_id);
+
+ // Tries to return a bitstream buffer if both a submitted job awaits to
+ // be completed and we have bitstream buffers from the client available
+ // to download the encoded data to.
+ void TryToReturnBitstreamBuffer();
+
+ // Puts the encoder into en error state and notifies client about the error.
+ void NotifyError(Error error);
+
+ // Sets the encoder state on the correct thread.
+ void SetState(State state);
+
+ // VaapiWrapper is the owner of all HW resources (surfaces and buffers)
+ // and will free them on destruction.
+ scoped_ptr<VaapiWrapper> vaapi_wrapper_;
+
+ // Input profile and sizes.
+ media::VideoCodecProfile profile_;
+ gfx::Size visible_size_;
+ gfx::Size coded_size_; // Macroblock-aligned.
+ // Width/height in macroblocks.
+ unsigned int mb_width_;
+ unsigned int mb_height_;
+
+ // Maximum size of the reference list 0.
+ unsigned int max_ref_idx_l0_size_;
+
+ // Initial QP.
+ unsigned int qp_;
+
+ // IDR frame period.
+ unsigned int idr_period_;
+ // I frame period.
+ unsigned int i_period_;
+ // IP period, i.e. how often do we need to have either an I or a P frame in
+ // the stream. Period of 1 means we can have no B frames.
+ unsigned int ip_period_;
+
+ // Size in bytes required for input bitstream buffers.
+ size_t output_buffer_byte_size_;
+
+ Display* x_display_;
+
+ // All of the members below must be accessed on the encoder_thread_,
+ // while it is running.
+
+ // Encoder state. Encode tasks will only run in kEncoding state.
+ State state_;
+
+ // frame_num to be used for the next frame.
+ unsigned int frame_num_;
+ // frame_num of the previous IDR.
+ unsigned int last_idr_frame_num_;
+
+ // Current bitrate in bps.
+ unsigned int bitrate_;
+ // Current fps.
+ unsigned int framerate_;
+ // CPB size in bits, i.e. bitrate in kbps * window size in ms/1000.
+ unsigned int cpb_size_;
+ // True if the parameters have changed and we need to submit a keyframe
+ // with updated parameters.
+ bool encoding_parameters_changed_;
+
+ // Job currently being prepared for encode.
+ scoped_ptr<EncodeJob> current_encode_job_;
+
+ // Current SPS, PPS and their packed versions. Packed versions are their NALUs
+ // in AnnexB format *without* emulation prevention three-byte sequences
+ // (those will be added by the driver).
+ media::H264SPS current_sps_;
+ media::H264BitstreamBuffer packed_sps_;
+ media::H264PPS current_pps_;
+ media::H264BitstreamBuffer packed_pps_;
+
+ // Picture currently being prepared for encode.
+ H264Picture current_pic_;
+
+ // VA surfaces available for reuse.
+ std::vector<VASurfaceID> available_va_surface_ids_;
+
+ // VA buffers for coded frames.
+ std::vector<VABufferID> available_va_buffer_ids_;
+
+ // Currently active reference surfaces.
+ RefPicList ref_pic_list0_;
+
+ // Callback via which finished VA surfaces are returned to us.
+ VASurface::ReleaseCB va_surface_release_cb_;
+
+ // VideoFrames passed from the client, waiting to be encoded.
+ std::queue<linked_ptr<InputFrameRef> > encoder_input_queue_;
+
+ // BitstreamBuffers mapped, ready to be filled.
+ std::queue<linked_ptr<BitstreamBufferRef> > available_bitstream_buffers_;
+
+ // Jobs submitted for encode, awaiting bitstream buffers to become available.
+ std::queue<linked_ptr<EncodeJob> > submitted_encode_jobs_;
+
+ // Encoder thread. All tasks are executed on it.
+ base::Thread encoder_thread_;
+ scoped_refptr<base::MessageLoopProxy> encoder_thread_proxy_;
+
+ const scoped_refptr<base::MessageLoopProxy> child_message_loop_proxy_;
+
+ // To expose client callbacks from VideoEncodeAccelerator.
+ // NOTE: all calls to these objects *MUST* be executed on
+ // child_message_loop_proxy_.
+ scoped_ptr<base::WeakPtrFactory<Client> > client_ptr_factory_;
+ base::WeakPtr<Client> client_;
+
+ // WeakPtr to post from the encoder thread back to the ChildThread, as it may
+ // outlive this. Posting from the ChildThread using base::Unretained(this)
+ // to the encoder thread is safe, because |this| always outlives the encoder
+ // thread (it's a member of this class).
+ base::WeakPtr<VaapiVideoEncodeAccelerator> weak_this_;
+ base::WeakPtrFactory<VaapiVideoEncodeAccelerator> weak_this_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(VaapiVideoEncodeAccelerator);
+};
+
+} // namespace content
+
+#endif // CONTENT_COMMON_GPU_MEDIA_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_
diff --git a/content/common/gpu/media/vaapi_wrapper.cc b/content/common/gpu/media/vaapi_wrapper.cc
index 5250e59..5e93b99 100644
--- a/content/common/gpu/media/vaapi_wrapper.cc
+++ b/content/common/gpu/media/vaapi_wrapper.cc
@@ -7,10 +7,12 @@
#include <dlfcn.h>
#include "base/bind.h"
+#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
// Auto-generated for dlopen libva libraries
#include "content/common/gpu/media/va_stubs.h"
+#include "third_party/libyuv/include/libyuv.h"
using content_common_gpu_media::kModuleVa;
using content_common_gpu_media::InitializeStubs;
@@ -43,6 +45,18 @@ static const base::FilePath::CharType kVaLib[] =
namespace content {
+// Config attributes common for both encode and decode.
+static const VAConfigAttrib kCommonVAConfigAttribs[] = {
+ {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420},
+};
+
+// Attributes required for encode.
+static const VAConfigAttrib kEncodeVAConfigAttribs[] = {
+ {VAConfigAttribRateControl, VA_RC_CBR},
+ {VAConfigAttribEncPackedHeaders,
+ VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE},
+};
+
// Maps Profile enum values to VaProfile values.
static VAProfile ProfileToVAProfile(
media::VideoCodecProfile profile,
@@ -106,17 +120,20 @@ VaapiWrapper::VaapiWrapper()
VaapiWrapper::~VaapiWrapper() {
DestroyPendingBuffers();
+ DestroyCodedBuffers();
DestroySurfaces();
Deinitialize();
}
scoped_ptr<VaapiWrapper> VaapiWrapper::Create(
+ CodecMode mode,
media::VideoCodecProfile profile,
Display* x_display,
const base::Closure& report_error_to_uma_cb) {
scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper());
- if (!vaapi_wrapper->Initialize(profile, x_display, report_error_to_uma_cb))
+ if (!vaapi_wrapper->Initialize(
+ mode, profile, x_display, report_error_to_uma_cb))
vaapi_wrapper.reset();
return vaapi_wrapper.Pass();
@@ -134,7 +151,8 @@ void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() {
DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default.";
}
-bool VaapiWrapper::Initialize(media::VideoCodecProfile profile,
+bool VaapiWrapper::Initialize(CodecMode mode,
+ media::VideoCodecProfile profile,
Display* x_display,
const base::Closure& report_error_to_uma_cb) {
static bool vaapi_functions_initialized = PostSandboxInitialization();
@@ -184,21 +202,73 @@ bool VaapiWrapper::Initialize(media::VideoCodecProfile profile,
return false;
}
- VAConfigAttrib attrib = {VAConfigAttribRTFormat, 0};
- const VAEntrypoint kEntrypoint = VAEntrypointVLD;
- va_res = vaGetConfigAttributes(va_display_, va_profile, kEntrypoint,
- &attrib, 1);
- VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false);
+ // Query the driver for supported entrypoints.
+ int max_entrypoints = vaMaxNumEntrypoints(va_display_);
+ std::vector<VAEntrypoint> supported_entrypoints(
+ base::checked_cast<size_t>(max_entrypoints));
+
+ int num_supported_entrypoints;
+ va_res = vaQueryConfigEntrypoints(va_display_,
+ va_profile,
+ &supported_entrypoints[0],
+ &num_supported_entrypoints);
+ VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigEntrypoints failed", false);
+ if (num_supported_entrypoints < 0 ||
+ num_supported_entrypoints > max_entrypoints) {
+ DVLOG(1) << "vaQueryConfigEntrypoints returned: "
+ << num_supported_entrypoints;
+ return false;
+ }
- if (!(attrib.value & VA_RT_FORMAT_YUV420)) {
- DVLOG(1) << "YUV420 not supported by this VAAPI implementation";
+ VAEntrypoint entrypoint =
+ (mode == kEncode ? VAEntrypointEncSlice : VAEntrypointVLD);
+
+ if (std::find(supported_entrypoints.begin(),
+ supported_entrypoints.end(),
+ entrypoint) == supported_entrypoints.end()) {
+ DVLOG(1) << "Unsupported entrypoint";
return false;
}
+ // Query the driver for required attributes.
+ std::vector<VAConfigAttrib> required_attribs;
+ required_attribs.insert(
+ required_attribs.end(),
+ kCommonVAConfigAttribs,
+ kCommonVAConfigAttribs + arraysize(kCommonVAConfigAttribs));
+ if (mode == kEncode) {
+ required_attribs.insert(
+ required_attribs.end(),
+ kEncodeVAConfigAttribs,
+ kEncodeVAConfigAttribs + arraysize(kEncodeVAConfigAttribs));
+ }
+
+ std::vector<VAConfigAttrib> attribs = required_attribs;
+ for (size_t i = 0; i < required_attribs.size(); ++i)
+ attribs[i].value = 0;
+
+ va_res = vaGetConfigAttributes(
+ va_display_, va_profile, entrypoint, &attribs[0], attribs.size());
+ VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false);
+
+ for (size_t i = 0; i < required_attribs.size(); ++i) {
+ if (attribs[i].type != required_attribs[i].type ||
+ (attribs[i].value & required_attribs[i].value) !=
+ required_attribs[i].value) {
+ DVLOG(1) << "Unsupported value " << required_attribs[i].value
+ << " for attribute type " << required_attribs[i].type;
+ return false;
+ }
+ }
+
TryToSetVADisplayAttributeToLocalGPU();
- va_res = vaCreateConfig(va_display_, va_profile, kEntrypoint,
- &attrib, 1, &va_config_id_);
+ va_res = vaCreateConfig(va_display_,
+ va_profile,
+ entrypoint,
+ &required_attribs[0],
+ required_attribs.size(),
+ &va_config_id_);
VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false);
return true;
@@ -299,6 +369,7 @@ bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type,
switch (va_buffer_type) {
case VASliceParameterBufferType:
case VASliceDataBufferType:
+ case VAEncSliceParameterBufferType:
pending_slice_bufs_.push_back(buffer_id);
break;
@@ -310,6 +381,43 @@ bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type,
return true;
}
+bool VaapiWrapper::SubmitVAEncMiscParamBuffer(
+ VAEncMiscParameterType misc_param_type,
+ size_t size,
+ void* buffer) {
+ base::AutoLock auto_lock(va_lock_);
+
+ VABufferID buffer_id;
+ VAStatus va_res = vaCreateBuffer(va_display_,
+ va_context_id_,
+ VAEncMiscParameterBufferType,
+ sizeof(VAEncMiscParameterBuffer) + size,
+ 1,
+ NULL,
+ &buffer_id);
+ VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false);
+
+ void* data_ptr = NULL;
+ va_res = vaMapBuffer(va_display_, buffer_id, &data_ptr);
+ VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed");
+ if (va_res != VA_STATUS_SUCCESS) {
+ vaDestroyBuffer(va_display_, buffer_id);
+ return false;
+ }
+
+ DCHECK(data_ptr);
+
+ VAEncMiscParameterBuffer* misc_param =
+ reinterpret_cast<VAEncMiscParameterBuffer*>(data_ptr);
+ misc_param->type = misc_param_type;
+ memcpy(misc_param->data, buffer, size);
+ va_res = vaUnmapBuffer(va_display_, buffer_id);
+ VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
+
+ pending_va_bufs_.push_back(buffer_id);
+ return true;
+}
+
void VaapiWrapper::DestroyPendingBuffers() {
base::AutoLock auto_lock(va_lock_);
@@ -327,38 +435,73 @@ void VaapiWrapper::DestroyPendingBuffers() {
pending_slice_bufs_.clear();
}
-bool VaapiWrapper::SubmitDecode(VASurfaceID va_surface_id) {
+bool VaapiWrapper::CreateCodedBuffer(size_t size, VABufferID* buffer_id) {
+ base::AutoLock auto_lock(va_lock_);
+ VAStatus va_res = vaCreateBuffer(va_display_,
+ va_context_id_,
+ VAEncCodedBufferType,
+ size,
+ 1,
+ NULL,
+ buffer_id);
+ VA_SUCCESS_OR_RETURN(va_res, "Failed to create a coded buffer", false);
+
+ DCHECK(coded_buffers_.insert(*buffer_id).second);
+ return true;
+}
+
+void VaapiWrapper::DestroyCodedBuffers() {
+ base::AutoLock auto_lock(va_lock_);
+
+ for (std::set<VABufferID>::const_iterator iter = coded_buffers_.begin();
+ iter != coded_buffers_.end();
+ ++iter) {
+ VAStatus va_res = vaDestroyBuffer(va_display_, *iter);
+ VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
+ }
+
+ coded_buffers_.clear();
+}
+
+bool VaapiWrapper::Execute(VASurfaceID va_surface_id) {
base::AutoLock auto_lock(va_lock_);
DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size();
DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size();
- DVLOG(4) << "Decoding into VA surface " << va_surface_id;
+ DVLOG(4) << "Target VA surface " << va_surface_id;
- // Get ready to decode into surface.
+ // Get ready to execute for given surface.
VAStatus va_res = vaBeginPicture(va_display_, va_context_id_,
va_surface_id);
VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false);
- // Commit parameter and slice buffers.
- va_res = vaRenderPicture(va_display_, va_context_id_,
- &pending_va_bufs_[0], pending_va_bufs_.size());
- VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false);
+ if (pending_va_bufs_.size() > 0) {
+ // Commit parameter and slice buffers.
+ va_res = vaRenderPicture(va_display_,
+ va_context_id_,
+ &pending_va_bufs_[0],
+ pending_va_bufs_.size());
+ VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false);
+ }
- va_res = vaRenderPicture(va_display_, va_context_id_,
- &pending_slice_bufs_[0],
- pending_slice_bufs_.size());
- VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false);
+ if (pending_slice_bufs_.size() > 0) {
+ va_res = vaRenderPicture(va_display_,
+ va_context_id_,
+ &pending_slice_bufs_[0],
+ pending_slice_bufs_.size());
+ VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false);
+ }
- // Instruct HW decoder to start processing committed buffers (decode this
- // picture). This does not block until the end of decode.
+ // Instruct HW codec to start processing committed buffers.
+ // Does not block and the job is not finished after this returns.
va_res = vaEndPicture(va_display_, va_context_id_);
VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false);
return true;
}
-bool VaapiWrapper::DecodeAndDestroyPendingBuffers(VASurfaceID va_surface_id) {
- bool result = SubmitDecode(va_surface_id);
+bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) {
+ bool result = Execute(va_surface_id);
DestroyPendingBuffers();
return result;
}
@@ -378,8 +521,7 @@ bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id,
0, 0, dest_size.width(), dest_size.height(),
0, 0, dest_size.width(), dest_size.height(),
NULL, 0, 0);
- VA_SUCCESS_OR_RETURN(va_res, "Failed putting decode surface to pixmap",
- false);
+ VA_SUCCESS_OR_RETURN(va_res, "Failed putting surface to pixmap", false);
return true;
}
@@ -403,15 +545,124 @@ bool VaapiWrapper::GetVaImageForTesting(VASurfaceID va_surface_id,
if (va_res == VA_STATUS_SUCCESS)
return true;
- vaDestroyImage(va_display_, image->image_id);
+ va_res = vaDestroyImage(va_display_, image->image_id);
+ VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed");
+
return false;
}
void VaapiWrapper::ReturnVaImageForTesting(VAImage* image) {
base::AutoLock auto_lock(va_lock_);
- vaUnmapBuffer(va_display_, image->buf);
- vaDestroyImage(va_display_, image->image_id);
+ VAStatus va_res = vaUnmapBuffer(va_display_, image->buf);
+ VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
+
+ va_res = vaDestroyImage(va_display_, image->image_id);
+ VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed");
+}
+
+static void DestroyVAImage(VADisplay va_display, VAImage image) {
+ if (image.image_id != VA_INVALID_ID)
+ vaDestroyImage(va_display, image.image_id);
+}
+
+bool VaapiWrapper::UploadVideoFrameToSurface(
+ const scoped_refptr<media::VideoFrame>& frame,
+ VASurfaceID va_surface_id) {
+ base::AutoLock auto_lock(va_lock_);
+
+ VAImage image;
+ VAStatus va_res = vaDeriveImage(va_display_, va_surface_id, &image);
+ VA_SUCCESS_OR_RETURN(va_res, "vaDeriveImage failed", false);
+ base::ScopedClosureRunner vaimage_deleter(
+ base::Bind(&DestroyVAImage, va_display_, image));
+
+ if (image.format.fourcc != VA_FOURCC_NV12) {
+ DVLOG(1) << "Unsupported image format: " << image.format.fourcc;
+ return false;
+ }
+
+ if (gfx::Rect(image.width, image.height) < gfx::Rect(frame->coded_size())) {
+ DVLOG(1) << "Buffer too small to fit the frame.";
+ return false;
+ }
+
+ void* image_ptr = NULL;
+ va_res = vaMapBuffer(va_display_, image.buf, &image_ptr);
+ VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false);
+ DCHECK(image_ptr);
+
+ int ret = 0;
+ {
+ base::AutoUnlock auto_unlock(va_lock_);
+ ret = libyuv::I420ToNV12(frame->data(media::VideoFrame::kYPlane),
+ frame->stride(media::VideoFrame::kYPlane),
+ frame->data(media::VideoFrame::kUPlane),
+ frame->stride(media::VideoFrame::kUPlane),
+ frame->data(media::VideoFrame::kVPlane),
+ frame->stride(media::VideoFrame::kVPlane),
+ static_cast<uint8*>(image_ptr) + image.offsets[0],
+ image.pitches[0],
+ static_cast<uint8*>(image_ptr) + image.offsets[1],
+ image.pitches[1],
+ image.width,
+ image.height);
+ }
+
+ va_res = vaUnmapBuffer(va_display_, image.buf);
+ VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
+
+ return ret == 0;
+}
+
+bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id,
+ VASurfaceID sync_surface_id,
+ uint8* target_ptr,
+ size_t target_size,
+ size_t* coded_data_size) {
+ base::AutoLock auto_lock(va_lock_);
+
+ VAStatus va_res = vaSyncSurface(va_display_, sync_surface_id);
+ VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
+
+ VACodedBufferSegment* buffer_segment = NULL;
+ va_res = vaMapBuffer(
+ va_display_, buffer_id, reinterpret_cast<void**>(&buffer_segment));
+ VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false);
+ DCHECK(target_ptr);
+
+ {
+ base::AutoUnlock auto_unlock(va_lock_);
+ *coded_data_size = 0;
+
+ while (buffer_segment) {
+ DCHECK(buffer_segment->buf);
+
+ if (buffer_segment->size > target_size) {
+ DVLOG(1) << "Insufficient output buffer size";
+ break;
+ }
+
+ memcpy(target_ptr, buffer_segment->buf, buffer_segment->size);
+
+ target_ptr += buffer_segment->size;
+ *coded_data_size += buffer_segment->size;
+ target_size -= buffer_segment->size;
+
+ buffer_segment =
+ reinterpret_cast<VACodedBufferSegment*>(buffer_segment->next);
+ }
+ }
+
+ va_res = vaUnmapBuffer(va_display_, buffer_id);
+ VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
+
+ va_res = vaDestroyBuffer(va_display_, buffer_id);
+ VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
+
+ DCHECK(coded_buffers_.erase(buffer_id));
+
+ return buffer_segment == NULL;
}
// static
diff --git a/content/common/gpu/media/vaapi_wrapper.h b/content/common/gpu/media/vaapi_wrapper.h
index 5570360..f600cdf 100644
--- a/content/common/gpu/media/vaapi_wrapper.h
+++ b/content/common/gpu/media/vaapi_wrapper.h
@@ -3,12 +3,16 @@
// found in the LICENSE file.
//
// This file contains an implementation of VaapiWrapper, used by
-// VaapiVideoDecodeAccelerator and VaapiH264Decoder to interface
-// with libva (VA-API library for hardware video decode).
+// VaapiVideoDecodeAccelerator and VaapiH264Decoder for decode,
+// and VaapiVideoEncodeAccelerator for encode, to interface
+// with libva (VA-API library for hardware video codec).
#ifndef CONTENT_COMMON_GPU_MEDIA_VAAPI_WRAPPER_H_
#define CONTENT_COMMON_GPU_MEDIA_VAAPI_WRAPPER_H_
+#include <set>
+#include <vector>
+
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
@@ -22,20 +26,26 @@
namespace content {
// This class handles VA-API calls and ensures proper locking of VA-API calls
-// to libva, the userspace shim to the HW decoder driver. libva is not
+// to libva, the userspace shim to the HW codec driver. libva is not
// thread-safe, so we have to perform locking ourselves. This class is fully
// synchronous and its methods can be called from any thread and may wait on
// the va_lock_ while other, concurrent calls run.
//
// This class is responsible for managing VAAPI connection, contexts and state.
// It is also responsible for managing and freeing VABuffers (not VASurfaces),
-// which are used to queue decode parameters and slice data to the HW decoder,
+// which are used to queue parameters and slice data to the HW codec,
// as well as underlying memory for VASurfaces themselves.
class CONTENT_EXPORT VaapiWrapper {
public:
+ enum CodecMode {
+ kDecode,
+ kEncode,
+ };
+
// |report_error_to_uma_cb| will be called independently from reporting
// errors to clients via method return values.
static scoped_ptr<VaapiWrapper> Create(
+ CodecMode mode,
media::VideoCodecProfile profile,
Display* x_display,
const base::Closure& report_error_to_uma_cb);
@@ -57,20 +67,30 @@ class CONTENT_EXPORT VaapiWrapper {
void DestroySurfaces();
// Submit parameters or slice data of |va_buffer_type|, copying them from
- // |buffer| of size |size|, into HW decoder. The data in |buffer| is no
+ // |buffer| of size |size|, into HW codec. The data in |buffer| is no
// longer needed and can be freed after this method returns.
- // Data submitted via this method awaits in the HW decoder until
- // DecodeAndDestroyPendingBuffers is called to execute or
- // DestroyPendingBuffers is used to cancel a pending decode.
+ // Data submitted via this method awaits in the HW codec until
+ // ExecuteAndDestroyPendingBuffers() is called to execute or
+ // DestroyPendingBuffers() is used to cancel a pending job.
bool SubmitBuffer(VABufferType va_buffer_type, size_t size, void* buffer);
- // Cancel and destroy all buffers queued to the HW decoder via SubmitBuffer.
- // Useful when a pending decode is to be cancelled (on reset or error).
+ // Submit a VAEncMiscParameterBuffer of given |misc_param_type|, copying its
+ // data from |buffer| of size |size|, into HW codec. The data in |buffer| is
+ // no longer needed and can be freed after this method returns.
+ // Data submitted via this method awaits in the HW codec until
+ // ExecuteAndDestroyPendingBuffers() is called to execute or
+ // DestroyPendingBuffers() is used to cancel a pending job.
+ bool SubmitVAEncMiscParamBuffer(VAEncMiscParameterType misc_param_type,
+ size_t size,
+ void* buffer);
+
+ // Cancel and destroy all buffers queued to the HW codec via SubmitBuffer().
+ // Useful when a pending job is to be cancelled (on reset or error).
void DestroyPendingBuffers();
- // Execute decode in hardware into |va_surface_id} and destroy pending
- // buffers. Return false if SubmitDecode() fails.
- bool DecodeAndDestroyPendingBuffers(VASurfaceID va_surface_id);
+ // Execute job in hardware on target |va_surface_id| and destroy pending
+ // buffers. Return false if Execute() fails.
+ bool ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id);
// Put data from |va_surface_id| into |x_pixmap| of size |size|,
// converting/scaling to it.
@@ -92,18 +112,41 @@ class CONTENT_EXPORT VaapiWrapper {
// GetVaImage(). This is intended for testing only.
void ReturnVaImageForTesting(VAImage* image);
+ // Upload contents of |frame| into |va_surface_id| for encode.
+ bool UploadVideoFrameToSurface(const scoped_refptr<media::VideoFrame>& frame,
+ VASurfaceID va_surface_id);
+
+ // Create a buffer of |size| bytes to be used as encode output.
+ bool CreateCodedBuffer(size_t size, VABufferID* buffer_id);
+
+ // Download the contents of the buffer with given |buffer_id| into a buffer of
+ // size |target_size|, pointed to by |target_ptr|. The number of bytes
+ // downloaded will be returned in |coded_data_size|. |sync_surface_id| will
+ // be used as a sync point, i.e. it will have to become idle before starting
+ // the download. |sync_surface_id| should be the source surface passed
+ // to the encode job.
+ bool DownloadAndDestroyCodedBuffer(VABufferID buffer_id,
+ VASurfaceID sync_surface_id,
+ uint8* target_ptr,
+ size_t target_size,
+ size_t* coded_data_size);
+
+ // Destroy all previously-allocated (and not yet destroyed) coded buffers.
+ void DestroyCodedBuffers();
+
private:
VaapiWrapper();
- bool Initialize(media::VideoCodecProfile profile,
+ bool Initialize(CodecMode mode,
+ media::VideoCodecProfile profile,
Display* x_display,
const base::Closure& report_error__to_uma_cb);
void Deinitialize();
- // Execute decode in hardware and destroy pending buffers. Return false if
- // vaapi driver refuses to accept parameter or slice buffers submitted
- // by client or if decode fails in hardware.
- bool SubmitDecode(VASurfaceID va_surface_id);
+ // Execute pending job in hardware and destroy pending buffers. Return false
+ // if vaapi driver refuses to accept parameter or slice buffers submitted
+ // by client, or if execution fails in hardware.
+ bool Execute(VASurfaceID va_surface_id);
// Attempt to set render mode to "render to texture.". Failure is non-fatal.
void TryToSetVADisplayAttributeToLocalGPU();
@@ -114,7 +157,7 @@ class CONTENT_EXPORT VaapiWrapper {
// Libva is not thread safe, so we have to do locking for it ourselves.
// This lock is to be taken for the duration of all VA-API calls and for
- // the entire decode execution sequence in DecodeAndDestroyPendingBuffers().
+ // the entire job submission sequence in ExecuteAndDestroyPendingBuffers().
base::Lock va_lock_;
// Allocated ids for VASurfaces.
@@ -131,11 +174,14 @@ class CONTENT_EXPORT VaapiWrapper {
// valid until DestroySurfaces().
VAContextID va_context_id_;
- // Data queued up for HW decoder, to be committed on next HW decode.
+ // Data queued up for HW codec, to be committed on next execution.
std::vector<VABufferID> pending_slice_bufs_;
std::vector<VABufferID> pending_va_bufs_;
- // Called to report decoding errors to UMA. Errors to clients are reported via
+ // Bitstream buffers for encode.
+ std::set<VABufferID> coded_buffers_;
+
+ // Called to report codec errors to UMA. Errors to clients are reported via
// return values from public methods.
base::Closure report_error_to_uma_cb_;
diff --git a/content/common/gpu/media/video_encode_accelerator_unittest.cc b/content/common/gpu/media/video_encode_accelerator_unittest.cc
index 5f60634..b07211d 100644
--- a/content/common/gpu/media/video_encode_accelerator_unittest.cc
+++ b/content/common/gpu/media/video_encode_accelerator_unittest.cc
@@ -13,7 +13,6 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/time/time.h"
-#include "content/common/gpu/media/v4l2_video_encode_accelerator.h"
#include "content/common/gpu/media/video_accelerator_unittest_helpers.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/bitstream_buffer.h"
@@ -22,6 +21,18 @@
#include "media/video/video_encode_accelerator.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if defined(USE_X11)
+#include "ui/gfx/x/x11_types.h"
+#endif
+
+#if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL)
+#include "content/common/gpu/media/v4l2_video_encode_accelerator.h"
+#elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11)
+#include "content/common/gpu/media/vaapi_video_encode_accelerator.h"
+#else
+#error The VideoEncodeAcceleratorUnittest is not supported on this platform.
+#endif
+
using media::VideoEncodeAccelerator;
namespace content {
@@ -244,28 +255,29 @@ class H264Validator : public StreamValidator {
seen_pps_(false),
seen_idr_(false) {}
- void ProcessStreamBuffer(const uint8* stream, size_t size) OVERRIDE;
+ virtual void ProcessStreamBuffer(const uint8* stream, size_t size) OVERRIDE;
private:
// Set to true when encoder provides us with the corresponding NALU type.
bool seen_sps_;
bool seen_pps_;
bool seen_idr_;
+
+ media::H264Parser h264_parser_;
};
void H264Validator::ProcessStreamBuffer(const uint8* stream, size_t size) {
- media::H264Parser h264_parser;
- h264_parser.SetStream(stream, size);
+ h264_parser_.SetStream(stream, size);
while (1) {
media::H264NALU nalu;
media::H264Parser::Result result;
- result = h264_parser.AdvanceToNextNALU(&nalu);
+ result = h264_parser_.AdvanceToNextNALU(&nalu);
if (result == media::H264Parser::kEOStream)
break;
- ASSERT_EQ(result, media::H264Parser::kOk);
+ ASSERT_EQ(media::H264Parser::kOk, result);
bool keyframe = false;
@@ -273,22 +285,35 @@ void H264Validator::ProcessStreamBuffer(const uint8* stream, size_t size) {
case media::H264NALU::kIDRSlice:
ASSERT_TRUE(seen_sps_);
ASSERT_TRUE(seen_pps_);
- seen_idr_ = keyframe = true;
+ seen_idr_ = true;
// fallthrough
- case media::H264NALU::kNonIDRSlice:
+ case media::H264NALU::kNonIDRSlice: {
ASSERT_TRUE(seen_idr_);
+
+ media::H264SliceHeader shdr;
+ ASSERT_EQ(media::H264Parser::kOk,
+ h264_parser_.ParseSliceHeader(nalu, &shdr));
+ keyframe = shdr.IsISlice() || shdr.IsSISlice();
+
if (!frame_cb_.Run(keyframe))
return;
break;
+ }
- case media::H264NALU::kSPS:
+ case media::H264NALU::kSPS: {
+ int sps_id;
+ ASSERT_EQ(media::H264Parser::kOk, h264_parser_.ParseSPS(&sps_id));
seen_sps_ = true;
break;
+ }
- case media::H264NALU::kPPS:
+ case media::H264NALU::kPPS: {
ASSERT_TRUE(seen_sps_);
+ int pps_id;
+ ASSERT_EQ(media::H264Parser::kOk, h264_parser_.ParsePPS(&pps_id));
seen_pps_ = true;
break;
+ }
default:
break;
@@ -302,7 +327,7 @@ class VP8Validator : public StreamValidator {
: StreamValidator(frame_cb),
seen_keyframe_(false) {}
- void ProcessStreamBuffer(const uint8* stream, size_t size) OVERRIDE;
+ virtual void ProcessStreamBuffer(const uint8* stream, size_t size) OVERRIDE;
private:
// Have we already got a keyframe in the stream?
@@ -357,13 +382,13 @@ class VEAClient : public VideoEncodeAccelerator::Client {
double frames_per_second();
// VideoDecodeAccelerator::Client implementation.
- void RequireBitstreamBuffers(unsigned int input_count,
- const gfx::Size& input_coded_size,
- size_t output_buffer_size) OVERRIDE;
- void BitstreamBufferReady(int32 bitstream_buffer_id,
- size_t payload_size,
- bool key_frame) OVERRIDE;
- void NotifyError(VideoEncodeAccelerator::Error error) OVERRIDE;
+ virtual void RequireBitstreamBuffers(unsigned int input_count,
+ const gfx::Size& input_coded_size,
+ size_t output_buffer_size) OVERRIDE;
+ virtual void BitstreamBufferReady(int32 bitstream_buffer_id,
+ size_t payload_size,
+ bool key_frame) OVERRIDE;
+ virtual void NotifyError(VideoEncodeAccelerator::Error error) OVERRIDE;
private:
bool has_encoder() { return encoder_.get(); }
@@ -559,8 +584,13 @@ void VEAClient::CreateEncoder() {
DCHECK(thread_checker_.CalledOnValidThread());
CHECK(!has_encoder());
+#if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL)
scoped_ptr<V4L2Device> device = V4L2Device::Create(V4L2Device::kEncoder);
encoder_.reset(new V4L2VideoEncodeAccelerator(device.Pass()));
+#elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11)
+ encoder_.reset(new VaapiVideoEncodeAccelerator(gfx::GetXDisplay()));
+#endif
+
SetState(CS_ENCODER_SET);
DVLOG(1) << "Profile: " << test_stream_.requested_profile
diff --git a/content/common/sandbox_linux/bpf_gpu_policy_linux.cc b/content/common/sandbox_linux/bpf_gpu_policy_linux.cc
index d6b54f8..14eab49 100644
--- a/content/common/sandbox_linux/bpf_gpu_policy_linux.cc
+++ b/content/common/sandbox_linux/bpf_gpu_policy_linux.cc
@@ -72,9 +72,15 @@ inline bool IsArchitectureArm() {
#endif
}
-bool IsAcceleratedVideoDecodeEnabled() {
+bool IsAcceleratedVideoEnabled() {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
- return !command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode);
+ bool accelerated_encode_enabled = false;
+#if defined(OS_CHROMEOS)
+ accelerated_encode_enabled =
+ command_line.HasSwitch(switches::kEnableVaapiAcceleratedVideoEncode);
+#endif
+ return !command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode) ||
+ accelerated_encode_enabled;
}
intptr_t GpuSIGSYS_Handler(const struct arch_seccomp_data& args,
@@ -214,9 +220,9 @@ bool GpuProcessPolicy::PreSandboxHook() {
std::vector<std::string>());
if (IsArchitectureX86_64() || IsArchitectureI386()) {
- // Accelerated video decode dlopen()'s some shared objects
+ // Accelerated video dlopen()'s some shared objects
// inside the sandbox, so preload them now.
- if (IsAcceleratedVideoDecodeEnabled()) {
+ if (IsAcceleratedVideoEnabled()) {
const char* I965DrvVideoPath = NULL;
if (IsArchitectureX86_64()) {
diff --git a/content/content_common.gypi b/content/content_common.gypi
index e3cda4e..bfaf016 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -723,6 +723,10 @@
],
}],
['target_arch != "arm" and chromeos == 1 and use_x11 == 1', {
+ 'dependencies': [
+ '../media/media.gyp:media',
+ '../third_party/libyuv/libyuv.gyp:libyuv',
+ ],
'sources': [
'common/gpu/media/h264_dpb.cc',
'common/gpu/media/h264_dpb.h',
@@ -731,6 +735,8 @@
'common/gpu/media/vaapi_h264_decoder.h',
'common/gpu/media/vaapi_video_decode_accelerator.cc',
'common/gpu/media/vaapi_video_decode_accelerator.h',
+ 'common/gpu/media/vaapi_video_encode_accelerator.cc',
+ 'common/gpu/media/vaapi_video_encode_accelerator.h',
'common/gpu/media/vaapi_wrapper.cc',
'common/gpu/media/vaapi_wrapper.h',
],
@@ -746,6 +752,7 @@
},
'include_dirs': [
'<(DEPTH)/third_party/libva',
+ '<(DEPTH)/third_party/libyuv',
'<(output_root)',
],
'actions': [
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 66f6dda..01af316 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -1467,7 +1467,7 @@
},
]
}],
- ['chromeos==1 and target_arch == "arm"', {
+ ['chromeos==1 and (target_arch == "arm" or use_x11 == 1)', {
'targets': [
{
'target_name': 'video_encode_accelerator_unittest',
@@ -1487,6 +1487,17 @@
'common/gpu/media/video_accelerator_unittest_helpers.h',
'common/gpu/media/video_encode_accelerator_unittest.cc',
],
+ 'include_dirs': [
+ '<(DEPTH)/third_party/libva',
+ '<(DEPTH)/third_party/libyuv',
+ ],
+ 'conditions': [
+ ['use_x11==1', {
+ 'dependencies': [
+ '../ui/gfx/x/gfx_x11.gyp:gfx_x11',
+ ],
+ }],
+ ],
},
]
}],
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 4242799..50e77f5 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -923,6 +923,10 @@ const char kDisableWebAudio[] = "disable-webaudio";
#if defined(OS_CHROMEOS)
// Disables panel fitting (used for mirror mode).
const char kDisablePanelFitting[] = "disable-panel-fitting";
+
+// Enables VA-API accelerated video encode.
+const char kEnableVaapiAcceleratedVideoEncode[] =
+ "enable-vaapi-accelerated-video-encode";
#endif
#if defined(OS_MACOSX) && !defined(OS_IOS)
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index b64d134..4639bff 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -266,6 +266,7 @@ CONTENT_EXPORT extern const char kDisableWebAudio[];
#if defined(OS_CHROMEOS)
CONTENT_EXPORT extern const char kDisablePanelFitting[];
+CONTENT_EXPORT extern const char kEnableVaapiAcceleratedVideoEncode[];
#endif
#if defined(OS_MACOSX) && !defined(OS_IOS)
diff --git a/media/filters/h264_bitstream_buffer.cc b/media/filters/h264_bitstream_buffer.cc
new file mode 100644
index 0000000..48463a5
--- /dev/null
+++ b/media/filters/h264_bitstream_buffer.cc
@@ -0,0 +1,152 @@
+// Copyright 2014 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/filters/h264_bitstream_buffer.h"
+
+#include "base/sys_byteorder.h"
+
+namespace media {
+
+H264BitstreamBuffer::H264BitstreamBuffer() : data_(NULL) {
+ Reset();
+}
+
+H264BitstreamBuffer::~H264BitstreamBuffer() {
+ free(data_);
+ data_ = NULL;
+}
+
+void H264BitstreamBuffer::Reset() {
+ free(data_);
+ data_ = NULL;
+
+ capacity_ = 0;
+ pos_ = 0;
+ reg_ = 0;
+
+ Grow();
+
+ bits_left_in_reg_ = kRegBitSize;
+}
+
+void H264BitstreamBuffer::Grow() {
+ data_ = static_cast<uint8*>(realloc(data_, capacity_ + kGrowBytes));
+ CHECK(data_) << "Failed growing the buffer";
+ capacity_ += kGrowBytes;
+}
+
+void H264BitstreamBuffer::FlushReg() {
+ // Flush all bytes that have at least one bit cached, but not more
+ // (on Flush(), reg_ may not be full).
+ size_t bits_in_reg = kRegBitSize - bits_left_in_reg_;
+ if (bits_in_reg == 0)
+ return;
+
+ size_t bytes_in_reg = (bits_in_reg + 7) / 8;
+ reg_ <<= (kRegBitSize - bits_in_reg);
+
+ // Convert to MSB and append as such to the stream.
+ reg_ = base::HostToNet64(reg_);
+
+ // Make sure we have enough space. Grow() will CHECK() on allocation failure.
+ if (pos_ + bytes_in_reg < capacity_)
+ Grow();
+
+ memcpy(data_ + pos_, &reg_, bytes_in_reg);
+ pos_ += bytes_in_reg;
+
+ reg_ = 0;
+ bits_left_in_reg_ = kRegBitSize;
+}
+
+void H264BitstreamBuffer::AppendU64(size_t num_bits, uint64 val) {
+ CHECK_LE(num_bits, kRegBitSize);
+
+ while (num_bits > 0) {
+ if (bits_left_in_reg_ == 0)
+ FlushReg();
+
+ uint64 bits_to_write =
+ num_bits > bits_left_in_reg_ ? bits_left_in_reg_ : num_bits;
+ uint64 val_to_write = (val >> (num_bits - bits_to_write));
+ if (bits_to_write < 64)
+ val_to_write &= ((1ull << bits_to_write) - 1);
+ reg_ <<= bits_to_write;
+ reg_ |= val_to_write;
+ num_bits -= bits_to_write;
+ bits_left_in_reg_ -= bits_to_write;
+ }
+}
+
+void H264BitstreamBuffer::AppendBool(bool val) {
+ if (bits_left_in_reg_ == 0)
+ FlushReg();
+
+ reg_ <<= 1;
+ reg_ |= (static_cast<uint64>(val) & 1);
+ --bits_left_in_reg_;
+}
+
+void H264BitstreamBuffer::AppendSE(int val) {
+ if (val > 0)
+ AppendUE(val * 2 - 1);
+ else
+ AppendUE(-val * 2);
+}
+
+void H264BitstreamBuffer::AppendUE(unsigned int val) {
+ size_t num_zeros = 0;
+ unsigned int v = val + 1;
+
+ while (v > 1) {
+ v >>= 1;
+ ++num_zeros;
+ }
+
+ AppendBits(num_zeros, 0);
+ AppendBits(num_zeros + 1, val + 1);
+}
+
+#define DCHECK_FINISHED() \
+ DCHECK_EQ(bits_left_in_reg_, kRegBitSize) << "Pending bits not yet written " \
+ "to the buffer, call " \
+ "FinishNALU() first."
+
+void H264BitstreamBuffer::BeginNALU(H264NALU::Type nalu_type, int nal_ref_idc) {
+ DCHECK_FINISHED();
+
+ DCHECK_LE(nalu_type, H264NALU::kEOStream);
+ DCHECK_GE(nal_ref_idc, 0);
+ DCHECK_LE(nal_ref_idc, 3);
+
+ AppendBits(32, 0x00000001);
+ AppendBits(1, 0); // forbidden_zero_bit
+ AppendBits(2, nal_ref_idc);
+ AppendBits(5, nalu_type);
+}
+
+void H264BitstreamBuffer::FinishNALU() {
+ // RBSP stop one bit.
+ AppendBits(1, 1);
+
+ // Byte-alignment zero bits.
+ AppendBits(bits_left_in_reg_ % 8, 0);
+
+ if (bits_left_in_reg_ != kRegBitSize)
+ FlushReg();
+}
+
+size_t H264BitstreamBuffer::BytesInBuffer() {
+ DCHECK_FINISHED();
+ return pos_;
+}
+
+uint8* H264BitstreamBuffer::data() {
+ DCHECK(data_);
+ DCHECK_FINISHED();
+
+ return data_;
+}
+
+} // namespace media
diff --git a/media/filters/h264_bitstream_buffer.h b/media/filters/h264_bitstream_buffer.h
new file mode 100644
index 0000000..4b0511d
--- /dev/null
+++ b/media/filters/h264_bitstream_buffer.h
@@ -0,0 +1,120 @@
+// Copyright 2014 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.
+//
+// This file contains an implementation of a H264BitstreamBuffer class for
+// constructing raw bitstream buffers containing NAL units in
+// H.264 Annex-B stream format.
+// See H.264 spec Annex B and chapter 7for more details.
+
+#ifndef MEDIA_FILTERS_H264_BITSTREAM_BUFFER_H_
+#define MEDIA_FILTERS_H264_BITSTREAM_BUFFER_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/numerics/safe_conversions.h"
+#include "media/base/media_export.h"
+#include "media/base/video_frame.h"
+#include "media/filters/h264_parser.h"
+
+namespace media {
+
+// Holds one or more NALUs as a raw bitstream buffer in H.264 Annex-B format.
+// Note that this class currently does NOT insert emulation prevention
+// three-byte sequences (spec 7.3.1).
+class MEDIA_EXPORT H264BitstreamBuffer {
+ public:
+ H264BitstreamBuffer();
+ ~H264BitstreamBuffer();
+
+ // Discard all data and reset the buffer for reuse.
+ void Reset();
+
+ // Append |num_bits| bits to the stream from |val|.
+ // |val| is interpreted in the host endianness.
+ template <typename T>
+ void AppendBits(size_t num_bits, T val) {
+ AppendU64(num_bits, static_cast<uint64>(val));
+ }
+
+ void AppendBits(size_t num_bits, bool val) {
+ DCHECK_EQ(num_bits, 1ul);
+ AppendBool(val);
+ }
+
+ // Append a one-bit bool/flag value |val| to the stream.
+ void AppendBool(bool val);
+
+ // Append a signed value in |val| in Exp-Golomb code.
+ void AppendSE(int val);
+
+ // Append an unsigned value in |val| in Exp-Golomb code.
+ void AppendUE(unsigned int val);
+
+ // Start a new NALU of type |nalu_type| and with given |nal_ref_idc|
+ // (see spec). Note, that until FinishNALU() is called, some of the bits
+ // may not be flushed into the buffer and the data will not be correctly
+ // aligned with trailing bits.
+ void BeginNALU(H264NALU::Type nalu_type, int nal_ref_idc);
+
+ // Finish current NALU. This will flush any cached bits and correctly align
+ // the buffer with RBSP trailing bits. This MUST be called for the stream
+ // returned by data() to be correct.
+ void FinishNALU();
+
+ // Return number of full bytes in the stream. Note that FinishNALU() has to
+ // be called to flush cached bits, or the return value will not include them.
+ size_t BytesInBuffer();
+
+ // Return a pointer to the stream. FinishNALU() must be called before
+ // accessing the stream, otherwise some bits may still be cached and not
+ // in the buffer.
+ uint8* data();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(H264BitstreamBufferAppendBitsTest,
+ AppendAndVerifyBits);
+
+ // Allocate additional memory (kGrowBytes bytes) for the buffer.
+ void Grow();
+
+ // Append |num_bits| bits from U64 value |val| (in host endianness).
+ void AppendU64(size_t num_bits, uint64 val);
+
+ // Flush any cached bits in the reg with byte granularity, i.e. enough
+ // bytes to flush all pending bits, but not more.
+ void FlushReg();
+
+ typedef uint64 RegType;
+ enum {
+ // Sizes of reg_.
+ kRegByteSize = sizeof(RegType),
+ kRegBitSize = kRegByteSize * 8,
+ // Amount of bytes to grow the buffer by when we run out of
+ // previously-allocated memory for it.
+ kGrowBytes = 4096,
+ };
+
+ COMPILE_ASSERT(kGrowBytes >= kRegByteSize,
+ kGrowBytes_must_be_larger_than_kRegByteSize);
+
+ // Unused bits left in reg_.
+ size_t bits_left_in_reg_;
+
+ // Cache for appended bits. Bits are flushed to data_ with kRegByteSize
+ // granularity, i.e. when reg_ becomes full, or when an explicit FlushReg()
+ // is called.
+ RegType reg_;
+
+ // Current capacity of data_, in bytes.
+ size_t capacity_;
+
+ // Current byte offset in data_ (points to the start of unwritten bits).
+ size_t pos_;
+
+ // Buffer for stream data.
+ uint8* data_;
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_H264_BITSTREAM_BUFFER_H_
diff --git a/media/filters/h264_bitstream_buffer_unittest.cc b/media/filters/h264_bitstream_buffer_unittest.cc
new file mode 100644
index 0000000..c959427
--- /dev/null
+++ b/media/filters/h264_bitstream_buffer_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright 2014 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/filters/h264_bitstream_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+const uint64 kTestPattern = 0xfedcba0987654321;
+}
+
+class H264BitstreamBufferAppendBitsTest
+ : public ::testing::TestWithParam<size_t> {};
+
+// TODO(posciak): More tests!
+
+TEST_P(H264BitstreamBufferAppendBitsTest, AppendAndVerifyBits) {
+ H264BitstreamBuffer b;
+ uint64 num_bits = GetParam();
+ // TODO(posciak): Tests for >64 bits.
+ ASSERT_LE(num_bits, 64u);
+ uint64 num_bytes = (num_bits + 7) / 8;
+
+ b.AppendBits(num_bits, kTestPattern);
+ b.FlushReg();
+
+ EXPECT_EQ(b.BytesInBuffer(), num_bytes);
+
+ uint8* ptr = b.data();
+ uint64 got = 0;
+ uint64 expected = kTestPattern;
+
+ if (num_bits < 64)
+ expected &= ((1ull << num_bits) - 1);
+
+ while (num_bits > 8) {
+ got |= (*ptr & 0xff);
+ num_bits -= 8;
+ got <<= (num_bits > 8 ? 8 : num_bits);
+ ptr++;
+ }
+ if (num_bits > 0) {
+ uint64 temp = (*ptr & 0xff);
+ temp >>= (8 - num_bits);
+ got |= temp;
+ }
+ EXPECT_EQ(got, expected) << std::hex << "0x" << got << " vs 0x" << expected;
+}
+
+INSTANTIATE_TEST_CASE_P(AppendNumBits,
+ H264BitstreamBufferAppendBitsTest,
+ ::testing::Range(static_cast<uint64>(1),
+ static_cast<uint64>(65)));
+} // namespace media
diff --git a/media/filters/h264_parser.cc b/media/filters/h264_parser.cc
index 4cdc695..ee21ab8 100644
--- a/media/filters/h264_parser.cc
+++ b/media/filters/h264_parser.cc
@@ -106,10 +106,6 @@ H264SEIMessage::H264SEIMessage() {
} \
} while (0)
-enum AspectRatioIdc {
- kExtendedSar = 255,
-};
-
// ISO 14496 part 10
// VUI parameters: Table E-1 "Meaning of sample aspect ratio indicator"
static const int kTableSarWidth[] = {
@@ -608,7 +604,7 @@ H264Parser::Result H264Parser::ParseVUIParameters(H264SPS* sps) {
if (aspect_ratio_info_present_flag) {
int aspect_ratio_idc;
READ_BITS_OR_RETURN(8, &aspect_ratio_idc);
- if (aspect_ratio_idc == kExtendedSar) {
+ if (aspect_ratio_idc == H264SPS::kExtendedSar) {
READ_BITS_OR_RETURN(16, &sps->sar_width);
READ_BITS_OR_RETURN(16, &sps->sar_height);
} else {
diff --git a/media/filters/h264_parser.h b/media/filters/h264_parser.h
index 3a60dce..45020af 100644
--- a/media/filters/h264_parser.h
+++ b/media/filters/h264_parser.h
@@ -63,6 +63,26 @@ enum {
struct MEDIA_EXPORT H264SPS {
H264SPS();
+ enum H264ProfileIDC {
+ kProfileIDCBaseline = 66,
+ kProfileIDCConstrainedBaseline = kProfileIDCBaseline,
+ kProfileIDCMain = 77,
+ kProfileIDCHigh = 100,
+ };
+
+ enum AspectRatioIdc {
+ kExtendedSar = 255,
+ };
+
+ enum {
+ // Constants for HRD parameters (spec ch. E.2.2).
+ kBitRateScaleConstantTerm = 6, // Equation E-37.
+ kCPBSizeScaleConstantTerm = 4, // Equation E-38.
+ kDefaultInitialCPBRemovalDelayLength = 24,
+ kDefaultDPBOutputDelayLength = 24,
+ kDefaultTimeOffsetLength = 24,
+ };
+
int profile_idc;
bool constraint_set0_flag;
bool constraint_set1_flag;
@@ -111,6 +131,25 @@ struct MEDIA_EXPORT H264SPS {
bool bitstream_restriction_flag;
int max_num_reorder_frames;
int max_dec_frame_buffering;
+ bool timing_info_present_flag;
+ int num_units_in_tick;
+ int time_scale;
+ bool fixed_frame_rate_flag;
+
+ // TODO(posciak): actually parse these instead of ParseAndIgnoreHRDParameters.
+ bool nal_hrd_parameters_present_flag;
+ int cpb_cnt_minus1;
+ int bit_rate_scale;
+ int cpb_size_scale;
+ int bit_rate_value_minus1[32];
+ int cpb_size_value_minus1[32];
+ bool cbr_flag[32];
+ int initial_cpb_removal_delay_length_minus_1;
+ int cpb_removal_delay_length_minus1;
+ int dpb_output_delay_length_minus1;
+ int time_offset_length;
+
+ bool low_delay_hrd_flag;
int chroma_array_type;
};
diff --git a/media/media.gyp b/media/media.gyp
index 7a38a4f..1b3987a 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -659,6 +659,13 @@
'formats/webm/chromeos/webm_encoder.h',
],
}],
+ # For VaapiVideoEncodeAccelerator.
+ ['target_arch != "arm" and chromeos == 1 and use_x11 == 1', {
+ 'sources': [
+ 'filters/h264_bitstream_buffer.cc',
+ 'filters/h264_bitstream_buffer.h',
+ ],
+ }],
['OS!="ios"', {
'dependencies': [
'../third_party/libyuv/libyuv.gyp:libyuv',
@@ -1223,6 +1230,11 @@
}],
],
}],
+ ['target_arch != "arm" and chromeos == 1 and use_x11 == 1', {
+ 'sources': [
+ 'filters/h264_bitstream_buffer_unittest.cc',
+ ],
+ }],
['use_alsa==0', {
'sources!': [
'audio/alsa/alsa_output_unittest.cc',
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index e35ae92..677b07f 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -10312,6 +10312,13 @@ Therefore, the affected-histogram name has to have at least one dot in it.
</summary>
</histogram>
+<histogram name="Media.VAVEA.EncoderFailure" enum="VAVEAEncoderFailure">
+ <owner>posciak@chromium.org</owner>
+ <summary>
+ Error codes reported by video encode using VA-API hardware video encoder.
+ </summary>
+</histogram>
+
<histogram name="Media.VideoCapture.AspectRatio">
<owner>Please list the metric's owners. Add more owner tags as needed.</owner>
<summary>
@@ -47026,6 +47033,10 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<int value="4" label="VAAPI_ERROR"/>
</enum>
+<enum name="VAVEAEncoderFailure" type="int">
+ <int value="0" label="VAAPI_ERROR"/>
+</enum>
+
<enum name="VideoCodec" type="int">
<int value="0" label="kUnknownVideoCodec"/>
<int value="1" label="kCodecH264"/>