Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(61)

Side by Side Diff: content/common/gpu/media/gpu_jpeg_decode_accelerator.cc

Issue 1124423008: MJPEG acceleration for video capture using VAAPI, the GPU and IPC part (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@mjpeg-1-media
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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/media/gpu_jpeg_decode_accelerator.h"
6
7 #include <stdint.h>
8
9 #include <unordered_map>
10
11 #include "base/bind.h"
12 #include "base/logging.h"
13 #include "base/memory/shared_memory.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/stl_util.h"
16 #include "base/trace_event/trace_event.h"
17 #include "content/common/gpu/gpu_channel.h"
18 #include "content/common/gpu/gpu_messages.h"
19 #include "ipc/ipc_message_macros.h"
20 #include "ipc/message_filter.h"
21 #include "media/filters/jpeg_parser.h"
22 #include "ui/gfx/geometry/size.h"
23
24 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
25 #include "content/common/gpu/media/vaapi_jpeg_decode_accelerator.h"
26 #endif
27
28 namespace {
29
30 void DecodeFinished(scoped_ptr<base::SharedMemory> shm) {
31 // Do nothing. Because VideoFrame is backed by |shm|, the purpose of this
32 // function is to just keep reference of |shm| to make sure it lives util
33 // decode finishes.
34 }
35
36 } // namespace
37
38 namespace content {
39
40 class GpuJpegDecodeAccelerator::Client
41 : public media::JpegDecodeAccelerator::Client,
42 public base::NonThreadSafe {
43 public:
44 Client(content::GpuJpegDecodeAccelerator* owner, int32 route_id)
45 : owner_(owner->AsWeakPtr()), route_id_(route_id) {}
46
47 ~Client() { DCHECK(CalledOnValidThread()); }
48
49 // media::JpegDecodeAccelerator::Client implementation.
50 void VideoFrameReady(int32_t bitstream_buffer_id) override {
51 DCHECK(CalledOnValidThread());
52 if (owner_)
53 owner_->NotifyDecodeStatus(route_id_, bitstream_buffer_id,
54 media::JpegDecodeAccelerator::NO_ERROR);
55 }
56
57 void NotifyError(int32_t bitstream_buffer_id,
58 media::JpegDecodeAccelerator::Error error) override {
59 DCHECK(CalledOnValidThread());
60 if (owner_)
61 owner_->NotifyDecodeStatus(route_id_, bitstream_buffer_id, error);
62 }
63
64 void Decode(const media::BitstreamBuffer& bitstream_buffer,
65 const scoped_refptr<media::VideoFrame>& video_frame) {
66 DCHECK(CalledOnValidThread());
67 DCHECK(accelerator_);
68 accelerator_->Decode(bitstream_buffer, video_frame);
69 }
70
71 void set_accelerator(scoped_ptr<media::JpegDecodeAccelerator> accelerator) {
72 DCHECK(CalledOnValidThread());
73 accelerator_ = accelerator.Pass();
74 }
75
76 private:
77 base::WeakPtr<content::GpuJpegDecodeAccelerator> owner_;
78 int32 route_id_;
79 scoped_ptr<media::JpegDecodeAccelerator> accelerator_;
80 };
81
82 // Create, destroy, and RemoveClient run on child thread. All other methods run
83 // on IO thread.
84 class GpuJpegDecodeAccelerator::MessageFilter : public IPC::MessageFilter {
85 public:
86 explicit MessageFilter(GpuJpegDecodeAccelerator* owner)
87 : owner_(owner->AsWeakPtr()),
88 child_task_runner_(owner_->child_task_runner_),
89 io_task_runner_(owner_->io_task_runner_) {}
90
91 void OnChannelError() override { sender_ = nullptr; }
92
93 void OnChannelClosing() override { sender_ = nullptr; }
94
95 void OnFilterAdded(IPC::Sender* sender) override { sender_ = sender; }
96
97 bool OnMessageReceived(const IPC::Message& msg) override {
98 const int32 route_id = msg.routing_id();
99 if (client_map_.find(route_id) == client_map_.end())
100 return false;
101
102 bool handled = true;
103 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(MessageFilter, msg, &route_id)
104 IPC_MESSAGE_HANDLER(AcceleratedJpegDecoderMsg_Decode, OnDecodeOnIOThread)
105 IPC_MESSAGE_HANDLER(AcceleratedJpegDecoderMsg_Destroy,
106 OnDestroyOnIOThread)
107 IPC_MESSAGE_UNHANDLED(handled = false)
108 IPC_END_MESSAGE_MAP()
109 return handled;
110 }
111
112 bool SendOnIOThread(IPC::Message* message) {
113 DCHECK(!message->is_sync());
114 if (!sender_) {
115 delete message;
116 return false;
117 }
118 return sender_->Send(message);
119 }
120
121 void AddClientOnIOThread(int32 route_id,
122 Client* client,
123 IPC::Message* reply_msg) {
124 DCHECK(io_task_runner_->BelongsToCurrentThread());
125 DCHECK(client_map_.count(route_id) == 0);
126
127 client_map_[route_id] = client;
128 GpuMsg_CreateJpegDecoder::WriteReplyParams(reply_msg, true);
129 SendOnIOThread(reply_msg);
130 }
131
132 void OnDestroyOnIOThread(const int32* route_id) {
133 DCHECK(io_task_runner_->BelongsToCurrentThread());
134 const auto& it = client_map_.find(*route_id);
135 DCHECK(it != client_map_.end());
136 Client* client = it->second;
137 DCHECK(client);
138 client_map_.erase(it);
139
140 child_task_runner_->PostTask(
141 FROM_HERE, base::Bind(&MessageFilter::DestroyClient, this, client));
142 }
143
144 void DestroyClient(Client* client) {
145 DCHECK(child_task_runner_->BelongsToCurrentThread());
146 delete client;
147 if (owner_)
148 owner_->ClientRemoved();
149 }
150
151 void NotifyDecodeStatusOnIOThread(int32 route_id,
152 int32_t buffer_id,
153 media::JpegDecodeAccelerator::Error error) {
154 DCHECK(io_task_runner_->BelongsToCurrentThread());
155 SendOnIOThread(new AcceleratedJpegDecoderHostMsg_DecodeAck(
156 route_id, buffer_id, error));
157 }
158
159 void OnDecodeOnIOThread(
160 const int32* route_id,
161 const AcceleratedJpegDecoderMsg_Decode_Params& params) {
162 DCHECK(io_task_runner_->BelongsToCurrentThread());
163 DCHECK(route_id);
164 TRACE_EVENT0("jpeg", "GpuJpegDecodeAccelerator::MessageFilter::OnDecode");
165
166 // For handles in |params|, |params.output_video_frame_handle| is wrapped
167 // into scoper first. |params.input_buffer_handle| is closed manually for
168 // early exits.
169 scoped_ptr<base::SharedMemory> output_shm(
170 new base::SharedMemory(params.output_video_frame_handle, false));
171 if (!output_shm->Map(params.output_buffer_size)) {
172 LOG(ERROR) << "Could not map output shared memory for input buffer id "
173 << params.input_buffer_id;
174 NotifyDecodeStatusOnIOThread(
175 *route_id, params.input_buffer_id,
176 media::JpegDecodeAccelerator::PLATFORM_FAILURE);
177 base::SharedMemory::CloseHandle(params.input_buffer_handle);
178 return;
179 }
180
181 if (params.input_buffer_id < 0) {
182 LOG(ERROR) << "BitstreamBuffer id " << params.input_buffer_id
183 << " out of range";
184 NotifyDecodeStatusOnIOThread(
185 *route_id, params.input_buffer_id,
186 media::JpegDecodeAccelerator::INVALID_ARGUMENT);
187 base::SharedMemory::CloseHandle(params.input_buffer_handle);
188 return;
189 }
190
191 media::BitstreamBuffer input_buffer(params.input_buffer_id,
192 params.input_buffer_handle,
193 params.input_buffer_size);
194
195 uint8_t* shm_memory = static_cast<uint8_t*>(output_shm->memory());
196 scoped_refptr<media::VideoFrame> frame =
197 media::VideoFrame::WrapExternalSharedMemory(
198 media::VideoFrame::I420, // format
199 params.coded_size, // coded_size
200 gfx::Rect(params.coded_size), // visible_rect
201 params.coded_size, // natural_size
202 shm_memory, // data
203 params.output_buffer_size, // data_size
204 params.output_video_frame_handle, // handle
205 0, // data_offset
206 base::TimeDelta()); // timestamp
207 frame->AddDestructionObserver(
208 base::Bind(DecodeFinished, base::Passed(&output_shm)));
209
210 if (!frame.get()) {
211 LOG(ERROR) << "Could not create VideoFrame for input buffer id "
212 << params.input_buffer_id;
213 NotifyDecodeStatusOnIOThread(
214 *route_id, params.input_buffer_id,
215 media::JpegDecodeAccelerator::PLATFORM_FAILURE);
216 base::SharedMemory::CloseHandle(params.input_buffer_handle);
217 return;
218 }
219
220 DCHECK_GT(client_map_.count(*route_id), 0u);
221 Client* client = client_map_[*route_id];
222 client->Decode(input_buffer, frame);
223 }
224
225 protected:
226 ~MessageFilter() override {
227 if (client_map_.empty())
228 return;
229
230 if (child_task_runner_->BelongsToCurrentThread()) {
231 STLDeleteValues(&client_map_);
232 } else {
233 // Make sure |Client| are deleted on child thread.
234 scoped_ptr<ClientMap> client_map(new ClientMap);
235 client_map->swap(client_map_);
236
237 child_task_runner_->PostTask(
238 FROM_HERE,
239 base::Bind(&DeleteClientMapOnChildThread, base::Passed(&client_map)));
240 }
241 }
242
243 private:
244 using ClientMap = std::unordered_map<int32, Client*>;
piman 2015/06/05 21:49:40 nit: we can't use unordered_map yet because it's a
kcwu 2015/06/08 13:31:09 Done.
245
246 // Must be static because this method runs after destructor.
247 static void DeleteClientMapOnChildThread(scoped_ptr<ClientMap> client_map) {
248 STLDeleteValues(client_map.get());
249 }
250
251 base::WeakPtr<GpuJpegDecodeAccelerator> owner_;
252
253 // GPU child task runner.
254 scoped_refptr<base::SingleThreadTaskRunner> child_task_runner_;
255
256 // GPU IO task runner.
257 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
258
259 // The sender to which this filter was added.
260 IPC::Sender* sender_;
261
262 // A map from route id to JpegDecodeAccelerator.
263 // Unless in destructor (maybe on child thread), |client_map_| should
264 // only be accessed on IO thread.
265 ClientMap client_map_;
266 };
267
268 GpuJpegDecodeAccelerator::GpuJpegDecodeAccelerator(
269 GpuChannel* channel,
270 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
271 : channel_(channel),
272 child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
273 io_task_runner_(io_task_runner),
274 client_number_(0) {
275 }
276
277 GpuJpegDecodeAccelerator::~GpuJpegDecodeAccelerator() {
278 DCHECK(CalledOnValidThread());
279 if (filter_) {
280 channel_->RemoveFilter(filter_.get());
281 }
282 }
283
284 void GpuJpegDecodeAccelerator::AddClient(int32 route_id,
285 IPC::Message* reply_msg) {
286 DCHECK(CalledOnValidThread());
287 scoped_ptr<media::JpegDecodeAccelerator> accelerator;
288
289 // When adding more platforms, GpuJpegDecoder::Supported need
290 // update as well.
291 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
292 accelerator.reset(new VaapiJpegDecodeAccelerator(io_task_runner_));
293 #else
294 DVLOG(1) << "HW JPEG decode acceleration not available.";
295 #endif
296
297 scoped_ptr<Client> client(new Client(this, route_id));
298 if (!accelerator.get() || !accelerator->Initialize(client.get())) {
299 DLOG(ERROR) << "JPEG accelerator Initialize failed";
300 GpuMsg_CreateJpegDecoder::WriteReplyParams(reply_msg, false);
301 Send(reply_msg);
302 return;
303 }
304 client->set_accelerator(accelerator.Pass());
305
306 if (!filter_) {
307 DCHECK_EQ(client_number_, 0);
308 filter_ = new MessageFilter(this);
309 // This should be before AddClientOnIOThread.
310 channel_->AddFilter(filter_.get());
311 }
312 client_number_++;
313
314 // In this PostTask, |client| may leak if |io_task_runner_| is destroyed
315 // before |client| reached AddClientOnIOThread. However we cannot use scoper
316 // to pretect it because |client| can only be deleted on child thread. We
piman 2015/06/05 21:49:40 nit: pretect->protect
kcwu 2015/06/08 13:31:09 Done.
317 // just crossed fingers here instead of making the code unnecessary
318 // complicated.
piman 2015/06/05 21:49:40 nit: maybe you can mention that the IO thread is d
kcwu 2015/06/08 13:31:09 Done.
319 io_task_runner_->PostTask(
320 FROM_HERE, base::Bind(&MessageFilter::AddClientOnIOThread, filter_,
321 route_id, client.release(), reply_msg));
322 }
323
324 void GpuJpegDecodeAccelerator::NotifyDecodeStatus(
325 int32 route_id,
326 int32_t buffer_id,
327 media::JpegDecodeAccelerator::Error error) {
328 DCHECK(CalledOnValidThread());
329 Send(new AcceleratedJpegDecoderHostMsg_DecodeAck(route_id, buffer_id, error));
330 }
331
332 void GpuJpegDecodeAccelerator::ClientRemoved() {
333 DCHECK(CalledOnValidThread());
334 DCHECK_GT(client_number_, 0);
335 client_number_--;
336 if (client_number_ == 0) {
337 channel_->RemoveFilter(filter_.get());
338 filter_ = nullptr;
339 }
340 }
341
342 bool GpuJpegDecodeAccelerator::Send(IPC::Message* message) {
343 DCHECK(CalledOnValidThread());
344 return channel_->Send(message);
345 }
346
347 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698