OLD | NEW |
---|---|
(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/vaapi_jpeg_decode_accelerator.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/logging.h" | |
9 #include "base/metrics/histogram.h" | |
10 #include "base/stl_util.h" | |
11 #include "base/strings/string_util.h" | |
12 #include "base/synchronization/waitable_event.h" | |
13 #include "content/common/gpu/gpu_channel.h" | |
14 #include "content/common/gpu/media/vaapi_picture.h" | |
15 #include "media/base/bind_to_current_loop.h" | |
16 #include "media/base/video_frame.h" | |
17 #include "media/filters/jpeg_parser.h" | |
18 #include "media/video/picture.h" | |
19 #include "ui/gl/gl_bindings.h" | |
20 | |
21 static void ReportVaapiError() { | |
22 } | |
23 | |
24 namespace content { | |
25 | |
26 #define RETURN_AND_NOTIFY_ON_FAILURE(result, log, buffer_id, error_code, ret) \ | |
27 do { \ | |
28 if (!(result)) { \ | |
29 LOG(ERROR) << log; \ | |
30 NotifyError(buffer_id, error_code); \ | |
31 return ret; \ | |
32 } \ | |
33 } while (0) | |
34 | |
35 // XXX | |
36 #undef DLOG | |
37 #define DLOG LOG | |
38 | |
39 VaapiJpegDecodeAccelerator::InputBuffer::InputBuffer() : id(0), size(0) { | |
40 } | |
41 | |
42 VaapiJpegDecodeAccelerator::InputBuffer::~InputBuffer() { | |
43 } | |
44 | |
45 void VaapiJpegDecodeAccelerator::NotifyError(int32_t bitstream_buffer_id, | |
46 Error error) { | |
47 if (message_loop_ != base::MessageLoop::current()) { | |
48 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); | |
49 message_loop_->PostTask(FROM_HERE, | |
50 base::Bind(&VaapiJpegDecodeAccelerator::NotifyError, | |
51 weak_this_, | |
52 bitstream_buffer_id, | |
53 error)); | |
54 return; | |
55 } | |
56 | |
57 // Post Cleanup() as a task so we don't recursively acquire lock_. | |
58 message_loop_->PostTask(FROM_HERE, base::Bind( | |
59 &VaapiJpegDecodeAccelerator::Cleanup, weak_this_)); | |
60 | |
61 LOG(ERROR) << "Notifying of error " << error; | |
62 if (client_) { | |
63 client_->NotifyError(bitstream_buffer_id, error); | |
64 client_ptr_factory_.reset(); | |
65 } | |
66 } | |
67 | |
68 VaapiJpegDecodeAccelerator::VaapiJpegDecodeAccelerator() | |
69 : state_(kUninitialized), | |
70 input_ready_(&lock_), | |
71 message_loop_(base::MessageLoop::current()), | |
72 decoder_thread_("VaapiDecoderThread"), | |
73 weak_this_factory_(this) { | |
74 weak_this_ = weak_this_factory_.GetWeakPtr(); | |
75 } | |
76 | |
77 VaapiJpegDecodeAccelerator::~VaapiJpegDecodeAccelerator() { | |
78 DCHECK_EQ(message_loop_, base::MessageLoop::current()); | |
79 } | |
80 | |
81 bool VaapiJpegDecodeAccelerator::Initialize(Client* client) { | |
82 DCHECK_EQ(message_loop_, base::MessageLoop::current()); | |
83 | |
84 client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client)); | |
85 client_ = client_ptr_factory_->GetWeakPtr(); | |
86 | |
87 base::AutoLock auto_lock(lock_); | |
88 DCHECK_EQ(state_, kUninitialized); | |
89 | |
90 vaapi_wrapper_ = VaapiWrapper::Create( | |
91 VaapiWrapper::kDecode, VAProfileJPEGBaseline, | |
92 base::Bind(&ReportVaapiError)); | |
93 | |
94 if (!vaapi_wrapper_.get()) { | |
95 LOG(ERROR) << "Failed initializing VAAPI"; | |
wuchengli
2015/03/23 06:30:15
DLOG
kcwu
2015/04/14 20:02:34
Done.
| |
96 return false; | |
97 } | |
98 | |
99 CHECK(decoder_thread_.Start()); | |
100 decoder_thread_proxy_ = decoder_thread_.message_loop_proxy(); | |
101 | |
102 return true; | |
103 } | |
104 | |
105 bool VaapiJpegDecodeAccelerator::OutputPicture( | |
106 VASurfaceID va_surface_id, | |
107 int32_t input_id, | |
108 const scoped_refptr<media::VideoFrame>& video_frame) { | |
109 DCHECK_EQ(message_loop_, base::MessageLoop::current()); | |
110 | |
111 DVLOG(3) << "Outputting VASurface " << va_surface_id | |
112 << " into video_frame associate to input buffer id " << input_id; | |
113 | |
114 #if 0 | |
115 RETURN_AND_NOTIFY_ON_FAILURE(picture->DownloadFromSurface(va_surface), | |
116 "Failed putting surface into pixmap", | |
117 input_id, | |
118 PLATFORM_FAILURE, ); | |
119 #endif | |
120 VAImage image; | |
121 VAImageFormat format; | |
122 const uint32_t kI420Fourcc = VA_FOURCC('I', '4', '2', '0'); | |
123 memset(&image, 0, sizeof(image)); | |
124 memset(&format, 0, sizeof(format)); | |
125 format.fourcc = kI420Fourcc; | |
126 format.byte_order = VA_LSB_FIRST; | |
127 format.bits_per_pixel = 12; // 12 for I420 | |
128 | |
129 void* mem; | |
130 gfx::Size coded_size = video_frame->coded_size(); | |
131 if (!vaapi_wrapper_->GetVaImage( | |
132 va_surface_id, &format, coded_size, &image, &mem)) { | |
133 LOG(ERROR) << "Cannot get VAImage"; | |
134 return false; | |
135 } | |
136 | |
137 uint8* frame_mem = video_frame->data(media::VideoFrame::kYPlane); | |
138 size_t frame_buffer_size = | |
139 media::VideoFrame::AllocationSize(media::VideoFrame::I420, coded_size); | |
140 memcpy(frame_mem, mem, frame_buffer_size); | |
141 | |
142 vaapi_wrapper_->ReturnVaImage(&image); | |
143 | |
144 if (client_) | |
145 client_->VideoFrameReady(input_id); | |
146 | |
147 return true; | |
148 } | |
149 | |
150 void VaapiJpegDecodeAccelerator::MapAndQueueNewInputBuffer( | |
151 const media::BitstreamBuffer& bitstream_buffer, | |
152 const scoped_refptr<media::VideoFrame>& video_frame) { | |
153 DCHECK_EQ(message_loop_, base::MessageLoop::current()); | |
154 | |
155 DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id() | |
156 << " size: " << (int)bitstream_buffer.size(); | |
157 | |
158 scoped_ptr<base::SharedMemory> shm( | |
159 new base::SharedMemory(bitstream_buffer.handle(), true)); | |
160 RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(bitstream_buffer.size()), | |
161 "Failed to map input buffer", | |
162 bitstream_buffer.id(), | |
163 UNREADABLE_INPUT,); | |
164 | |
165 base::AutoLock auto_lock(lock_); | |
166 | |
167 // Set up a new input buffer and queue it for later. | |
168 linked_ptr<InputBuffer> input_buffer(new InputBuffer()); | |
169 input_buffer->shm.reset(shm.release()); | |
170 input_buffer->id = bitstream_buffer.id(); | |
171 input_buffer->size = bitstream_buffer.size(); | |
172 input_buffer->video_frame = video_frame; | |
173 | |
174 input_buffers_.push(input_buffer); | |
175 input_ready_.Signal(); | |
176 } | |
177 | |
178 bool VaapiJpegDecodeAccelerator::GetInputBuffer_Locked() { | |
179 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); | |
180 lock_.AssertAcquired(); | |
181 | |
182 if (curr_input_buffer_.get()) | |
183 return true; | |
184 | |
185 // Will only wait if it is expected that in current state new buffers will | |
186 // be queued from the client via Decode(). The state can change during wait. | |
187 while (input_buffers_.empty() && (state_ == kDecoding || state_ == kIdle)) { | |
188 input_ready_.Wait(); | |
189 } | |
190 | |
191 // We could have got woken up in a different state or never got to sleep | |
192 // due to current state; check for that. | |
193 switch (state_) { | |
194 case kDecoding: | |
195 case kIdle: | |
196 DCHECK(!input_buffers_.empty()); | |
197 | |
198 curr_input_buffer_ = input_buffers_.front(); | |
199 input_buffers_.pop(); | |
200 | |
201 DVLOG(4) << "New current bitstream buffer, id: " | |
202 << curr_input_buffer_->id | |
203 << " size: " << curr_input_buffer_->size; | |
204 | |
205 return true; | |
206 | |
207 default: | |
208 // We got woken up due to being destroyed/reset, ignore any already | |
209 // queued inputs. | |
210 return false; | |
211 } | |
212 } | |
213 | |
214 void VaapiJpegDecodeAccelerator::DecodeTask() { | |
215 DCHECK(decoder_thread_proxy_->BelongsToCurrentThread()); | |
216 base::AutoLock auto_lock(lock_); | |
217 | |
218 if (state_ != kDecoding) | |
219 return; | |
220 | |
221 if (GetInputBuffer_Locked()) { | |
222 DCHECK(curr_input_buffer_.get()); | |
223 | |
224 do { | |
225 base::AutoUnlock auto_unlock(lock_); | |
226 | |
227 media::JpegParseResult parse_result; | |
228 | |
229 if (!media::ParseJpegPicture( | |
230 reinterpret_cast<const uint8_t*>(curr_input_buffer_->shm->memory()), | |
231 curr_input_buffer_->size, | |
232 &parse_result)) { | |
233 NotifyError(curr_input_buffer_->id, | |
234 media::JpegDecodeAccelerator::PARSE_JPEG_FAILED); | |
235 break; | |
236 } | |
237 | |
238 gfx::Size coded_size(parse_result.frame_header.coded_width, | |
239 parse_result.frame_header.coded_height); | |
240 | |
241 vaapi_wrapper_->DestroySurfaces(); // XXX assume one frame at a time | |
242 std::vector<VASurfaceID> va_surfaces; | |
243 if (!vaapi_wrapper_->CreateSurfaces(coded_size, 1, &va_surfaces)) | |
244 break; | |
245 | |
246 if (!VaapiJpegDecoder::Decode(vaapi_wrapper_.get(), parse_result, | |
247 va_surfaces[0])) { | |
248 // TODO UNSUPPORTED_JPEG ? | |
249 DLOG(ERROR) << "Decode failed"; | |
250 NotifyError(curr_input_buffer_->id, | |
251 media::JpegDecodeAccelerator::PLATFORM_FAILURE); | |
252 break; | |
253 } | |
254 | |
255 if (!OutputPicture(va_surfaces[0], curr_input_buffer_->id, | |
256 curr_input_buffer_->video_frame)) { | |
257 DLOG(ERROR) << "Output failed"; | |
258 NotifyError(curr_input_buffer_->id, | |
259 media::JpegDecodeAccelerator::PLATFORM_FAILURE); | |
260 break; | |
261 } | |
262 vaapi_wrapper_->DestroySurfaces(); | |
263 } while (0); | |
264 curr_input_buffer_.reset(); | |
265 | |
266 } | |
267 state_ = kIdle; | |
268 } | |
269 | |
270 void VaapiJpegDecodeAccelerator::Decode( | |
271 const media::BitstreamBuffer& bitstream_buffer, | |
272 const scoped_refptr<media::VideoFrame>& video_frame) { | |
273 DCHECK_EQ(message_loop_, base::MessageLoop::current()); | |
wuchengli
2015/03/23 06:30:15
Decode runs on IO thread. Right? DCHECK(io_message
kcwu
2015/04/16 14:38:26
Done.
| |
274 | |
275 | |
276 // We got a new input buffer from the client, map it and queue for later use. | |
277 MapAndQueueNewInputBuffer(bitstream_buffer, video_frame); | |
278 | |
279 base::AutoLock auto_lock(lock_); | |
280 state_ = kDecoding; | |
281 decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind( | |
282 &VaapiJpegDecodeAccelerator::DecodeTask, | |
283 base::Unretained(this))); | |
284 } | |
285 | |
286 void VaapiJpegDecodeAccelerator::Cleanup() { | |
287 DCHECK_EQ(message_loop_, base::MessageLoop::current()); | |
288 | |
289 if (state_ == kUninitialized || state_ == kDestroying) | |
290 return; | |
291 | |
292 DVLOG(1) << "Destroying VAVDA"; | |
293 base::AutoLock auto_lock(lock_); | |
294 state_ = kDestroying; | |
295 | |
296 client_ptr_factory_.reset(); | |
297 weak_this_factory_.InvalidateWeakPtrs(); | |
298 | |
299 // Signal all potential waiters on the decoder_thread_, let them early-exit, | |
300 // as we've just moved to the kDestroying state, and wait for all tasks | |
301 // to finish. | |
302 input_ready_.Signal(); | |
303 { | |
304 base::AutoUnlock auto_unlock(lock_); | |
305 decoder_thread_.Stop(); | |
306 } | |
307 | |
308 state_ = kUninitialized; | |
309 } | |
310 | |
311 void VaapiJpegDecodeAccelerator::Destroy() { | |
312 DCHECK_EQ(message_loop_, base::MessageLoop::current()); | |
313 Cleanup(); | |
314 delete this; | |
315 } | |
316 | |
317 } // namespace content | |
OLD | NEW |