1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/common/gpu/media/gpu_video_encode_accelerator.h"
7 #include "base/callback.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/memory/shared_memory.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "build/build_config.h"
13 #include "content/common/gpu/gpu_channel.h"
14 #include "content/common/gpu/gpu_messages.h"
15 #include "content/public/common/content_switches.h"
16 #include "ipc/ipc_message_macros.h"
17 #include "media/base/limits.h"
18 #include "media/base/video_frame.h"
20 #if defined(OS_CHROMEOS) && defined(USE_X11)
22 #if defined(ARCH_CPU_ARMEL)
23 #include "content/common/gpu/media/v4l2_video_encode_accelerator.h"
24 #elif defined(ARCH_CPU_X86_FAMILY)
25 #include "content/common/gpu/media/vaapi_video_encode_accelerator.h"
26 #include "ui/gfx/x/x11_types.h"
29 #elif defined(OS_ANDROID) && defined(ENABLE_WEBRTC)
30 #include "content/common/gpu/media/android_video_encode_accelerator.h"
35 static bool MakeDecoderContextCurrent(
36 const base::WeakPtr<GpuCommandBufferStub> stub) {
38 DLOG(ERROR) << "Stub is gone; won't MakeCurrent().";
42 if (!stub->decoder()->MakeCurrent()) {
43 DLOG(ERROR) << "Failed to MakeCurrent()";
50 GpuVideoEncodeAccelerator::GpuVideoEncodeAccelerator(int32 host_route_id,
51 GpuCommandBufferStub* stub)
52 : host_route_id_(host_route_id),
54 input_format_(media::VideoFrame::UNKNOWN),
55 output_buffer_size_(0),
56 weak_this_factory_(this) {
57 stub_->AddDestructionObserver(this);
58 make_context_current_ =
59 base::Bind(&MakeDecoderContextCurrent, stub_->AsWeakPtr());
62 GpuVideoEncodeAccelerator::~GpuVideoEncodeAccelerator() {
63 // This class can only be self-deleted from OnWillDestroyStub(), which means
64 // the VEA has already been destroyed in there.
68 void GpuVideoEncodeAccelerator::Initialize(
69 media::VideoFrame::Format input_format,
70 const gfx::Size& input_visible_size,
71 media::VideoCodecProfile output_profile,
72 uint32 initial_bitrate,
73 IPC::Message* init_done_msg) {
74 DVLOG(2) << "GpuVideoEncodeAccelerator::Initialize(): "
75 "input_format=" << input_format
76 << ", input_visible_size=" << input_visible_size.ToString()
77 << ", output_profile=" << output_profile
78 << ", initial_bitrate=" << initial_bitrate;
81 if (!stub_->channel()->AddRoute(host_route_id_, this)) {
82 DLOG(ERROR) << "GpuVideoEncodeAccelerator::Initialize(): "
83 "failed to add route";
84 SendCreateEncoderReply(init_done_msg, false);
88 if (input_visible_size.width() > media::limits::kMaxDimension ||
89 input_visible_size.height() > media::limits::kMaxDimension ||
90 input_visible_size.GetArea() > media::limits::kMaxCanvas) {
91 DLOG(ERROR) << "GpuVideoEncodeAccelerator::Initialize(): "
92 "input_visible_size " << input_visible_size.ToString()
94 SendCreateEncoderReply(init_done_msg, false);
98 encoder_ = CreateEncoder();
101 << "GpuVideoEncodeAccelerator::Initialize(): VEA creation failed";
102 SendCreateEncoderReply(init_done_msg, false);
105 if (!encoder_->Initialize(input_format,
111 << "GpuVideoEncodeAccelerator::Initialize(): VEA initialization failed";
112 SendCreateEncoderReply(init_done_msg, false);
115 input_format_ = input_format;
116 input_visible_size_ = input_visible_size;
117 SendCreateEncoderReply(init_done_msg, true);
120 bool GpuVideoEncodeAccelerator::OnMessageReceived(const IPC::Message& message) {
122 IPC_BEGIN_MESSAGE_MAP(GpuVideoEncodeAccelerator, message)
123 IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderMsg_Encode, OnEncode)
124 IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderMsg_UseOutputBitstreamBuffer,
125 OnUseOutputBitstreamBuffer)
127 AcceleratedVideoEncoderMsg_RequestEncodingParametersChange,
128 OnRequestEncodingParametersChange)
129 IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderMsg_Destroy, OnDestroy)
130 IPC_MESSAGE_UNHANDLED(handled = false)
131 IPC_END_MESSAGE_MAP()
135 void GpuVideoEncodeAccelerator::RequireBitstreamBuffers(
136 unsigned int input_count,
137 const gfx::Size& input_coded_size,
138 size_t output_buffer_size) {
139 Send(new AcceleratedVideoEncoderHostMsg_RequireBitstreamBuffers(
140 host_route_id_, input_count, input_coded_size, output_buffer_size));
141 input_coded_size_ = input_coded_size;
142 output_buffer_size_ = output_buffer_size;
145 void GpuVideoEncodeAccelerator::BitstreamBufferReady(int32 bitstream_buffer_id,
148 Send(new AcceleratedVideoEncoderHostMsg_BitstreamBufferReady(
149 host_route_id_, bitstream_buffer_id, payload_size, key_frame));
152 void GpuVideoEncodeAccelerator::NotifyError(
153 media::VideoEncodeAccelerator::Error error) {
154 Send(new AcceleratedVideoEncoderHostMsg_NotifyError(host_route_id_, error));
157 void GpuVideoEncodeAccelerator::OnWillDestroyStub() {
159 stub_->channel()->RemoveRoute(host_route_id_);
160 stub_->RemoveDestructionObserver(this);
166 std::vector<media::VideoEncodeAccelerator::SupportedProfile>
167 GpuVideoEncodeAccelerator::GetSupportedProfiles() {
168 #if defined(OS_CHROMEOS) && defined(USE_X11) && defined(ARCH_CPU_ARMEL)
169 // This is a work-around for M39 because the video device is not ready at
171 // TODO(wuchengli): remove this after http://crbug.com/418762 is fixed.
172 return V4L2VideoEncodeAccelerator::GetSupportedProfilesStatic();
174 scoped_ptr<media::VideoEncodeAccelerator> encoder = CreateEncoder();
176 return std::vector<media::VideoEncodeAccelerator::SupportedProfile>();
177 return encoder->GetSupportedProfiles();
180 scoped_ptr<media::VideoEncodeAccelerator>
181 GpuVideoEncodeAccelerator::CreateEncoder() {
182 scoped_ptr<media::VideoEncodeAccelerator> encoder;
183 #if defined(OS_CHROMEOS) && defined(USE_X11)
184 #if defined(ARCH_CPU_ARMEL)
185 scoped_ptr<V4L2Device> device = V4L2Device::Create(V4L2Device::kEncoder);
187 encoder.reset(new V4L2VideoEncodeAccelerator(device.Pass()));
188 #elif defined(ARCH_CPU_X86_FAMILY)
189 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
190 if (!cmd_line->HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode))
191 encoder.reset(new VaapiVideoEncodeAccelerator(gfx::GetXDisplay()));
193 #elif defined(OS_ANDROID) && defined(ENABLE_WEBRTC)
194 encoder.reset(new AndroidVideoEncodeAccelerator());
196 return encoder.Pass();
199 void GpuVideoEncodeAccelerator::OnEncode(int32 frame_id,
200 base::SharedMemoryHandle buffer_handle,
202 bool force_keyframe) {
203 DVLOG(3) << "GpuVideoEncodeAccelerator::OnEncode(): frame_id=" << frame_id
204 << ", buffer_size=" << buffer_size
205 << ", force_keyframe=" << force_keyframe;
209 DLOG(ERROR) << "GpuVideoEncodeAccelerator::OnEncode(): invalid frame_id="
211 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
215 scoped_ptr<base::SharedMemory> shm(
216 new base::SharedMemory(buffer_handle, true));
217 if (!shm->Map(buffer_size)) {
218 DLOG(ERROR) << "GpuVideoEncodeAccelerator::OnEncode(): "
219 "could not map frame_id=" << frame_id;
220 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
224 uint8* shm_memory = reinterpret_cast<uint8*>(shm->memory());
225 scoped_refptr<media::VideoFrame> frame =
226 media::VideoFrame::WrapExternalPackedMemory(
229 gfx::Rect(input_visible_size_),
235 // It's turtles all the way down...
236 base::Bind(base::IgnoreResult(&base::MessageLoopProxy::PostTask),
237 base::MessageLoopProxy::current(),
239 base::Bind(&GpuVideoEncodeAccelerator::EncodeFrameFinished,
240 weak_this_factory_.GetWeakPtr(),
242 base::Passed(&shm))));
245 DLOG(ERROR) << "GpuVideoEncodeAccelerator::OnEncode(): "
246 "could not create VideoFrame for frame_id=" << frame_id;
247 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
251 encoder_->Encode(frame, force_keyframe);
254 void GpuVideoEncodeAccelerator::OnUseOutputBitstreamBuffer(
256 base::SharedMemoryHandle buffer_handle,
257 uint32 buffer_size) {
258 DVLOG(3) << "GpuVideoEncodeAccelerator::OnUseOutputBitstreamBuffer(): "
259 "buffer_id=" << buffer_id
260 << ", buffer_size=" << buffer_size;
264 DLOG(ERROR) << "GpuVideoEncodeAccelerator::OnUseOutputBitstreamBuffer(): "
265 "invalid buffer_id=" << buffer_id;
266 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
269 if (buffer_size < output_buffer_size_) {
270 DLOG(ERROR) << "GpuVideoEncodeAccelerator::OnUseOutputBitstreamBuffer(): "
271 "buffer too small for buffer_id=" << buffer_id;
272 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
275 encoder_->UseOutputBitstreamBuffer(
276 media::BitstreamBuffer(buffer_id, buffer_handle, buffer_size));
279 void GpuVideoEncodeAccelerator::OnDestroy() {
280 DVLOG(2) << "GpuVideoEncodeAccelerator::OnDestroy()";
284 void GpuVideoEncodeAccelerator::OnRequestEncodingParametersChange(
287 DVLOG(2) << "GpuVideoEncodeAccelerator::OnRequestEncodingParametersChange(): "
288 "bitrate=" << bitrate
289 << ", framerate=" << framerate;
292 encoder_->RequestEncodingParametersChange(bitrate, framerate);
295 void GpuVideoEncodeAccelerator::EncodeFrameFinished(
297 scoped_ptr<base::SharedMemory> shm) {
298 Send(new AcceleratedVideoEncoderHostMsg_NotifyInputDone(host_route_id_,
300 // Just let shm fall out of scope.
303 void GpuVideoEncodeAccelerator::Send(IPC::Message* message) {
304 stub_->channel()->Send(message);
307 void GpuVideoEncodeAccelerator::SendCreateEncoderReply(IPC::Message* message,
309 GpuCommandBufferMsg_CreateVideoEncoder::WriteReplyParams(message, succeeded);
313 } // namespace content