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

Side by Side Diff: content/browser/renderer_host/gpu_jpeg_decode_accelerator_adapter.cc

Issue 1016773002: MJPEG acceleration for video capture using VAAPI (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Move JPEG related code to separated file. And address some other comments Created 5 years, 8 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/browser/renderer_host/gpu_jpeg_decode_accelerator_adapter.h"
6
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "content/browser/gpu/browser_gpu_channel_host_factory.h"
10 #include "content/common/gpu/client/gpu_jpeg_decode_accelerator_host.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "media/base/video_frame.h"
13
14 namespace content {
15
16 // static
17 bool GpuJpegDecodeAcceleratorAdapter::Supported() {
18 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
19 return true;
20 #endif
21 return false;
22 }
23
24 GpuJpegDecodeAcceleratorAdapter::GpuJpegDecodeAcceleratorAdapter(
25 media::VideoCaptureDevice::Client* device_client)
26 : device_client_(device_client),
27 device_thread_(base::MessageLoopProxy::current()),
28 fail_state_(NOT_FAIL),
29 next_bitstream_buffer_id_(0),
30 in_buffer_(media::JpegDecodeAccelerator::kInvalidBitstreamBufferId,
31 base::SharedMemory::NULLHandle(),
32 0) {
33 // Device thread may be blocked waiting next frame, so it should not be used
34 // to receive decoding response (IO thread).
35 DCHECK_NE(device_thread_,
36 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO));
mcasas 2015/04/16 23:55:13 Please rephrase comment, I find it hard to read, a
kcwu 2015/04/20 17:47:59 I thought again. Since IO thread is not possible b
37 }
38
39 GpuJpegDecodeAcceleratorAdapter::~GpuJpegDecodeAcceleratorAdapter() {
40 DCHECK(device_thread_->BelongsToCurrentThread());
41 }
42
43 bool GpuJpegDecodeAcceleratorAdapter::IsDecoding() {
44 base::AutoLock lock(lock_);
45 return out_frame_ != nullptr;
46 }
47
48 bool GpuJpegDecodeAcceleratorAdapter::IsExpectedDecodeResponse(
49 int32 bitstream_buffer_id) {
50 if (bitstream_buffer_id !=
51 media::JpegDecodeAccelerator::kInvalidBitstreamBufferId) {
mcasas 2015/04/16 23:55:13 Prefer early termination; by the way shouldn't we
kcwu 2015/04/20 17:48:00 kInvalidBitstreamBufferId is possible value for No
mcasas 2015/04/22 01:23:52 The comment says "Check |bitstream_buffer_id| fro
kcwu 2015/04/22 14:43:33 Done. Now I handle kInvalidBitstreamBufferId in No
52 if (!IsDecoding()) {
53 LOG(ERROR) << "Got decode response while not decoding";
54 return false;
55 }
56 if (bitstream_buffer_id != in_buffer_.id()) {
57 LOG(ERROR) << "Unexpected bitstream_buffer_id " << bitstream_buffer_id
58 << ", expected " << in_buffer_.id();
59 return false;
60 }
61 }
62 return true;
63 }
64
65 void GpuJpegDecodeAcceleratorAdapter::DecodeDone() {
66 base::AutoLock lock(lock_);
67 memset(&captured_data_, 0, sizeof(captured_data_));
68 out_buffer_ = nullptr;
69 out_frame_ = nullptr;
70 }
71
72 bool GpuJpegDecodeAcceleratorAdapter::Initialize() {
73 DCHECK(device_thread_->BelongsToCurrentThread());
74 DVLOG(3) << __func__;
mcasas 2015/04/16 23:55:13 pro tip: In Linux you can use __PRETTY_FUNCTION__
kcwu 2015/04/20 17:47:59 Acknowledged.
75 GpuChannelHost* const host =
76 BrowserGpuChannelHostFactory::instance()->GetGpuChannel();
77 decoder_ = host->CreateJpegDecoder(
78 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO));
79 if (!decoder_->Initialize(this)) {
80 decoder_.reset();
81 fail_state_ = FAILED;
82 return false;
83 }
84 return true;
85 }
86
87 bool GpuJpegDecodeAcceleratorAdapter::IsFailed() {
88 base::AutoLock lock(lock_);
89 return fail_state_ == FAILED;
90 }
91
92 void GpuJpegDecodeAcceleratorAdapter::VideoFrameReady(
93 int32_t bitstream_buffer_id) {
94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
95 DVLOG(3) << __func__;
96
97 // Verify parameter from GPU process.
98 if (!IsExpectedDecodeResponse(bitstream_buffer_id))
99 return;
100
101 device_client_->OnIncomingCapturedVideoFrame(out_buffer_, out_frame_,
102 captured_data_.timestamp);
mcasas 2015/04/16 23:55:13 If you discard |captured_data_|, you can use |out_
kcwu 2015/04/20 17:48:00 out_frame_.timestamp() was zero. Can I merge captu
mcasas 2015/04/28 00:04:49 IIRC, VideoFrame is created in VideoCaptureDeviceC
kcwu 2015/05/08 14:42:40 Acknowledged.
103
104 DecodeDone();
105 }
106
107 void GpuJpegDecodeAcceleratorAdapter::FallbackToSoftwareDecode() {
108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO) ||
109 device_thread_->BelongsToCurrentThread());
110 if (BrowserThread::CurrentlyOn(BrowserThread::IO))
111 DCHECK(IsDecoding());
112 else
113 DCHECK(!IsDecoding());
114 DVLOG(1) << "Fallback to software decode";
115
116 CapturedData captured_data;
117 {
118 base::AutoLock lock(lock_);
119 // See comment below.
120 if (BrowserThread::CurrentlyOn(BrowserThread::IO))
121 fail_state_ = FAILING;
122 else
123 fail_state_ = FAILED;
124 captured_data = captured_data_;
125 }
126
127 // If we are on device thread, OnIncomingCapturedData is reentrant. But it's
128 // okay since |fail_state_| is FAILED and it won't go hardware decoding route
129 // this time.
130 // If we are on IO thread and next frame is coming on device thread, the next
131 // frame will call DecodeCapturedData() and drop immediately due to
132 // |fail_state_| is FAILING. This is to avoid next frame decoded before this
133 // frame.
134 device_client_->OnIncomingCapturedData(captured_data.data,
135 captured_data.length,
136 captured_data.frame_format,
137 captured_data.rotation,
138 captured_data.timestamp);
139 {
140 base::AutoLock lock(lock_);
141 fail_state_ = FAILED;
142 }
143 }
144
145 void GpuJpegDecodeAcceleratorAdapter::NotifyError(
146 int32_t bitstream_buffer_id,
147 media::JpegDecodeAccelerator::Error error) {
148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
149 DVLOG(3) << __func__;
150
151 // Verify parameter from GPU process.
152 if (!IsExpectedDecodeResponse(bitstream_buffer_id))
153 return;
154
155 // Error not associated to any bitstream buffer. For example, GPU channel
156 // is broken.
157 if (bitstream_buffer_id ==
158 media::JpegDecodeAccelerator::kInvalidBitstreamBufferId) {
mcasas 2015/04/16 23:55:13 This condition is already covered inside IsExpecte
kcwu 2015/04/20 17:48:00 IsExpectedDecodeResponse returns true for this val
159 {
160 base::AutoLock lock(lock_);
161 fail_state_ = FAILED;
162 }
163 DecodeDone();
164 return;
165 }
166
167 FallbackToSoftwareDecode();
168 DecodeDone();
169 }
170
171 void GpuJpegDecodeAcceleratorAdapter::DecodeCapturedData(
172 const uint8* data,
173 int length,
174 const media::VideoCaptureFormat& frame_format,
175 int rotation,
176 const base::TimeTicks& timestamp) {
177 DCHECK(device_thread_->BelongsToCurrentThread());
178 DCHECK(decoder_);
179 if (IsDecoding()) {
180 DVLOG(1) << "Drop captured frame. Previous jpeg frame is still decoding";
181 return;
182 }
183 DCHECK(!IsFailed());
184
185 gfx::Size dimensions = frame_format.frame_size;
mcasas 2015/04/16 23:55:13 const?
kcwu 2015/04/20 17:48:00 Done.
186 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> out_buffer =
mcasas 2015/04/16 23:55:13 Suggest to directly allocate onto |out_buffer_| an
kcwu 2015/04/20 17:47:59 @wuchengli prefer to shorten lock time at the end
187 device_client_->ReserveOutputBuffer(media::PIXEL_FORMAT_I420, dimensions);
188 if (!out_buffer.get()) {
189 DVLOG(1) << "Drop captured frame. No available buffers"
190 << " (all buffers are still queued to display)";
191 return;
192 }
193
194 CapturedData captured_data;
195 captured_data.data = const_cast<uint8*>(data);
196 captured_data.length = length;
197 captured_data.frame_format = frame_format;
198 captured_data.rotation = rotation;
199 captured_data.timestamp = timestamp;
200
201 size_t in_buffer_size = captured_data.length;
mcasas 2015/04/16 23:55:13 Why an extra alias? Just rename the input param.
kcwu 2015/04/20 17:48:00 I just want to keep the name of input param matchi
202
203 scoped_ptr<base::SharedMemory> in_shared_memory;
204 {
205 // Swap out to local variable in order to reduce lock time in IO thread.
mcasas 2015/04/16 23:55:13 s/in/on/
kcwu 2015/04/20 17:48:00 Done.
206 base::AutoLock lock(lock_);
207 std::swap(in_shared_memory, in_shared_memory_);
208 }
209 // Enlarge input buffer if necessary.
210 if (!in_shared_memory.get() ||
211 in_buffer_size > in_shared_memory->mapped_size()) {
mcasas 2015/04/16 23:55:14 I suspect this allocate-as-you-go scheme might end
kcwu 2015/04/20 17:48:00 However JPEG does not guarantee compressed image i
kcwu 2015/05/08 14:42:40 Now I allocate 2x input size at first time.
212 in_shared_memory.reset(new base::SharedMemory);
213 if (!in_shared_memory->CreateAnonymous(in_buffer_size)) {
214 DLOG(ERROR) << "CreateAnonymous failed";
215 captured_data_ = captured_data;
216 FallbackToSoftwareDecode();
217 return;
218 }
219
220 if (!in_shared_memory->Map(in_buffer_size)) {
mcasas 2015/04/16 23:55:14 Merge this with the previous error condition in l.
kcwu 2015/04/20 17:48:00 Done.
221 DLOG(ERROR) << "Map failed";
222 captured_data_ = captured_data;
223 FallbackToSoftwareDecode();
224 return;
225 }
226 }
227
228 media::BitstreamBuffer in_buffer(next_bitstream_buffer_id_,
229 in_shared_memory->handle(), in_buffer_size);
230 // Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
231 next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF;
232
233 scoped_refptr<media::VideoFrame> out_frame =
234 media::VideoFrame::WrapExternalPackedMemory(
235 media::VideoFrame::I420,
236 dimensions,
237 gfx::Rect(dimensions),
238 dimensions,
239 reinterpret_cast<uint8*>(out_buffer->data()),
240 out_buffer->size(),
241 out_buffer->handle(),
242 0,
243 base::TimeDelta(),
244 base::Closure());
245 DCHECK(out_frame.get());
246
247 out_frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE,
248 captured_data.frame_format.frame_rate);
249
250 memcpy(in_shared_memory->memory(), captured_data.data, in_buffer_size);
251 captured_data.data = reinterpret_cast<uint8*>(in_shared_memory->memory());
252
253 base::AutoLock lock(lock_);
254 captured_data_ = captured_data;
255 in_buffer_ = in_buffer;
256 std::swap(in_shared_memory_, in_shared_memory);
257 out_buffer_ = out_buffer;
258 out_frame_ = out_frame;
259
260 decoder_->Decode(in_buffer_, out_frame_);
mcasas 2015/04/16 23:55:13 IIUC, only |in_buffer_->id()| is really needed. Pl
kcwu 2015/04/20 17:48:00 in_* are for fallback. out_* are keep because we w
261 }
262
263 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698