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/metrics/histogram.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/trace_event/trace_event.h"
15 #include "content/browser/gpu/browser_gpu_channel_host_factory.h"
16 #include "content/common/gpu/client/gpu_jpeg_decode_accelerator_host.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/common/content_switches.h"
19 #include "media/base/video_frame.h"
23 VideoCaptureGpuJpegDecoder::VideoCaptureGpuJpegDecoder(
24 const DecodeDoneCB
& decode_done_cb
)
25 : decode_done_cb_(decode_done_cb
),
26 next_bitstream_buffer_id_(0),
27 in_buffer_id_(media::JpegDecodeAccelerator::kInvalidBitstreamBufferId
),
28 decoder_status_(INIT_PENDING
) {}
30 VideoCaptureGpuJpegDecoder::~VideoCaptureGpuJpegDecoder() {
31 DCHECK(CalledOnValidThread());
33 // |decoder_| guarantees no more JpegDecodeAccelerator::Client callbacks
34 // on IO thread after deletion.
37 // |gpu_channel_host_| should outlive |decoder_|, so |gpu_channel_host_|
38 // must be released after |decoder_| has been destroyed.
39 gpu_channel_host_
= nullptr;
42 void VideoCaptureGpuJpegDecoder::Initialize() {
43 DCHECK(CalledOnValidThread());
45 base::AutoLock
lock(lock_
);
46 // TODO(henryhsu): enable on ARM platform after V4L2 JpegDecodeAccelerator is
48 bool is_platform_supported
= false;
49 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
50 // Non-ChromeOS platforms do not support HW JPEG decode now. Do not establish
51 // gpu channel to avoid introducing overhead.
52 is_platform_supported
= true;
55 if (!is_platform_supported
||
56 base::CommandLine::ForCurrentProcess()->HasSwitch(
57 switches::kDisableAcceleratedMjpegDecode
)) {
58 decoder_status_
= FAILED
;
59 RecordInitDecodeUMA_Locked();
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()));
70 VideoCaptureGpuJpegDecoder::STATUS
VideoCaptureGpuJpegDecoder::GetStatus()
72 DCHECK(CalledOnValidThread());
73 base::AutoLock
lock(lock_
);
74 return decoder_status_
;
77 void VideoCaptureGpuJpegDecoder::DecodeCapturedData(
79 size_t in_buffer_size
,
80 const media::VideoCaptureFormat
& frame_format
,
81 const base::TimeTicks
& timestamp
,
82 scoped_ptr
<media::VideoCaptureDevice::Client::Buffer
> out_buffer
) {
83 DCHECK(CalledOnValidThread());
86 TRACE_EVENT_ASYNC_BEGIN0("jpeg", "VideoCaptureGpuJpegDecoder decoding",
87 next_bitstream_buffer_id_
);
88 TRACE_EVENT0("jpeg", "VideoCaptureGpuJpegDecoder::DecodeCapturedData");
90 // TODO(kcwu): enqueue decode requests in case decoding is not fast enough
91 // (say, if decoding time is longer than 16ms for 60fps 4k image)
93 base::AutoLock
lock(lock_
);
94 if (IsDecoding_Locked()) {
95 DVLOG(1) << "Drop captured frame. Previous jpeg frame is still decoding";
100 // Enlarge input buffer if necessary.
101 if (!in_shared_memory_
.get() ||
102 in_buffer_size
> in_shared_memory_
->mapped_size()) {
103 // Reserve 2x space to avoid frequent reallocations for initial frames.
104 const size_t reserved_size
= 2 * in_buffer_size
;
105 in_shared_memory_
.reset(new base::SharedMemory
);
106 if (!in_shared_memory_
->CreateAndMapAnonymous(reserved_size
)) {
107 base::AutoLock
lock(lock_
);
108 decoder_status_
= FAILED
;
109 LOG(WARNING
) << "CreateAndMapAnonymous failed, size=" << reserved_size
;
113 memcpy(in_shared_memory_
->memory(), data
, in_buffer_size
);
115 // No need to lock for |in_buffer_id_| since IsDecoding_Locked() is false.
116 in_buffer_id_
= next_bitstream_buffer_id_
;
117 media::BitstreamBuffer
in_buffer(in_buffer_id_
, in_shared_memory_
->handle(),
119 // Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
120 next_bitstream_buffer_id_
= (next_bitstream_buffer_id_
+ 1) & 0x3FFFFFFF;
122 #if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
123 const gfx::Size dimensions
= frame_format
.frame_size
;
124 base::SharedMemoryHandle out_handle
= out_buffer
->AsPlatformFile();
125 scoped_refptr
<media::VideoFrame
> out_frame
=
126 media::VideoFrame::WrapExternalSharedMemory(
127 media::PIXEL_FORMAT_I420
, // format
128 dimensions
, // coded_size
129 gfx::Rect(dimensions
), // visible_rect
130 dimensions
, // natural_size
131 static_cast<uint8_t*>(out_buffer
->data()), // data
132 out_buffer
->mapped_size(), // data_size
133 out_handle
, // handle
134 0, // shared_memory_offset
135 base::TimeDelta()); // timestamp
137 base::AutoLock
lock(lock_
);
138 decoder_status_
= FAILED
;
139 LOG(ERROR
) << "DecodeCapturedData: WrapExternalSharedMemory failed";
142 out_frame
->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE
,
143 frame_format
.frame_rate
);
146 base::AutoLock
lock(lock_
);
147 decode_done_closure_
= base::Bind(
148 decode_done_cb_
, base::Passed(&out_buffer
), out_frame
, timestamp
);
150 decoder_
->Decode(in_buffer
, out_frame
);
156 void VideoCaptureGpuJpegDecoder::VideoFrameReady(int32_t bitstream_buffer_id
) {
157 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
158 TRACE_EVENT0("jpeg", "VideoCaptureGpuJpegDecoder::VideoFrameReady");
159 base::AutoLock
lock(lock_
);
161 if (!IsDecoding_Locked()) {
162 LOG(ERROR
) << "Got decode response while not decoding";
166 if (bitstream_buffer_id
!= in_buffer_id_
) {
167 LOG(ERROR
) << "Unexpected bitstream_buffer_id " << bitstream_buffer_id
168 << ", expected " << in_buffer_id_
;
171 in_buffer_id_
= media::JpegDecodeAccelerator::kInvalidBitstreamBufferId
;
173 decode_done_closure_
.Run();
174 decode_done_closure_
.Reset();
176 TRACE_EVENT_ASYNC_END0("jpeg", "VideoCaptureGpuJpegDecoder decoding",
177 bitstream_buffer_id
);
180 void VideoCaptureGpuJpegDecoder::NotifyError(
181 int32_t bitstream_buffer_id
,
182 media::JpegDecodeAccelerator::Error error
) {
183 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
184 LOG(ERROR
) << "Decode error, bitstream_buffer_id=" << bitstream_buffer_id
185 << ", error=" << error
;
187 base::AutoLock
lock(lock_
);
188 decode_done_closure_
.Reset();
189 decoder_status_
= FAILED
;
193 void VideoCaptureGpuJpegDecoder::EstablishGpuChannelOnUIThread(
194 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
,
195 base::WeakPtr
<VideoCaptureGpuJpegDecoder
> weak_this
) {
196 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
197 DCHECK(BrowserGpuChannelHostFactory::instance());
199 BrowserGpuChannelHostFactory::instance()->EstablishGpuChannel(
200 CAUSE_FOR_GPU_LAUNCH_JPEGDECODEACCELERATOR_INITIALIZE
,
201 base::Bind(&VideoCaptureGpuJpegDecoder::GpuChannelEstablishedOnUIThread
,
202 task_runner
, weak_this
));
206 void VideoCaptureGpuJpegDecoder::GpuChannelEstablishedOnUIThread(
207 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
,
208 base::WeakPtr
<VideoCaptureGpuJpegDecoder
> weak_this
) {
209 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
211 scoped_refptr
<GpuChannelHost
> gpu_channel_host(
212 BrowserGpuChannelHostFactory::instance()->GetGpuChannel());
213 task_runner
->PostTask(
214 FROM_HERE
, base::Bind(&VideoCaptureGpuJpegDecoder::FinishInitialization
,
215 weak_this
, base::Passed(&gpu_channel_host
)));
218 void VideoCaptureGpuJpegDecoder::FinishInitialization(
219 scoped_refptr
<GpuChannelHost
> gpu_channel_host
) {
220 DCHECK(CalledOnValidThread());
221 base::AutoLock
lock(lock_
);
222 if (!gpu_channel_host
) {
223 LOG(ERROR
) << "Failed to establish GPU channel for JPEG decoder";
224 } else if (gpu_channel_host
->gpu_info().jpeg_decode_accelerator_supported
) {
225 gpu_channel_host_
= gpu_channel_host
.Pass();
226 decoder_
= gpu_channel_host_
->CreateJpegDecoder(this);
228 decoder_status_
= decoder_
? INIT_PASSED
: FAILED
;
229 RecordInitDecodeUMA_Locked();
232 bool VideoCaptureGpuJpegDecoder::IsDecoding_Locked() const {
233 lock_
.AssertAcquired();
234 return !decode_done_closure_
.is_null();
237 void VideoCaptureGpuJpegDecoder::RecordInitDecodeUMA_Locked() {
238 UMA_HISTOGRAM_BOOLEAN("Media.VideoCaptureGpuJpegDecoder.InitDecodeSuccess",
239 decoder_status_
== INIT_PASSED
);
242 } // namespace content