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.
5 #include "content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/trace_event/trace_event.h"
14 #include "content/browser/gpu/browser_gpu_channel_host_factory.h"
15 #include "content/common/gpu/client/gpu_jpeg_decode_accelerator_host.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/common/content_switches.h"
18 #include "media/base/video_frame.h"
22 VideoCaptureGpuJpegDecoder::VideoCaptureGpuJpegDecoder(
23 const DecodeDoneCB
& decode_done_cb
)
24 : decode_done_cb_(decode_done_cb
),
25 next_bitstream_buffer_id_(0),
26 in_buffer_id_(media::JpegDecodeAccelerator::kInvalidBitstreamBufferId
),
27 decoder_status_(INIT_PENDING
) {}
29 VideoCaptureGpuJpegDecoder::~VideoCaptureGpuJpegDecoder() {
30 DCHECK(CalledOnValidThread());
32 // |decoder_| guarantees no more JpegDecodeAccelerator::Client callbacks
33 // on IO thread after deletion.
36 // |gpu_channel_host_| should outlive |decoder_|, so |gpu_channel_host_|
37 // must be released after |decoder_| has been destroyed.
38 gpu_channel_host_
= nullptr;
41 bool VideoCaptureGpuJpegDecoder::IsDecoding_Locked() const {
42 lock_
.AssertAcquired();
43 return !decode_done_closure_
.is_null();
46 void VideoCaptureGpuJpegDecoder::Initialize() {
47 DCHECK(CalledOnValidThread());
49 base::AutoLock
lock(lock_
);
50 // TODO(henryhsu): enable on ARM platform after V4L2 JDA is ready.
51 #if !defined(OS_CHROMEOS) || !defined(ARCH_CPU_X86_FAMILY)
52 // Non-ChromeOS platforms do not support HW JPEG decode now. Do not establish
53 // gpu channel to avoid introducing overhead.
54 decoder_status_
= FAILED
;
57 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
58 switches::kDisableAcceleratedMjpegDecode
)) {
59 decoder_status_
= FAILED
;
63 const scoped_refptr
<base::SingleThreadTaskRunner
> current_task_runner(
64 base::ThreadTaskRunnerHandle::Get());
65 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
66 base::Bind(&EstablishGpuChannelOnUIThread
,
67 current_task_runner
, AsWeakPtr()));
71 VideoCaptureGpuJpegDecoder::STATUS
VideoCaptureGpuJpegDecoder::GetStatus()
73 DCHECK(CalledOnValidThread());
74 base::AutoLock
lock(lock_
);
75 return decoder_status_
;
79 void VideoCaptureGpuJpegDecoder::EstablishGpuChannelOnUIThread(
80 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
,
81 base::WeakPtr
<VideoCaptureGpuJpegDecoder
> weak_this
) {
82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
83 DCHECK(BrowserGpuChannelHostFactory::instance());
85 BrowserGpuChannelHostFactory::instance()->EstablishGpuChannel(
86 CAUSE_FOR_GPU_LAUNCH_JPEGDECODEACCELERATOR_INITIALIZE
,
87 base::Bind(&VideoCaptureGpuJpegDecoder::GpuChannelEstablishedOnUIThread
,
88 task_runner
, weak_this
));
92 void VideoCaptureGpuJpegDecoder::GpuChannelEstablishedOnUIThread(
93 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
,
94 base::WeakPtr
<VideoCaptureGpuJpegDecoder
> weak_this
) {
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
97 scoped_refptr
<GpuChannelHost
> gpu_channel_host(
98 BrowserGpuChannelHostFactory::instance()->GetGpuChannel());
99 task_runner
->PostTask(
100 FROM_HERE
, base::Bind(&VideoCaptureGpuJpegDecoder::FinishInitialization
,
101 weak_this
, base::Passed(&gpu_channel_host
)));
104 void VideoCaptureGpuJpegDecoder::FinishInitialization(
105 scoped_refptr
<GpuChannelHost
> gpu_channel_host
) {
106 DCHECK(CalledOnValidThread());
107 base::AutoLock
lock(lock_
);
108 if (!gpu_channel_host
) {
109 LOG(ERROR
) << "Failed to establish GPU channel for JPEG decoder";
110 decoder_status_
= FAILED
;
114 if (gpu_channel_host
->gpu_info().jpeg_decode_accelerator_supported
) {
115 gpu_channel_host_
= gpu_channel_host
.Pass();
116 decoder_
= gpu_channel_host_
->CreateJpegDecoder(this);
118 decoder_status_
= decoder_
? INIT_PASSED
: FAILED
;
121 void VideoCaptureGpuJpegDecoder::VideoFrameReady(int32_t bitstream_buffer_id
) {
122 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
123 TRACE_EVENT0("jpeg", "VideoCaptureGpuJpegDecoder::VideoFrameReady");
124 base::AutoLock
lock(lock_
);
126 if (!IsDecoding_Locked()) {
127 LOG(ERROR
) << "Got decode response while not decoding";
131 if (bitstream_buffer_id
!= in_buffer_id_
) {
132 LOG(ERROR
) << "Unexpected bitstream_buffer_id " << bitstream_buffer_id
133 << ", expected " << in_buffer_id_
;
136 in_buffer_id_
= media::JpegDecodeAccelerator::kInvalidBitstreamBufferId
;
138 decode_done_closure_
.Run();
139 decode_done_closure_
.Reset();
141 TRACE_EVENT_ASYNC_END0("jpeg", "VideoCaptureGpuJpegDecoder decoding",
142 bitstream_buffer_id
);
145 void VideoCaptureGpuJpegDecoder::NotifyError(
146 int32_t bitstream_buffer_id
,
147 media::JpegDecodeAccelerator::Error error
) {
148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
149 LOG(ERROR
) << "Decode error, bitstream_buffer_id=" << bitstream_buffer_id
150 << ", error=" << error
;
152 base::AutoLock
lock(lock_
);
153 decode_done_closure_
.Reset();
154 decoder_status_
= FAILED
;
157 void VideoCaptureGpuJpegDecoder::DecodeCapturedData(
159 size_t in_buffer_size
,
160 const media::VideoCaptureFormat
& frame_format
,
161 const base::TimeTicks
& timestamp
,
162 scoped_ptr
<media::VideoCaptureDevice::Client::Buffer
> out_buffer
) {
163 DCHECK(CalledOnValidThread());
166 TRACE_EVENT_ASYNC_BEGIN0("jpeg", "VideoCaptureGpuJpegDecoder decoding",
167 next_bitstream_buffer_id_
);
168 TRACE_EVENT0("jpeg", "VideoCaptureGpuJpegDecoder::DecodeCapturedData");
170 // TODO(kcwu): enqueue decode requests in case decoding is not fast enough
171 // (say, if decoding time is longer than 16ms for 60fps 4k image)
173 base::AutoLock
lock(lock_
);
174 if (IsDecoding_Locked()) {
175 DVLOG(1) << "Drop captured frame. Previous jpeg frame is still decoding";
180 // Enlarge input buffer if necessary.
181 if (!in_shared_memory_
.get() ||
182 in_buffer_size
> in_shared_memory_
->mapped_size()) {
183 // Reserve 2x space to avoid frequent reallocations for initial frames.
184 const size_t reserved_size
= 2 * in_buffer_size
;
185 in_shared_memory_
.reset(new base::SharedMemory
);
186 if (!in_shared_memory_
->CreateAndMapAnonymous(reserved_size
)) {
187 base::AutoLock
lock(lock_
);
188 decoder_status_
= FAILED
;
189 LOG(WARNING
) << "CreateAndMapAnonymous failed, size=" << reserved_size
;
193 memcpy(in_shared_memory_
->memory(), data
, in_buffer_size
);
195 // No need to lock for |in_buffer_id_| since IsDecoding_Locked() is false.
196 in_buffer_id_
= next_bitstream_buffer_id_
;
197 media::BitstreamBuffer
in_buffer(in_buffer_id_
, in_shared_memory_
->handle(),
199 // Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
200 next_bitstream_buffer_id_
= (next_bitstream_buffer_id_
+ 1) & 0x3FFFFFFF;
202 #if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
203 const gfx::Size dimensions
= frame_format
.frame_size
;
204 base::SharedMemoryHandle out_handle
= out_buffer
->AsPlatformFile();
205 scoped_refptr
<media::VideoFrame
> out_frame
=
206 media::VideoFrame::WrapExternalSharedMemory(
207 media::PIXEL_FORMAT_I420
, // format
208 dimensions
, // coded_size
209 gfx::Rect(dimensions
), // visible_rect
210 dimensions
, // natural_size
211 static_cast<uint8_t*>(out_buffer
->data()), // data
212 out_buffer
->size(), // data_size
213 out_handle
, // handle
214 0, // shared_memory_offset
215 base::TimeDelta()); // timestamp
217 base::AutoLock
lock(lock_
);
218 decoder_status_
= FAILED
;
219 LOG(ERROR
) << "DecodeCapturedData: WrapExternalSharedMemory failed";
222 out_frame
->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE
,
223 frame_format
.frame_rate
);
226 base::AutoLock
lock(lock_
);
227 decode_done_closure_
= base::Bind(
228 decode_done_cb_
, base::Passed(&out_buffer
), out_frame
, timestamp
);
230 decoder_
->Decode(in_buffer
, out_frame
);
236 } // namespace content