• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "content/common/gpu/client/gpu_video_encode_accelerator_host.h"
6 
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"
13 
14 namespace content {
15 
16 #define NOTIFY_ERROR(error) \
17   PostNotifyError(error);   \
18   DLOG(ERROR)
19 
GpuVideoEncodeAcceleratorHost(GpuChannelHost * channel,CommandBufferProxyImpl * impl)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);
32 }
33 
~GpuVideoEncodeAcceleratorHost()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);
40 }
41 
42 // static
43 std::vector<media::VideoEncodeAccelerator::SupportedProfile>
GetSupportedProfiles()44 GpuVideoEncodeAcceleratorHost::GetSupportedProfiles() {
45   return GpuVideoEncodeAccelerator::GetSupportedProfiles();
46 }
47 
OnMessageReceived(const IPC::Message & message)48 bool GpuVideoEncodeAcceleratorHost::OnMessageReceived(
49     const IPC::Message& message) {
50   bool handled = true;
51   IPC_BEGIN_MESSAGE_MAP(GpuVideoEncodeAcceleratorHost, message)
52     IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderHostMsg_RequireBitstreamBuffers,
53                         OnRequireBitstreamBuffers)
54     IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderHostMsg_NotifyInputDone,
55                         OnNotifyInputDone)
56     IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderHostMsg_BitstreamBufferReady,
57                         OnBitstreamBufferReady)
58     IPC_MESSAGE_HANDLER(AcceleratedVideoEncoderHostMsg_NotifyError,
59                         OnNotifyError)
60     IPC_MESSAGE_UNHANDLED(handled = false)
61   IPC_END_MESSAGE_MAP()
62   DCHECK(handled);
63   // See OnNotifyError for why |this| mustn't be used after OnNotifyError might
64   // have been called above.
65   return handled;
66 }
67 
OnChannelError()68 void GpuVideoEncodeAcceleratorHost::OnChannelError() {
69   DCHECK(CalledOnValidThread());
70   if (channel_) {
71     if (encoder_route_id_ != MSG_ROUTING_NONE)
72       channel_->RemoveRoute(encoder_route_id_);
73     channel_ = NULL;
74   }
75   NOTIFY_ERROR(kPlatformFailureError) << "OnChannelError()";
76 }
77 
Initialize(media::VideoFrame::Format input_format,const gfx::Size & input_visible_size,media::VideoCodecProfile output_profile,uint32 initial_bitrate,Client * client)78 bool GpuVideoEncodeAcceleratorHost::Initialize(
79     media::VideoFrame::Format input_format,
80     const gfx::Size& input_visible_size,
81     media::VideoCodecProfile output_profile,
82     uint32 initial_bitrate,
83     Client* client) {
84   DCHECK(CalledOnValidThread());
85   client_ = client;
86   if (!impl_) {
87     DLOG(ERROR) << "impl_ destroyed";
88     return false;
89   }
90 
91   int32 route_id = channel_->GenerateRouteID();
92   channel_->AddRoute(route_id, weak_this_factory_.GetWeakPtr());
93 
94   bool succeeded = false;
95   Send(new GpuCommandBufferMsg_CreateVideoEncoder(impl_->GetRouteID(),
96                                                   input_format,
97                                                   input_visible_size,
98                                                   output_profile,
99                                                   initial_bitrate,
100                                                   route_id,
101                                                   &succeeded));
102   if (!succeeded) {
103     DLOG(ERROR) << "Send(GpuCommandBufferMsg_CreateVideoEncoder()) failed";
104     channel_->RemoveRoute(route_id);
105     return false;
106   }
107   encoder_route_id_ = route_id;
108   return true;
109 }
110 
Encode(const scoped_refptr<media::VideoFrame> & frame,bool force_keyframe)111 void GpuVideoEncodeAcceleratorHost::Encode(
112     const scoped_refptr<media::VideoFrame>& frame,
113     bool force_keyframe) {
114   DCHECK(CalledOnValidThread());
115   if (!channel_)
116     return;
117 
118   if (!base::SharedMemory::IsHandleValid(frame->shared_memory_handle())) {
119     NOTIFY_ERROR(kPlatformFailureError)
120         << "Encode(): cannot encode frame not backed by shared memory";
121     return;
122   }
123   base::SharedMemoryHandle handle =
124       channel_->ShareToGpuProcess(frame->shared_memory_handle());
125   if (!base::SharedMemory::IsHandleValid(handle)) {
126     NOTIFY_ERROR(kPlatformFailureError)
127         << "Encode(): failed to duplicate buffer handle for GPU process";
128     return;
129   }
130 
131   // We assume that planar frame data passed here is packed and contiguous.
132   const size_t plane_count = media::VideoFrame::NumPlanes(frame->format());
133   size_t frame_size = 0;
134   for (size_t i = 0; i < plane_count; ++i) {
135     // Cast DCHECK parameters to void* to avoid printing uint8* as a string.
136     DCHECK_EQ(reinterpret_cast<void*>(frame->data(i)),
137               reinterpret_cast<void*>((frame->data(0) + frame_size)))
138         << "plane=" << i;
139     frame_size += frame->stride(i) * frame->rows(i);
140   }
141 
142   Send(new AcceleratedVideoEncoderMsg_Encode(
143       encoder_route_id_, next_frame_id_, handle, frame_size, force_keyframe));
144   frame_map_[next_frame_id_] = frame;
145 
146   // Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
147   next_frame_id_ = (next_frame_id_ + 1) & 0x3FFFFFFF;
148 }
149 
UseOutputBitstreamBuffer(const media::BitstreamBuffer & buffer)150 void GpuVideoEncodeAcceleratorHost::UseOutputBitstreamBuffer(
151     const media::BitstreamBuffer& buffer) {
152   DCHECK(CalledOnValidThread());
153   if (!channel_)
154     return;
155 
156   base::SharedMemoryHandle handle =
157       channel_->ShareToGpuProcess(buffer.handle());
158   if (!base::SharedMemory::IsHandleValid(handle)) {
159     NOTIFY_ERROR(kPlatformFailureError)
160         << "UseOutputBitstreamBuffer(): failed to duplicate buffer handle "
161            "for GPU process: buffer.id()=" << buffer.id();
162     return;
163   }
164   Send(new AcceleratedVideoEncoderMsg_UseOutputBitstreamBuffer(
165       encoder_route_id_, buffer.id(), handle, buffer.size()));
166 }
167 
RequestEncodingParametersChange(uint32 bitrate,uint32 framerate)168 void GpuVideoEncodeAcceleratorHost::RequestEncodingParametersChange(
169     uint32 bitrate,
170     uint32 framerate) {
171   DCHECK(CalledOnValidThread());
172   if (!channel_)
173     return;
174 
175   Send(new AcceleratedVideoEncoderMsg_RequestEncodingParametersChange(
176       encoder_route_id_, bitrate, framerate));
177 }
178 
Destroy()179 void GpuVideoEncodeAcceleratorHost::Destroy() {
180   DCHECK(CalledOnValidThread());
181   if (channel_)
182     Send(new AcceleratedVideoEncoderMsg_Destroy(encoder_route_id_));
183   client_ = NULL;
184   delete this;
185 }
186 
OnWillDeleteImpl()187 void GpuVideoEncodeAcceleratorHost::OnWillDeleteImpl() {
188   DCHECK(CalledOnValidThread());
189   impl_ = NULL;
190 
191   // The CommandBufferProxyImpl is going away; error out this VEA.
192   OnChannelError();
193 }
194 
PostNotifyError(Error error)195 void GpuVideoEncodeAcceleratorHost::PostNotifyError(Error error) {
196   DCHECK(CalledOnValidThread());
197   DVLOG(2) << "PostNotifyError(): error=" << error;
198   // Post the error notification back to this thread, to avoid re-entrancy.
199   base::MessageLoopProxy::current()->PostTask(
200       FROM_HERE,
201       base::Bind(&GpuVideoEncodeAcceleratorHost::OnNotifyError,
202                  weak_this_factory_.GetWeakPtr(),
203                  error));
204 }
205 
Send(IPC::Message * message)206 void GpuVideoEncodeAcceleratorHost::Send(IPC::Message* message) {
207   DCHECK(CalledOnValidThread());
208   uint32 message_type = message->type();
209   if (!channel_->Send(message)) {
210     NOTIFY_ERROR(kPlatformFailureError) << "Send(" << message_type
211                                         << ") failed";
212   }
213 }
214 
OnRequireBitstreamBuffers(uint32 input_count,const gfx::Size & input_coded_size,uint32 output_buffer_size)215 void GpuVideoEncodeAcceleratorHost::OnRequireBitstreamBuffers(
216     uint32 input_count,
217     const gfx::Size& input_coded_size,
218     uint32 output_buffer_size) {
219   DCHECK(CalledOnValidThread());
220   DVLOG(2) << "OnRequireBitstreamBuffers(): input_count=" << input_count
221            << ", input_coded_size=" << input_coded_size.ToString()
222            << ", output_buffer_size=" << output_buffer_size;
223   if (client_) {
224     client_->RequireBitstreamBuffers(
225         input_count, input_coded_size, output_buffer_size);
226   }
227 }
228 
OnNotifyInputDone(int32 frame_id)229 void GpuVideoEncodeAcceleratorHost::OnNotifyInputDone(int32 frame_id) {
230   DCHECK(CalledOnValidThread());
231   DVLOG(3) << "OnNotifyInputDone(): frame_id=" << frame_id;
232   // Fun-fact: std::hash_map is not spec'd to be re-entrant; since freeing a
233   // frame can trigger a further encode to be kicked off and thus an .insert()
234   // back into the map, we separate the frame's dtor running from the .erase()
235   // running by holding on to the frame temporarily.  This isn't "just
236   // theoretical" - Android's std::hash_map crashes if we don't do this.
237   scoped_refptr<media::VideoFrame> frame = frame_map_[frame_id];
238   if (!frame_map_.erase(frame_id)) {
239     DLOG(ERROR) << "OnNotifyInputDone(): "
240                    "invalid frame_id=" << frame_id;
241     // See OnNotifyError for why this needs to be the last thing in this
242     // function.
243     OnNotifyError(kPlatformFailureError);
244     return;
245   }
246   frame = NULL;  // Not necessary but nice to be explicit; see fun-fact above.
247 }
248 
OnBitstreamBufferReady(int32 bitstream_buffer_id,uint32 payload_size,bool key_frame)249 void GpuVideoEncodeAcceleratorHost::OnBitstreamBufferReady(
250     int32 bitstream_buffer_id,
251     uint32 payload_size,
252     bool key_frame) {
253   DCHECK(CalledOnValidThread());
254   DVLOG(3) << "OnBitstreamBufferReady(): "
255               "bitstream_buffer_id=" << bitstream_buffer_id
256            << ", payload_size=" << payload_size
257            << ", key_frame=" << key_frame;
258   if (client_)
259     client_->BitstreamBufferReady(bitstream_buffer_id, payload_size, key_frame);
260 }
261 
OnNotifyError(Error error)262 void GpuVideoEncodeAcceleratorHost::OnNotifyError(Error error) {
263   DCHECK(CalledOnValidThread());
264   DVLOG(2) << "OnNotifyError(): error=" << error;
265   if (!client_)
266     return;
267   weak_this_factory_.InvalidateWeakPtrs();
268 
269   // Client::NotifyError() may Destroy() |this|, so calling it needs to be the
270   // last thing done on this stack!
271   media::VideoEncodeAccelerator::Client* client = NULL;
272   std::swap(client_, client);
273   client->NotifyError(error);
274 }
275 
276 }  // namespace content
277