Chromium Code Reviews| 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 |