GPU workaround to simulate Out of Memory errors with large textures
[chromium-blink-merge.git] / content / common / gpu / client / gpu_video_encode_accelerator_host.cc
blob2082dade72b0d88cba2301da1ae1d3cd5e7a7ae6
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/client/gpu_video_encode_accelerator_host.h"
7 #include "base/logging.h"
8 #include "base/message_loop/message_loop_proxy.h"
9 #include "content/common/gpu/client/gpu_channel_host.h"
10 #include "content/common/gpu/gpu_messages.h"
11 #include "content/common/gpu/media/gpu_video_encode_accelerator.h"
12 #include "media/base/video_frame.h"
14 namespace content {
16 #define NOTIFY_ERROR(error) \
17 PostNotifyError(error); \
18 DLOG(ERROR)
20 GpuVideoEncodeAcceleratorHost::GpuVideoEncodeAcceleratorHost(
21 GpuChannelHost* channel,
22 CommandBufferProxyImpl* impl)
23 : channel_(channel),
24 encoder_route_id_(MSG_ROUTING_NONE),
25 client_(NULL),
26 impl_(impl),
27 next_frame_id_(0),
28 weak_this_factory_(this) {
29 DCHECK(channel_);
30 DCHECK(impl_);
31 impl_->AddDeletionObserver(this);
34 GpuVideoEncodeAcceleratorHost::~GpuVideoEncodeAcceleratorHost() {
35 DCHECK(CalledOnValidThread());
36 if (channel_ && encoder_route_id_ != MSG_ROUTING_NONE)
37 channel_->RemoveRoute(encoder_route_id_);
38 if (impl_)
39 impl_->RemoveDeletionObserver(this);
42 bool GpuVideoEncodeAcceleratorHost::OnMessageReceived(
43 const IPC::Message& message) {
44 bool handled = true;
45 IPC_BEGIN_MESSAGE_MAP(GpuVideoEncodeAcceleratorHost, message)
46 IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderHostMsg_RequireBitstreamBuffers,
47 OnRequireBitstreamBuffers)
48 IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderHostMsg_NotifyInputDone,
49 OnNotifyInputDone)
50 IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderHostMsg_BitstreamBufferReady,
51 OnBitstreamBufferReady)
52 IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderHostMsg_NotifyError,
53 OnNotifyError)
54 IPC_MESSAGE_UNHANDLED(handled = false)
55 IPC_END_MESSAGE_MAP()
56 DCHECK(handled);
57 // See OnNotifyError for why |this| mustn't be used after OnNotifyError might
58 // have been called above.
59 return handled;
62 void GpuVideoEncodeAcceleratorHost::OnChannelError() {
63 DCHECK(CalledOnValidThread());
64 if (channel_) {
65 if (encoder_route_id_ != MSG_ROUTING_NONE)
66 channel_->RemoveRoute(encoder_route_id_);
67 channel_ = NULL;
69 NOTIFY_ERROR(kPlatformFailureError) << "OnChannelError()";
72 std::vector<media::VideoEncodeAccelerator::SupportedProfile>
73 GpuVideoEncodeAcceleratorHost::GetSupportedProfiles() {
74 DCHECK(CalledOnValidThread());
75 if (!channel_)
76 return std::vector<media::VideoEncodeAccelerator::SupportedProfile>();
77 return ConvertGpuToMediaProfiles(
78 channel_->gpu_info().video_encode_accelerator_supported_profiles);
81 // Make sure the enum values of media::VideoCodecProfile and
82 // gpu::VideoCodecProfile match.
83 #define STATIC_ASSERT_ENUM_MATCH(name) \
84 static_assert( \
85 media::name == static_cast<media::VideoCodecProfile>(gpu::name), \
86 #name " value must match in media and gpu.")
88 STATIC_ASSERT_ENUM_MATCH(VIDEO_CODEC_PROFILE_UNKNOWN);
89 STATIC_ASSERT_ENUM_MATCH(VIDEO_CODEC_PROFILE_MIN);
90 STATIC_ASSERT_ENUM_MATCH(H264PROFILE_BASELINE);
91 STATIC_ASSERT_ENUM_MATCH(H264PROFILE_MAIN);
92 STATIC_ASSERT_ENUM_MATCH(H264PROFILE_EXTENDED);
93 STATIC_ASSERT_ENUM_MATCH(H264PROFILE_HIGH);
94 STATIC_ASSERT_ENUM_MATCH(H264PROFILE_HIGH10PROFILE);
95 STATIC_ASSERT_ENUM_MATCH(H264PROFILE_HIGH422PROFILE);
96 STATIC_ASSERT_ENUM_MATCH(H264PROFILE_HIGH444PREDICTIVEPROFILE);
97 STATIC_ASSERT_ENUM_MATCH(H264PROFILE_SCALABLEBASELINE);
98 STATIC_ASSERT_ENUM_MATCH(H264PROFILE_SCALABLEHIGH);
99 STATIC_ASSERT_ENUM_MATCH(H264PROFILE_STEREOHIGH);
100 STATIC_ASSERT_ENUM_MATCH(H264PROFILE_MULTIVIEWHIGH);
101 STATIC_ASSERT_ENUM_MATCH(VP8PROFILE_ANY);
102 STATIC_ASSERT_ENUM_MATCH(VP9PROFILE_ANY);
103 STATIC_ASSERT_ENUM_MATCH(VIDEO_CODEC_PROFILE_MAX);
105 std::vector<media::VideoEncodeAccelerator::SupportedProfile>
106 GpuVideoEncodeAcceleratorHost::ConvertGpuToMediaProfiles(const std::vector<
107 gpu::VideoEncodeAcceleratorSupportedProfile>& gpu_profiles) {
108 std::vector<media::VideoEncodeAccelerator::SupportedProfile> profiles;
109 for (size_t i = 0; i < gpu_profiles.size(); i++) {
110 media::VideoEncodeAccelerator::SupportedProfile profile;
111 profile.profile =
112 static_cast<media::VideoCodecProfile>(gpu_profiles[i].profile);
113 profile.max_resolution = gpu_profiles[i].max_resolution;
114 profile.max_framerate_numerator = gpu_profiles[i].max_framerate_numerator;
115 profile.max_framerate_denominator =
116 gpu_profiles[i].max_framerate_denominator;
117 profiles.push_back(profile);
119 return profiles;
122 bool GpuVideoEncodeAcceleratorHost::Initialize(
123 media::VideoFrame::Format input_format,
124 const gfx::Size& input_visible_size,
125 media::VideoCodecProfile output_profile,
126 uint32 initial_bitrate,
127 Client* client) {
128 DCHECK(CalledOnValidThread());
129 client_ = client;
130 if (!impl_) {
131 DLOG(ERROR) << "impl_ destroyed";
132 return false;
135 int32 route_id = channel_->GenerateRouteID();
136 channel_->AddRoute(route_id, weak_this_factory_.GetWeakPtr());
138 bool succeeded = false;
139 Send(new GpuCommandBufferMsg_CreateVideoEncoder(impl_->GetRouteID(),
140 input_format,
141 input_visible_size,
142 output_profile,
143 initial_bitrate,
144 route_id,
145 &succeeded));
146 if (!succeeded) {
147 DLOG(ERROR) << "Send(GpuCommandBufferMsg_CreateVideoEncoder()) failed";
148 channel_->RemoveRoute(route_id);
149 return false;
151 encoder_route_id_ = route_id;
152 return true;
155 void GpuVideoEncodeAcceleratorHost::Encode(
156 const scoped_refptr<media::VideoFrame>& frame,
157 bool force_keyframe) {
158 DCHECK(CalledOnValidThread());
159 if (!channel_)
160 return;
162 if (!base::SharedMemory::IsHandleValid(frame->shared_memory_handle())) {
163 NOTIFY_ERROR(kPlatformFailureError)
164 << "Encode(): cannot encode frame not backed by shared memory";
165 return;
167 base::SharedMemoryHandle handle =
168 channel_->ShareToGpuProcess(frame->shared_memory_handle());
169 if (!base::SharedMemory::IsHandleValid(handle)) {
170 NOTIFY_ERROR(kPlatformFailureError)
171 << "Encode(): failed to duplicate buffer handle for GPU process";
172 return;
175 // We assume that planar frame data passed here is packed and contiguous.
176 const size_t plane_count = media::VideoFrame::NumPlanes(frame->format());
177 size_t frame_size = 0;
178 for (size_t i = 0; i < plane_count; ++i) {
179 // Cast DCHECK parameters to void* to avoid printing uint8* as a string.
180 DCHECK_EQ(reinterpret_cast<void*>(frame->data(i)),
181 reinterpret_cast<void*>((frame->data(0) + frame_size)))
182 << "plane=" << i;
183 frame_size += frame->stride(i) * frame->rows(i);
186 Send(new AcceleratedVideoEncoderMsg_Encode(
187 encoder_route_id_, next_frame_id_, handle, frame->shared_memory_offset(),
188 frame_size, force_keyframe));
189 frame_map_[next_frame_id_] = frame;
191 // Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
192 next_frame_id_ = (next_frame_id_ + 1) & 0x3FFFFFFF;
195 void GpuVideoEncodeAcceleratorHost::UseOutputBitstreamBuffer(
196 const media::BitstreamBuffer& buffer) {
197 DCHECK(CalledOnValidThread());
198 if (!channel_)
199 return;
201 base::SharedMemoryHandle handle =
202 channel_->ShareToGpuProcess(buffer.handle());
203 if (!base::SharedMemory::IsHandleValid(handle)) {
204 NOTIFY_ERROR(kPlatformFailureError)
205 << "UseOutputBitstreamBuffer(): failed to duplicate buffer handle "
206 "for GPU process: buffer.id()=" << buffer.id();
207 return;
209 Send(new AcceleratedVideoEncoderMsg_UseOutputBitstreamBuffer(
210 encoder_route_id_, buffer.id(), handle, buffer.size()));
213 void GpuVideoEncodeAcceleratorHost::RequestEncodingParametersChange(
214 uint32 bitrate,
215 uint32 framerate) {
216 DCHECK(CalledOnValidThread());
217 if (!channel_)
218 return;
220 Send(new AcceleratedVideoEncoderMsg_RequestEncodingParametersChange(
221 encoder_route_id_, bitrate, framerate));
224 void GpuVideoEncodeAcceleratorHost::Destroy() {
225 DCHECK(CalledOnValidThread());
226 if (channel_)
227 Send(new AcceleratedVideoEncoderMsg_Destroy(encoder_route_id_));
228 client_ = NULL;
229 delete this;
232 void GpuVideoEncodeAcceleratorHost::OnWillDeleteImpl() {
233 DCHECK(CalledOnValidThread());
234 impl_ = NULL;
236 // The CommandBufferProxyImpl is going away; error out this VEA.
237 OnChannelError();
240 void GpuVideoEncodeAcceleratorHost::PostNotifyError(Error error) {
241 DCHECK(CalledOnValidThread());
242 DVLOG(2) << "PostNotifyError(): error=" << error;
243 // Post the error notification back to this thread, to avoid re-entrancy.
244 base::MessageLoopProxy::current()->PostTask(
245 FROM_HERE,
246 base::Bind(&GpuVideoEncodeAcceleratorHost::OnNotifyError,
247 weak_this_factory_.GetWeakPtr(),
248 error));
251 void GpuVideoEncodeAcceleratorHost::Send(IPC::Message* message) {
252 DCHECK(CalledOnValidThread());
253 uint32 message_type = message->type();
254 if (!channel_->Send(message)) {
255 NOTIFY_ERROR(kPlatformFailureError) << "Send(" << message_type
256 << ") failed";
260 void GpuVideoEncodeAcceleratorHost::OnRequireBitstreamBuffers(
261 uint32 input_count,
262 const gfx::Size& input_coded_size,
263 uint32 output_buffer_size) {
264 DCHECK(CalledOnValidThread());
265 DVLOG(2) << "OnRequireBitstreamBuffers(): input_count=" << input_count
266 << ", input_coded_size=" << input_coded_size.ToString()
267 << ", output_buffer_size=" << output_buffer_size;
268 if (client_) {
269 client_->RequireBitstreamBuffers(
270 input_count, input_coded_size, output_buffer_size);
274 void GpuVideoEncodeAcceleratorHost::OnNotifyInputDone(int32 frame_id) {
275 DCHECK(CalledOnValidThread());
276 DVLOG(3) << "OnNotifyInputDone(): frame_id=" << frame_id;
277 // Fun-fact: std::hash_map is not spec'd to be re-entrant; since freeing a
278 // frame can trigger a further encode to be kicked off and thus an .insert()
279 // back into the map, we separate the frame's dtor running from the .erase()
280 // running by holding on to the frame temporarily. This isn't "just
281 // theoretical" - Android's std::hash_map crashes if we don't do this.
282 scoped_refptr<media::VideoFrame> frame = frame_map_[frame_id];
283 if (!frame_map_.erase(frame_id)) {
284 DLOG(ERROR) << "OnNotifyInputDone(): "
285 "invalid frame_id=" << frame_id;
286 // See OnNotifyError for why this needs to be the last thing in this
287 // function.
288 OnNotifyError(kPlatformFailureError);
289 return;
291 frame = NULL; // Not necessary but nice to be explicit; see fun-fact above.
294 void GpuVideoEncodeAcceleratorHost::OnBitstreamBufferReady(
295 int32 bitstream_buffer_id,
296 uint32 payload_size,
297 bool key_frame) {
298 DCHECK(CalledOnValidThread());
299 DVLOG(3) << "OnBitstreamBufferReady(): "
300 "bitstream_buffer_id=" << bitstream_buffer_id
301 << ", payload_size=" << payload_size
302 << ", key_frame=" << key_frame;
303 if (client_)
304 client_->BitstreamBufferReady(bitstream_buffer_id, payload_size, key_frame);
307 void GpuVideoEncodeAcceleratorHost::OnNotifyError(Error error) {
308 DCHECK(CalledOnValidThread());
309 DVLOG(2) << "OnNotifyError(): error=" << error;
310 if (!client_)
311 return;
312 weak_this_factory_.InvalidateWeakPtrs();
314 // Client::NotifyError() may Destroy() |this|, so calling it needs to be the
315 // last thing done on this stack!
316 media::VideoEncodeAccelerator::Client* client = NULL;
317 std::swap(client_, client);
318 client->NotifyError(error);
321 } // namespace content