OLD | NEW |
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 Loading... |
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 |
OLD | NEW |