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

Side by Side Diff: media/filters/skcanvas_video_renderer.cc

Issue 445013002: media: Optimize HW Video to 2D Canvas copy. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Don't move SkCanvasVideoRenderer Created 6 years, 4 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/filters/skcanvas_video_renderer.h" 5 #include "media/filters/skcanvas_video_renderer.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "gpu/GLES2/gl2extchromium.h"
9 #include "gpu/command_buffer/client/gles2_interface.h"
10 #include "gpu/command_buffer/common/mailbox_holder.h"
8 #include "media/base/video_frame.h" 11 #include "media/base/video_frame.h"
9 #include "media/base/yuv_convert.h" 12 #include "media/base/yuv_convert.h"
13 #include "skia/ext/refptr.h"
10 #include "third_party/libyuv/include/libyuv.h" 14 #include "third_party/libyuv/include/libyuv.h"
11 #include "third_party/skia/include/core/SkCanvas.h" 15 #include "third_party/skia/include/core/SkCanvas.h"
16 #include "third_party/skia/include/gpu/GrContext.h"
17 #include "third_party/skia/include/gpu/SkGrPixelRef.h"
12 18
13 // Skia internal format depends on a platform. On Android it is ABGR, on others 19 // Skia internal format depends on a platform. On Android it is ABGR, on others
14 // it is ARGB. 20 // it is ARGB.
15 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \ 21 #if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \
16 SK_A32_SHIFT == 24 22 SK_A32_SHIFT == 24
17 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB 23 #define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB
18 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB 24 #define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB
19 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \ 25 #elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \
20 SK_A32_SHIFT == 24 26 SK_A32_SHIFT == 24
21 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR 27 #define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR
22 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR 28 #define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR
23 #else 29 #else
24 #error Unexpected Skia ARGB_8888 layout! 30 #error Unexpected Skia ARGB_8888 layout!
25 #endif 31 #endif
26 32
27 namespace media { 33 namespace media {
28 34
29 static bool IsYUV(media::VideoFrame::Format format) { 35 namespace {
36
37 bool IsYUV(media::VideoFrame::Format format) {
30 return format == media::VideoFrame::YV12 || 38 return format == media::VideoFrame::YV12 ||
31 format == media::VideoFrame::YV16 || 39 format == media::VideoFrame::YV16 ||
32 format == media::VideoFrame::I420 || 40 format == media::VideoFrame::I420 ||
33 format == media::VideoFrame::YV12A || 41 format == media::VideoFrame::YV12A ||
34 format == media::VideoFrame::YV12J || 42 format == media::VideoFrame::YV12J ||
35 format == media::VideoFrame::YV24; 43 format == media::VideoFrame::YV24;
36 } 44 }
37 45
38 static bool IsYUVOrNative(media::VideoFrame::Format format) { 46 bool IsYUVOrNative(media::VideoFrame::Format format) {
39 return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE; 47 return IsYUV(format) || format == media::VideoFrame::NATIVE_TEXTURE;
40 } 48 }
41 49
42 // Converts a VideoFrame containing YUV data to a SkBitmap containing RGB data. 50 // Converts a VideoFrame containing YUV data to a SkBitmap containing RGB data.
43 // 51 //
44 // |bitmap| will be (re)allocated to match the dimensions of |video_frame|. 52 // |bitmap| will be (re)allocated to match the dimensions of |video_frame|.
45 static void ConvertVideoFrameToBitmap( 53 void ConvertVideoFrameToBitmap(media::VideoFrame* video_frame,
46 const scoped_refptr<media::VideoFrame>& video_frame, 54 SkBitmap* bitmap) {
47 SkBitmap* bitmap) {
48 DCHECK(IsYUVOrNative(video_frame->format())) 55 DCHECK(IsYUVOrNative(video_frame->format()))
49 << video_frame->format(); 56 << video_frame->format();
50 if (IsYUV(video_frame->format())) { 57 if (IsYUV(video_frame->format())) {
51 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane), 58 DCHECK_EQ(video_frame->stride(media::VideoFrame::kUPlane),
52 video_frame->stride(media::VideoFrame::kVPlane)); 59 video_frame->stride(media::VideoFrame::kVPlane));
53 } 60 }
54 61
55 // Check if |bitmap| needs to be (re)allocated. 62 // Check if |bitmap| needs to be (re)allocated.
56 if (bitmap->isNull() || 63 if (bitmap->isNull() ||
57 bitmap->width() != video_frame->visible_rect().width() || 64 bitmap->width() != video_frame->visible_rect().width() ||
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 break; 179 break;
173 180
174 default: 181 default:
175 NOTREACHED(); 182 NOTREACHED();
176 break; 183 break;
177 } 184 }
178 bitmap->notifyPixelsChanged(); 185 bitmap->notifyPixelsChanged();
179 bitmap->unlockPixels(); 186 bitmap->unlockPixels();
180 } 187 }
181 188
189 bool EnsureTextureBackedSkBitmap(GrContext* gr,
190 SkBitmap* bitmap,
191 const gfx::Size& size,
192 GrSurfaceOrigin origin,
193 GrPixelConfig config) {
194 if (!bitmap->getTexture() || bitmap->width() != size.width() ||
195 bitmap->height() != size.height()) {
196 if (!gr)
197 return false;
198 GrTextureDesc desc;
199 desc.fConfig = config;
200 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
201 desc.fSampleCnt = 0;
202 desc.fOrigin = origin;
203 desc.fWidth = size.width();
204 desc.fHeight = size.height();
205 skia::RefPtr<GrTexture> texture;
206 texture = skia::AdoptRef(gr->createUncachedTexture(desc, 0, 0));
207 if (!texture.get())
208 return false;
209
210 SkImageInfo info = SkImageInfo::MakeN32Premul(desc.fWidth, desc.fHeight);
211 SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (info, texture.get()));
212 if (!pixelRef)
213 return false;
214 bitmap->setInfo(info);
215 bitmap->setPixelRef(pixelRef)->unref();
216 }
217
218 return true;
219 }
220
221 bool ConvertVideoFrameToTexture(
222 VideoFrame* video_frame,
223 SkBitmap* bitmap,
224 SkCanvasVideoRenderer::ContextProvider* context_provider) {
225 DCHECK(context_provider &&
226 video_frame->format() == VideoFrame::NATIVE_TEXTURE);
227 gpu::gles2::GLES2Interface* gl = context_provider->gl;
228 DCHECK(gl);
229
230 // Check if we could reuse existing texture based bitmap.
231 // Otherwise, release existing texture based bitmap and allocate
232 // a new one based on video size.
233 if (!EnsureTextureBackedSkBitmap(context_provider->gr_context,
234 bitmap,
235 video_frame->visible_rect().size(),
236 kTopLeft_GrSurfaceOrigin,
237 kSkia8888_GrPixelConfig)) {
238 return false;
239 }
240
241 unsigned textureId =
242 static_cast<unsigned>((bitmap->getTexture())->getTextureHandle());
243 SkCanvasVideoRenderer::CopyVideoFrameToTexture(
244 gl, video_frame, textureId, 0, GL_RGBA, GL_UNSIGNED_BYTE, true, false);
245 return true;
246 }
247
248 class SyncPointClientImpl : public VideoFrame::SyncPointClient {
249 public:
250 explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
251 virtual ~SyncPointClientImpl() {}
252 virtual uint32 InsertSyncPoint() OVERRIDE {
253 return gl_->InsertSyncPointCHROMIUM();
254 }
255 virtual void WaitSyncPoint(uint32 sync_point) OVERRIDE {
256 gl_->WaitSyncPointCHROMIUM(sync_point);
257 }
258
259 private:
260 gpu::gles2::GLES2Interface* gl_;
261 };
262
263 } // anonymous namespace
264
182 SkCanvasVideoRenderer::SkCanvasVideoRenderer() 265 SkCanvasVideoRenderer::SkCanvasVideoRenderer()
183 : last_frame_timestamp_(media::kNoTimestamp()) { 266 : last_frame_timestamp_(media::kNoTimestamp()),
267 accelerated_last_frame_timestamp_(media::kNoTimestamp()) {
184 } 268 }
185 269
186 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} 270 SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {}
187 271
188 void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame, 272 void SkCanvasVideoRenderer::Paint(
189 SkCanvas* canvas, 273 media::VideoFrame* video_frame,
190 const gfx::RectF& dest_rect, 274 SkCanvas* canvas,
191 uint8 alpha) { 275 const gfx::RectF& dest_rect,
276 uint8 alpha,
277 SkCanvasVideoRenderer::ContextProvider* context_provider) {
192 if (alpha == 0) { 278 if (alpha == 0) {
193 return; 279 return;
194 } 280 }
195 281
196 SkRect dest; 282 SkRect dest;
197 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom()); 283 dest.set(dest_rect.x(), dest_rect.y(), dest_rect.right(), dest_rect.bottom());
198 284
199 SkPaint paint; 285 SkPaint paint;
200 paint.setAlpha(alpha); 286 paint.setAlpha(alpha);
201 287
202 // Paint black rectangle if there isn't a frame available or the 288 // Paint black rectangle if there isn't a frame available or the
203 // frame has an unexpected format. 289 // frame has an unexpected format.
204 if (!video_frame || !IsYUVOrNative(video_frame->format())) { 290 if (!video_frame || video_frame->natural_size().IsEmpty() ||
291 !IsYUVOrNative(video_frame->format())) {
205 canvas->drawRect(dest, paint); 292 canvas->drawRect(dest, paint);
206 return; 293 return;
207 } 294 }
208 295
296 bool accelerated = false;
297 if (context_provider &&
298 video_frame->format() == media::VideoFrame::NATIVE_TEXTURE &&
299 canvas->getGrContext()) {
300 // TODO(dshwang): Android video decoder doesn't update the timestamp on a
301 // VideoFrame. To reduce redundant copy, Android should update the
302 // timestamp.
303 if (accelerated_last_frame_.isNull() ||
304 video_frame->timestamp() != accelerated_last_frame_timestamp_ ||
305 video_frame->timestamp() == base::TimeDelta()) {
306 accelerated = ConvertVideoFrameToTexture(
307 video_frame, &accelerated_last_frame_, context_provider);
308 if (accelerated) {
309 accelerated_last_frame_timestamp_ = video_frame->timestamp();
310 }
311 } else {
312 DCHECK(accelerated_last_frame_.getTexture());
313 accelerated = true;
314 }
315 }
316
209 // Check if we should convert and update |last_frame_|. 317 // Check if we should convert and update |last_frame_|.
210 if (last_frame_.isNull() || 318 if (!accelerated && (last_frame_.isNull() ||
211 video_frame->timestamp() != last_frame_timestamp_) { 319 video_frame->timestamp() != last_frame_timestamp_)) {
212 ConvertVideoFrameToBitmap(video_frame, &last_frame_); 320 ConvertVideoFrameToBitmap(video_frame, &last_frame_);
213 last_frame_timestamp_ = video_frame->timestamp(); 321 last_frame_timestamp_ = video_frame->timestamp();
214 } 322 }
215 323
216 // Use SRC mode so we completely overwrite the buffer (in case we have alpha) 324 // Use SRC mode so we completely overwrite the buffer (in case we have alpha)
217 // this means we don't need the extra cost of clearing the buffer first. 325 // this means we don't need the extra cost of clearing the buffer first.
218 paint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode)); 326 paint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode));
219 327
220 // Paint using |last_frame_|. 328 canvas->drawBitmapRect(
221 paint.setFilterLevel(SkPaint::kLow_FilterLevel); 329 accelerated ? accelerated_last_frame_ : last_frame_, NULL, dest, &paint);
222 canvas->drawBitmapRect(last_frame_, NULL, dest, &paint); 330
331 CleanUpTemporaryBuffers();
332 }
333
334 // If a buffer is not used by 3 sec, remove it.
335 void SkCanvasVideoRenderer::CleanUpTemporaryBuffers() {
336 static const base::TimeDelta buffer_time = base::TimeDelta::FromSeconds(3);
337 base::TimeDelta last_timestamp =
338 accelerated_last_frame_timestamp_ > last_frame_timestamp_
339 ? accelerated_last_frame_timestamp_
340 : last_frame_timestamp_;
341 if (last_timestamp > last_frame_timestamp_ + buffer_time &&
342 !last_frame_.isNull())
343 last_frame_.reset();
344 if (last_timestamp > accelerated_last_frame_timestamp_ + buffer_time &&
345 !accelerated_last_frame_.isNull())
346 accelerated_last_frame_.reset();
347 }
348
349 // static
350 void SkCanvasVideoRenderer::CopyVideoFrameToTexture(
351 gpu::gles2::GLES2Interface* gl,
352 VideoFrame* video_frame,
353 unsigned int texture,
354 unsigned int level,
355 unsigned int internal_format,
356 unsigned int type,
357 bool premultiply_alpha,
358 bool flip_y) {
359 DCHECK(video_frame && video_frame->format() == VideoFrame::NATIVE_TEXTURE);
360 const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
361 DCHECK(mailbox_holder->texture_target == GL_TEXTURE_2D ||
362 mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES);
363
364 gl->WaitSyncPointCHROMIUM(mailbox_holder->sync_point);
365 uint32 source_texture = gl->CreateAndConsumeTextureCHROMIUM(
366 mailbox_holder->texture_target, mailbox_holder->mailbox.name);
367
368 // The video is stored in a unmultiplied format, so premultiply
369 // if necessary.
370 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, premultiply_alpha);
371 // Application itself needs to take care of setting the right flip_y
372 // value down to get the expected result.
373 // flip_y==true means to reverse the video orientation while
374 // flip_y==false means to keep the intrinsic orientation.
375 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y);
376 gl->CopyTextureCHROMIUM(
377 GL_TEXTURE_2D, source_texture, texture, level, internal_format, type);
378 gl->PixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false);
379 gl->PixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, false);
380
381 gl->DeleteTextures(1, &source_texture);
382 gl->Flush();
383
384 SyncPointClientImpl client(gl);
385 video_frame->UpdateReleaseSyncPoint(&client);
223 } 386 }
224 387
225 } // namespace media 388 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698