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

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 <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/synchronization/waitable_event.h"
17 #include "base/trace_event/trace_event.h"
18 #include "content/common/gpu/gpu_channel.h"
19 #include "content/common/gpu/gpu_messages.h"
20 #include "ipc/ipc_message_macros.h"
21 #include "ipc/message_filter.h"
22 #include "media/filters/jpeg_parser.h"
23 #include "ui/gfx/geometry/size.h"
24
25 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
26 #include "content/common/gpu/media/vaapi_jpeg_decode_accelerator.h"
27 #endif
28
29 namespace {
30
31 void DecodeFinished(scoped_ptr<base::SharedMemory> shm) {
32 // Do nothing. Because VideoFrame is backed by |shm|, the purpose of this
33 // function is to just keep reference of |shm| to make sure it lives util
34 // decode finishes.
35 }
36
37 } // namespace
38
39 namespace content {
40
41 class GpuJpegDecodeAccelerator::Client
42 : public media::JpegDecodeAccelerator::Client {
piman 2015/05/28 22:01:40 nit: can you make this NonThreadSafe, and check th
kcwu 2015/05/29 11:11:25 Done.
43 public:
44 Client(content::GpuJpegDecodeAccelerator* owner, int32 route_id)
45 : owner_(owner->AsWeakPtr()), route_id_(route_id) {}
46
47 // media::JpegDecodeAccelerator::Client implementation.
48 void VideoFrameReady(int32_t bitstream_buffer_id) override {
49 if (owner_)
50 owner_->NotifyDecodeStatus(route_id_, bitstream_buffer_id,
51 media::JpegDecodeAccelerator::NO_ERROR);
52 }
53
54 void NotifyError(int32_t bitstream_buffer_id,
55 media::JpegDecodeAccelerator::Error error) override {
56 if (owner_)
57 owner_->NotifyDecodeStatus(route_id_, bitstream_buffer_id, error);
58 }
59
60 void Decode(const media::BitstreamBuffer& bitstream_buffer,
61 const scoped_refptr<media::VideoFrame>& video_frame) {
62 DCHECK(accelerator_);
63 accelerator_->Decode(bitstream_buffer, video_frame);
64 }
65
66 void set_accelerator(scoped_ptr<media::JpegDecodeAccelerator> accelerator) {
67 accelerator_ = accelerator.Pass();
68 }
69
70 private:
71 base::WeakPtr<content::GpuJpegDecodeAccelerator> owner_;
72 int32 route_id_;
73 scoped_ptr<media::JpegDecodeAccelerator> accelerator_;
74 };
75
76 // Create, destroy, and RemoveClient run on child thread. All other methods run
77 // on IO thread.
78 class GpuJpegDecodeAccelerator::MessageFilter : public IPC::MessageFilter {
79 public:
80 explicit MessageFilter(GpuJpegDecodeAccelerator* owner)
81 : owner_(owner->AsWeakPtr()),
82 child_task_runner_(owner_->child_task_runner_),
83 io_task_runner_(owner_->io_task_runner_),
84 filter_removed_(true, false) {}
85
86 void OnChannelError() override { sender_ = nullptr; }
87
88 void OnChannelClosing() override { sender_ = nullptr; }
89
90 void OnFilterAdded(IPC::Sender* sender) override { sender_ = sender; }
91
92 void OnFilterRemoved() override {
93 // After this, |this| could be safely deleted on child thread.
94 filter_removed_.Signal();
95 }
96
97 bool OnMessageReceived(const IPC::Message& msg) override {
98 const int32 route_id = msg.routing_id();
99 if (client_map_.count(route_id) == 0)
piman 2015/05/28 22:01:39 nit: if (client_map_.find(route_id) == client_map_
kcwu 2015/05/29 11:11:24 Done.
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 if (params.input_buffer_id < 0) {
167 LOG(ERROR) << "BitstreamBuffer id " << params.input_buffer_id
168 << " out of range";
169 NotifyDecodeStatusOnIOThread(
170 *route_id, params.input_buffer_id,
171 media::JpegDecodeAccelerator::INVALID_ARGUMENT);
172 return;
173 }
174
175 media::BitstreamBuffer input_buffer(params.input_buffer_id,
176 params.input_buffer_handle,
piman 2015/05/28 22:01:40 input_buffer_handle is only released by Decode, bu
kcwu 2015/05/29 11:11:25 Because SharedMemory takes ownership of SharedMemo
piman 2015/06/01 22:33:36 The documentation in JpegDecodeAccelerator::Decode
kcwu 2015/06/02 15:07:26 The "ownership" are referring to the underlying me
177 params.input_buffer_size);
178
179 scoped_ptr<base::SharedMemory> output_shm(
180 new base::SharedMemory(params.output_video_frame_handle, false));
piman 2015/05/28 22:01:40 You probably want to do this before the first earl
kcwu 2015/05/29 11:11:24 Done.
181 if (!output_shm->Map(params.output_buffer_size)) {
182 LOG(ERROR) << "Could not map output shared memory for input buffer id "
183 << params.input_buffer_id;
184 NotifyDecodeStatusOnIOThread(
185 *route_id, params.input_buffer_id,
186 media::JpegDecodeAccelerator::PLATFORM_FAILURE);
187 return;
188 }
189
190 uint8* shm_memory = reinterpret_cast<uint8*>(output_shm->memory());
piman 2015/05/28 22:01:40 nit: uint8_t instead of uint8, also you can use st
kcwu 2015/05/29 11:11:24 This aligns to VideoFrame::WrapExternalPackedMemor
piman 2015/06/01 22:33:35 Please use uint8_t in new code. uint8 is deprecate
kcwu 2015/06/02 15:07:26 Done.
191 scoped_refptr<media::VideoFrame> frame =
192 media::VideoFrame::WrapExternalPackedMemory(
193 media::VideoFrame::I420,
194 params.coded_size,
195 gfx::Rect(params.coded_size),
196 params.coded_size,
197 shm_memory,
198 params.output_buffer_size,
199 params.output_video_frame_handle,
200 0,
201 base::TimeDelta(),
202 base::Bind(DecodeFinished, base::Passed(&output_shm)));
203
204 if (!frame.get()) {
205 LOG(ERROR) << "Could not create VideoFrame for input buffer id "
206 << params.input_buffer_id;
207 NotifyDecodeStatusOnIOThread(
208 *route_id, params.input_buffer_id,
209 media::JpegDecodeAccelerator::PLATFORM_FAILURE);
210 return;
211 }
212
213 DCHECK_GT(client_map_.count(*route_id), 0u);
214 Client* client = client_map_[*route_id];
215 client->Decode(input_buffer, frame);
216 }
217
218 void WaitForRemoved() { filter_removed_.Wait(); }
219
220 protected:
221 ~MessageFilter() override {
222 if (client_map_.empty())
223 return;
224
225 if (child_task_runner_->BelongsToCurrentThread()) {
226 STLDeleteValues(&client_map_);
227 } else {
228 // Make sure |Client| are deleted on child thread.
229 scoped_ptr<std::map<int32, Client*>> client_map(
230 new std::map<int32, Client*>());
231 std::swap(client_map_, *client_map);
piman 2015/05/28 22:01:39 nit: client_map->swap(client_map_);
kcwu 2015/05/29 11:11:25 Done.
232
233 child_task_runner_->PostTask(
234 FROM_HERE,
235 base::Bind(&DeleteClientMapOnChildThread, base::Passed(&client_map)));
236 }
237 }
238
239 private:
240 // Must be static because this method runs after destructor.
241 static void DeleteClientMapOnChildThread(
242 scoped_ptr<std::map<int32, Client*>> client_map) {
243 STLDeleteValues(client_map.get());
244 }
245
246 base::WeakPtr<GpuJpegDecodeAccelerator> owner_;
247
248 // GPU child task runner.
249 scoped_refptr<base::SingleThreadTaskRunner> child_task_runner_;
250
251 // GPU IO task runner.
252 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
253
254 // The sender to which this filter was added.
255 IPC::Sender* sender_;
256
257 // A map from route id to JpegDecodeAccelerator.
258 // Unless in destructor (maybe on child thread), |client_map_| should
259 // only be accessed on IO thread.
260 std::map<int32, Client*> client_map_;
piman 2015/05/28 22:01:39 nit: hash_map. You can also make a typedef to avoi
kcwu 2015/05/29 11:11:24 Done.
261
262 // Used to wait on for |this| to be removed from the IPC channel, before
263 // we can safely delete |this|.
264 base::WaitableEvent filter_removed_;
265 };
266
267 GpuJpegDecodeAccelerator::GpuJpegDecodeAccelerator(
268 GpuChannel* channel,
269 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
270 : channel_(channel),
271 child_task_runner_(base::ThreadTaskRunnerHandle::Get()),
272 io_task_runner_(io_task_runner),
273 client_number_(0) {
274 }
275
276 GpuJpegDecodeAccelerator::~GpuJpegDecodeAccelerator() {
277 DCHECK(CalledOnValidThread());
278 if (filter_) {
279 channel_->RemoveFilter(filter_.get());
280 filter_->WaitForRemoved();
piman 2015/05/28 22:01:39 As discussed, you don't need this.
kcwu 2015/05/29 11:11:24 Done.
281 }
282 }
283
284 bool GpuJpegDecodeAccelerator::OnMessageReceived(const IPC::Message& msg) {
285 // Messages are actually handled in filter on IO thread.
286 return false;
287 }
288
289 void GpuJpegDecodeAccelerator::AddClient(int32 route_id,
290 IPC::Message* reply_msg) {
291 DCHECK(CalledOnValidThread());
292 scoped_ptr<media::JpegDecodeAccelerator> accelerator;
293
294 // When adding more platforms, GpuJpegDecoder::Supported need
295 // update as well.
296 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
297 accelerator.reset(new VaapiJpegDecodeAccelerator(io_task_runner_));
298 #else
299 DVLOG(1) << "HW JPEG decode acceleration not available.";
300 #endif
301
302 scoped_ptr<Client> client(new Client(this, route_id));
303 if (!accelerator.get() || !accelerator->Initialize(client.get())) {
304 DLOG(ERROR) << "JPEG accelerator Initialize failed";
305 GpuMsg_CreateJpegDecoder::WriteReplyParams(reply_msg, false);
306 Send(reply_msg);
307 return;
308 }
309 client->set_accelerator(accelerator.Pass());
310
311 if (!filter_) {
312 DCHECK_EQ(client_number_, 0);
313 filter_ = new MessageFilter(this);
314 // This should be before AddClientOnIOThread.
315 channel_->AddFilter(filter_.get());
316 }
317 client_number_++;
318
319 io_task_runner_->PostTask(
320 FROM_HERE, base::Bind(&MessageFilter::AddClientOnIOThread, filter_,
321 route_id, client.release(), reply_msg));
piman 2015/05/28 22:01:39 can you use Passed(client) instead?
kcwu 2015/05/29 11:11:25 Does io_task_runner guarantee the task of RefCount
piman 2015/06/01 22:33:36 Fair enough, the message loop would be destroyed o
kcwu 2015/06/02 15:07:25 Done.
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