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/numerics/safe_math.h"
12 #include "base/sys_info.h"
13 #include "build/build_config.h"
14 #include "content/common/gpu/gpu_channel.h"
15 #include "content/common/gpu/gpu_messages.h"
16 #include "content/common/gpu/media/gpu_video_accelerator_util.h"
17 #include "content/public/common/content_switches.h"
18 #include "ipc/ipc_message_macros.h"
19 #include "media/base/bind_to_current_loop.h"
20 #include "media/base/limits.h"
21 #include "media/base/video_frame.h"
23 #if defined(OS_CHROMEOS)
24 #if defined(USE_V4L2_CODEC)
25 #include "content/common/gpu/media/v4l2_video_encode_accelerator.h"
27 #if defined(ARCH_CPU_X86_FAMILY)
28 #include "content/common/gpu/media/vaapi_video_encode_accelerator.h"
30 #elif defined(OS_ANDROID) && defined(ENABLE_WEBRTC)
31 #include "content/common/gpu/media/android_video_encode_accelerator.h"
36 static bool MakeDecoderContextCurrent(
37 const base::WeakPtr
<GpuCommandBufferStub
> stub
) {
39 DLOG(ERROR
) << "Stub is gone; won't MakeCurrent().";
43 if (!stub
->decoder()->MakeCurrent()) {
44 DLOG(ERROR
) << "Failed to MakeCurrent()";
51 GpuVideoEncodeAccelerator::GpuVideoEncodeAccelerator(int32 host_route_id
,
52 GpuCommandBufferStub
* stub
)
53 : host_route_id_(host_route_id
),
55 input_format_(media::VideoFrame::UNKNOWN
),
56 output_buffer_size_(0),
57 weak_this_factory_(this) {
58 stub_
->AddDestructionObserver(this);
59 make_context_current_
=
60 base::Bind(&MakeDecoderContextCurrent
, stub_
->AsWeakPtr());
63 GpuVideoEncodeAccelerator::~GpuVideoEncodeAccelerator() {
64 // This class can only be self-deleted from OnWillDestroyStub(), which means
65 // the VEA has already been destroyed in there.
69 void GpuVideoEncodeAccelerator::Initialize(
70 media::VideoFrame::Format input_format
,
71 const gfx::Size
& input_visible_size
,
72 media::VideoCodecProfile output_profile
,
73 uint32 initial_bitrate
,
74 IPC::Message
* init_done_msg
) {
75 DVLOG(2) << "GpuVideoEncodeAccelerator::Initialize(): "
76 "input_format=" << input_format
77 << ", input_visible_size=" << input_visible_size
.ToString()
78 << ", output_profile=" << output_profile
79 << ", initial_bitrate=" << initial_bitrate
;
82 if (!stub_
->channel()->AddRoute(host_route_id_
, this)) {
83 DLOG(ERROR
) << "GpuVideoEncodeAccelerator::Initialize(): "
84 "failed to add route";
85 SendCreateEncoderReply(init_done_msg
, false);
89 if (input_visible_size
.width() > media::limits::kMaxDimension
||
90 input_visible_size
.height() > media::limits::kMaxDimension
||
91 input_visible_size
.GetArea() > media::limits::kMaxCanvas
) {
92 DLOG(ERROR
) << "GpuVideoEncodeAccelerator::Initialize(): "
93 "input_visible_size " << input_visible_size
.ToString()
95 SendCreateEncoderReply(init_done_msg
, false);
99 std::vector
<GpuVideoEncodeAccelerator::CreateVEAFp
>
100 create_vea_fps
= CreateVEAFps();
101 // Try all possible encoders and use the first successful encoder.
102 for (size_t i
= 0; i
< create_vea_fps
.size(); ++i
) {
103 encoder_
= (*create_vea_fps
[i
])();
104 if (encoder_
&& encoder_
->Initialize(input_format
,
109 input_format_
= input_format
;
110 input_visible_size_
= input_visible_size
;
111 SendCreateEncoderReply(init_done_msg
, true);
117 << "GpuVideoEncodeAccelerator::Initialize(): VEA initialization failed";
118 SendCreateEncoderReply(init_done_msg
, false);
121 bool GpuVideoEncodeAccelerator::OnMessageReceived(const IPC::Message
& message
) {
123 IPC_BEGIN_MESSAGE_MAP(GpuVideoEncodeAccelerator
, message
)
124 IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderMsg_Encode
, OnEncode
)
125 IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderMsg_UseOutputBitstreamBuffer
,
126 OnUseOutputBitstreamBuffer
)
128 AcceleratedVideoEncoderMsg_RequestEncodingParametersChange
,
129 OnRequestEncodingParametersChange
)
130 IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderMsg_Destroy
, OnDestroy
)
131 IPC_MESSAGE_UNHANDLED(handled
= false)
132 IPC_END_MESSAGE_MAP()
136 void GpuVideoEncodeAccelerator::RequireBitstreamBuffers(
137 unsigned int input_count
,
138 const gfx::Size
& input_coded_size
,
139 size_t output_buffer_size
) {
140 Send(new AcceleratedVideoEncoderHostMsg_RequireBitstreamBuffers(
141 host_route_id_
, input_count
, input_coded_size
, output_buffer_size
));
142 input_coded_size_
= input_coded_size
;
143 output_buffer_size_
= output_buffer_size
;
146 void GpuVideoEncodeAccelerator::BitstreamBufferReady(int32 bitstream_buffer_id
,
149 Send(new AcceleratedVideoEncoderHostMsg_BitstreamBufferReady(
150 host_route_id_
, bitstream_buffer_id
, payload_size
, key_frame
));
153 void GpuVideoEncodeAccelerator::NotifyError(
154 media::VideoEncodeAccelerator::Error error
) {
155 Send(new AcceleratedVideoEncoderHostMsg_NotifyError(host_route_id_
, error
));
158 void GpuVideoEncodeAccelerator::OnWillDestroyStub() {
160 stub_
->channel()->RemoveRoute(host_route_id_
);
161 stub_
->RemoveDestructionObserver(this);
167 gpu::VideoEncodeAcceleratorSupportedProfiles
168 GpuVideoEncodeAccelerator::GetSupportedProfiles() {
169 media::VideoEncodeAccelerator::SupportedProfiles profiles
;
170 std::vector
<GpuVideoEncodeAccelerator::CreateVEAFp
>
171 create_vea_fps
= CreateVEAFps();
173 for (size_t i
= 0; i
< create_vea_fps
.size(); ++i
) {
174 scoped_ptr
<media::VideoEncodeAccelerator
>
175 encoder
= (*create_vea_fps
[i
])();
178 media::VideoEncodeAccelerator::SupportedProfiles vea_profiles
=
179 encoder
->GetSupportedProfiles();
180 GpuVideoAcceleratorUtil::InsertUniqueEncodeProfiles(
181 vea_profiles
, &profiles
);
183 return GpuVideoAcceleratorUtil::ConvertMediaToGpuEncodeProfiles(profiles
);
187 std::vector
<GpuVideoEncodeAccelerator::CreateVEAFp
>
188 GpuVideoEncodeAccelerator::CreateVEAFps() {
189 std::vector
<GpuVideoEncodeAccelerator::CreateVEAFp
> create_vea_fps
;
190 create_vea_fps
.push_back(&GpuVideoEncodeAccelerator::CreateV4L2VEA
);
191 create_vea_fps
.push_back(&GpuVideoEncodeAccelerator::CreateVaapiVEA
);
192 create_vea_fps
.push_back(&GpuVideoEncodeAccelerator::CreateAndroidVEA
);
193 return create_vea_fps
;
197 scoped_ptr
<media::VideoEncodeAccelerator
>
198 GpuVideoEncodeAccelerator::CreateV4L2VEA() {
199 scoped_ptr
<media::VideoEncodeAccelerator
> encoder
;
200 #if defined(OS_CHROMEOS) && defined(USE_V4L2_CODEC)
201 scoped_refptr
<V4L2Device
> device
= V4L2Device::Create(V4L2Device::kEncoder
);
203 encoder
.reset(new V4L2VideoEncodeAccelerator(device
));
205 return encoder
.Pass();
209 scoped_ptr
<media::VideoEncodeAccelerator
>
210 GpuVideoEncodeAccelerator::CreateVaapiVEA() {
211 scoped_ptr
<media::VideoEncodeAccelerator
> encoder
;
212 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
213 const base::CommandLine
* cmd_line
= base::CommandLine::ForCurrentProcess();
214 if (!cmd_line
->HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode
))
215 encoder
.reset(new VaapiVideoEncodeAccelerator());
217 return encoder
.Pass();
221 scoped_ptr
<media::VideoEncodeAccelerator
>
222 GpuVideoEncodeAccelerator::CreateAndroidVEA() {
223 scoped_ptr
<media::VideoEncodeAccelerator
> encoder
;
224 #if defined(OS_ANDROID) && defined(ENABLE_WEBRTC)
225 encoder
.reset(new AndroidVideoEncodeAccelerator());
227 return encoder
.Pass();
230 void GpuVideoEncodeAccelerator::OnEncode(int32 frame_id
,
231 base::SharedMemoryHandle buffer_handle
,
232 uint32 buffer_offset
,
234 bool force_keyframe
) {
235 DVLOG(3) << "GpuVideoEncodeAccelerator::OnEncode(): frame_id=" << frame_id
236 << ", buffer_size=" << buffer_size
237 << ", force_keyframe=" << force_keyframe
;
241 DLOG(ERROR
) << "GpuVideoEncodeAccelerator::OnEncode(): invalid frame_id="
243 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
247 uint32 aligned_offset
=
248 buffer_offset
% base::SysInfo::VMAllocationGranularity();
249 base::CheckedNumeric
<off_t
> map_offset
= buffer_offset
;
250 map_offset
-= aligned_offset
;
251 base::CheckedNumeric
<size_t> map_size
= buffer_size
;
252 map_size
+= aligned_offset
;
254 if (!map_offset
.IsValid() || !map_size
.IsValid()) {
255 DLOG(ERROR
) << "GpuVideoEncodeAccelerator::OnEncode():"
256 << " invalid (buffer_offset,buffer_size)";
257 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
261 scoped_ptr
<base::SharedMemory
> shm(
262 new base::SharedMemory(buffer_handle
, true));
263 if (!shm
->MapAt(map_offset
.ValueOrDie(), map_size
.ValueOrDie())) {
264 DLOG(ERROR
) << "GpuVideoEncodeAccelerator::OnEncode(): "
265 "could not map frame_id=" << frame_id
;
266 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
270 uint8
* shm_memory
= reinterpret_cast<uint8
*>(shm
->memory()) + aligned_offset
;
271 scoped_refptr
<media::VideoFrame
> frame
=
272 media::VideoFrame::WrapExternalSharedMemory(
275 gfx::Rect(input_visible_size_
),
282 frame
->AddDestructionObserver(
283 media::BindToCurrentLoop(
284 base::Bind(&GpuVideoEncodeAccelerator::EncodeFrameFinished
,
285 weak_this_factory_
.GetWeakPtr(),
287 base::Passed(&shm
))));
290 DLOG(ERROR
) << "GpuVideoEncodeAccelerator::OnEncode(): "
291 "could not create VideoFrame for frame_id=" << frame_id
;
292 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
296 encoder_
->Encode(frame
, force_keyframe
);
299 void GpuVideoEncodeAccelerator::OnUseOutputBitstreamBuffer(
301 base::SharedMemoryHandle buffer_handle
,
302 uint32 buffer_size
) {
303 DVLOG(3) << "GpuVideoEncodeAccelerator::OnUseOutputBitstreamBuffer(): "
304 "buffer_id=" << buffer_id
305 << ", buffer_size=" << buffer_size
;
309 DLOG(ERROR
) << "GpuVideoEncodeAccelerator::OnUseOutputBitstreamBuffer(): "
310 "invalid buffer_id=" << buffer_id
;
311 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
314 if (buffer_size
< output_buffer_size_
) {
315 DLOG(ERROR
) << "GpuVideoEncodeAccelerator::OnUseOutputBitstreamBuffer(): "
316 "buffer too small for buffer_id=" << buffer_id
;
317 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError
);
320 encoder_
->UseOutputBitstreamBuffer(
321 media::BitstreamBuffer(buffer_id
, buffer_handle
, buffer_size
));
324 void GpuVideoEncodeAccelerator::OnDestroy() {
325 DVLOG(2) << "GpuVideoEncodeAccelerator::OnDestroy()";
329 void GpuVideoEncodeAccelerator::OnRequestEncodingParametersChange(
332 DVLOG(2) << "GpuVideoEncodeAccelerator::OnRequestEncodingParametersChange(): "
333 "bitrate=" << bitrate
334 << ", framerate=" << framerate
;
337 encoder_
->RequestEncodingParametersChange(bitrate
, framerate
);
340 void GpuVideoEncodeAccelerator::EncodeFrameFinished(
342 scoped_ptr
<base::SharedMemory
> shm
) {
343 Send(new AcceleratedVideoEncoderHostMsg_NotifyInputDone(host_route_id_
,
345 // Just let shm fall out of scope.
348 void GpuVideoEncodeAccelerator::Send(IPC::Message
* message
) {
349 stub_
->channel()->Send(message
);
352 void GpuVideoEncodeAccelerator::SendCreateEncoderReply(IPC::Message
* message
,
354 GpuCommandBufferMsg_CreateVideoEncoder::WriteReplyParams(message
, succeeded
);
358 } // namespace content