Chromium Code Reviews| 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/renderers/skcanvas_video_renderer.h" | 5 #include "media/renderers/skcanvas_video_renderer.h" |
| 6 | 6 |
| 7 #include <GLES3/gl3.h> | 7 #include <GLES3/gl3.h> |
| 8 #include <limits> | 8 #include <limits> |
| 9 | 9 |
| 10 #include "base/macros.h" | 10 #include "base/macros.h" |
| (...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 153 handles, yuvSizes, | 153 handles, yuvSizes, |
| 154 kTopLeft_GrSurfaceOrigin); | 154 kTopLeft_GrSurfaceOrigin); |
| 155 } | 155 } |
| 156 for (size_t i = 0; i < media::VideoFrame::NumPlanes(video_frame->format()); | 156 for (size_t i = 0; i < media::VideoFrame::NumPlanes(video_frame->format()); |
| 157 ++i) { | 157 ++i) { |
| 158 gl->DeleteTextures(1, &source_textures[i].fID); | 158 gl->DeleteTextures(1, &source_textures[i].fID); |
| 159 } | 159 } |
| 160 return img; | 160 return img; |
| 161 } | 161 } |
| 162 | 162 |
| 163 bool VideoTextureNeedsClipping(const VideoFrame* video_frame) { | |
| 164 // There are multiple reasons that the size of the video frame's | |
| 165 // visible rectangle may differ from the coded size, including the | |
| 166 // encoder rounding up to the size of a macroblock, or use of | |
| 167 // non-square pixels. | |
| 168 // | |
| 169 // Some callers of these APIs (HTMLVideoElement and the 2D canvas | |
| 170 // context) already clip to the video frame's visible rectangle. | |
| 171 // WebGL on the other hand assumes that only the valid pixels are | |
| 172 // contained in the destination texture. This helper function | |
| 173 // determines whether this slower path is needed. | |
| 174 return video_frame->visible_rect().size() != video_frame->coded_size(); | |
|
sandersd (OOO until July 31)
2017/04/07 23:40:59
Might be more clear as Rect(coded_size() == visibl
Ken Russell (switch to Gerrit)
2017/04/07 23:48:18
WebGL's the only consumer of the code paths where
sandersd (OOO until July 31)
2017/04/07 23:51:53
My question is about the source texture though. Wh
Ken Russell (switch to Gerrit)
2017/04/07 23:54:51
Yes, that's my assumption. It appeared to be the c
sandersd (OOO until July 31)
2017/04/08 00:01:25
I'm not sure.
I believe it is true when we create
sandersd (OOO until July 31)
2017/04/08 00:05:04
Upon reflection, I think Android is the only worry
sandersd (OOO until July 31)
2017/04/08 00:07:14
Relevant Android code is here: https://cs.chromium
| |
| 175 } | |
| 176 | |
| 177 gfx::Size AdjustedVideoTextureSize(const VideoFrame* video_frame) { | |
|
sandersd (OOO until July 31)
2017/04/07 23:40:59
Unused?
Ken Russell (switch to Gerrit)
2017/04/07 23:48:18
Fixed in next (current) patch set. I was still in
| |
| 178 gfx::Size result = video_frame->natural_size(); | |
| 179 result.SetToMin(video_frame->coded_size()); | |
| 180 return result; | |
| 181 } | |
| 182 | |
| 163 // Creates a SkImage from a |video_frame| backed by native resources. | 183 // Creates a SkImage from a |video_frame| backed by native resources. |
| 164 // The SkImage will take ownership of the underlying resource. | 184 // The SkImage will take ownership of the underlying resource. |
| 165 sk_sp<SkImage> NewSkImageFromVideoFrameNative(VideoFrame* video_frame, | 185 sk_sp<SkImage> NewSkImageFromVideoFrameNative(VideoFrame* video_frame, |
| 166 const Context3D& context_3d) { | 186 const Context3D& context_3d) { |
| 167 DCHECK(PIXEL_FORMAT_ARGB == video_frame->format() || | 187 DCHECK(PIXEL_FORMAT_ARGB == video_frame->format() || |
| 168 PIXEL_FORMAT_XRGB == video_frame->format() || | 188 PIXEL_FORMAT_XRGB == video_frame->format() || |
| 169 PIXEL_FORMAT_NV12 == video_frame->format() || | 189 PIXEL_FORMAT_NV12 == video_frame->format() || |
| 170 PIXEL_FORMAT_UYVY == video_frame->format()); | 190 PIXEL_FORMAT_UYVY == video_frame->format()); |
| 171 | 191 |
| 172 const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0); | 192 const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0); |
| 173 DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D || | 193 DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D || |
| 174 mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB || | 194 mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB || |
| 175 mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES) | 195 mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES) |
| 176 << mailbox_holder.texture_target; | 196 << mailbox_holder.texture_target; |
| 177 | 197 |
| 178 gpu::gles2::GLES2Interface* gl = context_3d.gl; | 198 gpu::gles2::GLES2Interface* gl = context_3d.gl; |
| 179 unsigned source_texture = 0; | 199 unsigned source_texture = 0; |
| 180 if (mailbox_holder.texture_target != GL_TEXTURE_2D) { | 200 if (mailbox_holder.texture_target != GL_TEXTURE_2D) { |
| 181 // TODO(dcastagna): At the moment Skia doesn't support targets different | 201 // TODO(dcastagna): At the moment Skia doesn't support targets different |
| 182 // than GL_TEXTURE_2D. Avoid this copy once | 202 // than GL_TEXTURE_2D. Avoid this copy once |
| 183 // https://code.google.com/p/skia/issues/detail?id=3868 is addressed. | 203 // https://code.google.com/p/skia/issues/detail?id=3868 is addressed. |
| 184 gl->GenTextures(1, &source_texture); | 204 gl->GenTextures(1, &source_texture); |
| 185 DCHECK(source_texture); | 205 DCHECK(source_texture); |
| 186 gl->BindTexture(GL_TEXTURE_2D, source_texture); | 206 gl->BindTexture(GL_TEXTURE_2D, source_texture); |
| 187 const gfx::Size& natural_size = video_frame->natural_size(); | |
| 188 gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, natural_size.width(), | |
| 189 natural_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, | |
| 190 nullptr); | |
| 191 SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture( | 207 SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture( |
| 192 gl, video_frame, source_texture, true, false); | 208 gl, video_frame, |
| 209 SkCanvasVideoRenderer::SingleFrameForVideoElementOrCanvas, | |
| 210 source_texture, GL_RGBA, GL_UNSIGNED_BYTE, true, false); | |
| 193 } else { | 211 } else { |
| 194 gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); | 212 gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); |
| 195 source_texture = gl->CreateAndConsumeTextureCHROMIUM( | 213 source_texture = gl->CreateAndConsumeTextureCHROMIUM( |
| 196 mailbox_holder.texture_target, mailbox_holder.mailbox.name); | 214 mailbox_holder.texture_target, mailbox_holder.mailbox.name); |
| 197 } | 215 } |
| 198 GrBackendTextureDesc desc; | 216 GrBackendTextureDesc desc; |
| 199 desc.fFlags = kRenderTarget_GrBackendTextureFlag; | 217 desc.fFlags = kRenderTarget_GrBackendTextureFlag; |
| 200 desc.fOrigin = kTopLeft_GrSurfaceOrigin; | 218 desc.fOrigin = kTopLeft_GrSurfaceOrigin; |
| 201 desc.fWidth = video_frame->coded_size().width(); | 219 desc.fWidth = video_frame->coded_size().width(); |
| 202 desc.fHeight = video_frame->coded_size().height(); | 220 desc.fHeight = video_frame->coded_size().height(); |
| (...skipping 543 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 746 case PIXEL_FORMAT_Y8: | 764 case PIXEL_FORMAT_Y8: |
| 747 case PIXEL_FORMAT_UNKNOWN: | 765 case PIXEL_FORMAT_UNKNOWN: |
| 748 NOTREACHED() << "Only YUV formats and Y16 are supported."; | 766 NOTREACHED() << "Only YUV formats and Y16 are supported."; |
| 749 } | 767 } |
| 750 } | 768 } |
| 751 | 769 |
| 752 // static | 770 // static |
| 753 void SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture( | 771 void SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture( |
| 754 gpu::gles2::GLES2Interface* gl, | 772 gpu::gles2::GLES2Interface* gl, |
| 755 VideoFrame* video_frame, | 773 VideoFrame* video_frame, |
| 774 SingleFrameCopyMode copy_mode, | |
| 756 unsigned int texture, | 775 unsigned int texture, |
| 776 unsigned int internal_format, | |
| 777 unsigned int type, | |
| 757 bool premultiply_alpha, | 778 bool premultiply_alpha, |
| 758 bool flip_y) { | 779 bool flip_y) { |
| 759 DCHECK(video_frame); | 780 DCHECK(video_frame); |
| 760 DCHECK(video_frame->HasTextures()); | 781 DCHECK(video_frame->HasTextures()); |
| 761 | 782 |
| 762 const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0); | 783 const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0); |
| 763 DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D || | 784 DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D || |
| 764 mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB || | 785 mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB || |
| 765 mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES) | 786 mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES) |
| 766 << mailbox_holder.texture_target; | 787 << mailbox_holder.texture_target; |
| 767 | 788 |
| 768 gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); | 789 gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); |
| 769 uint32_t source_texture = gl->CreateAndConsumeTextureCHROMIUM( | 790 uint32_t source_texture = gl->CreateAndConsumeTextureCHROMIUM( |
| 770 mailbox_holder.texture_target, mailbox_holder.mailbox.name); | 791 mailbox_holder.texture_target, mailbox_holder.mailbox.name); |
| 771 | 792 |
| 772 // The video is stored in a unmultiplied format, so premultiply | 793 // The video is stored in a unmultiplied format, so premultiply |
| 773 // if necessary. | 794 // if necessary. |
| 774 // Application itself needs to take care of setting the right |flip_y| | 795 // Application itself needs to take care of setting the right |flip_y| |
| 775 // value down to get the expected result. | 796 // value down to get the expected result. |
| 776 // "flip_y == true" means to reverse the video orientation while | 797 // "flip_y == true" means to reverse the video orientation while |
| 777 // "flip_y == false" means to keep the intrinsic orientation. | 798 // "flip_y == false" means to keep the intrinsic orientation. |
| 778 | 799 |
| 779 // The video's texture might be larger than the natural size because | 800 if (copy_mode == SingleFrameForVideoElementOrCanvas || |
| 780 // the encoder might have had to round up to the size of a macroblock. | 801 !VideoTextureNeedsClipping(video_frame)) { |
| 781 // Make sure to only copy the natural size to avoid putting garbage | 802 // No need to clip the source video texture. |
| 782 // into the bottom of the destination texture. | 803 gl->CopyTextureCHROMIUM(source_texture, 0, GL_TEXTURE_2D, texture, 0, |
| 783 const gfx::Size& natural_size = video_frame->natural_size(); | 804 internal_format, type, flip_y, premultiply_alpha, |
| 784 gl->CopySubTextureCHROMIUM(source_texture, 0, GL_TEXTURE_2D, texture, 0, 0, 0, | 805 false); |
| 785 0, 0, natural_size.width(), natural_size.height(), | 806 } else { |
| 786 flip_y, premultiply_alpha, false); | 807 // Must reallocate the destination texture and copy only a sub-portion. |
| 808 gfx::Rect dest_rect = video_frame->visible_rect(); | |
| 809 #if DCHECK_IS_ON() | |
| 810 // The caller should have bound _texture_ to the GL_TEXTURE_2D | |
| 811 // binding point already. | |
| 812 GLuint current_texture = 0; | |
| 813 gl->GetIntegerv(GL_TEXTURE_BINDING_2D, | |
| 814 reinterpret_cast<GLint*>(¤t_texture)); | |
| 815 DCHECK_EQ(current_texture, texture); | |
| 816 // There should always be enough data in the source texture to | |
| 817 // cover this copy. | |
| 818 DCHECK_LE(dest_rect.width(), video_frame->coded_size().width()); | |
| 819 DCHECK_LE(dest_rect.height(), video_frame->coded_size().height()); | |
|
sandersd (OOO until July 31)
2017/04/07 23:40:59
Perhaps better written as Rect(coded_size()).Conta
| |
| 820 #endif | |
| 821 gl->TexImage2D(GL_TEXTURE_2D, 0, internal_format, dest_rect.width(), | |
| 822 dest_rect.height(), 0, internal_format, type, nullptr); | |
| 823 gl->CopySubTextureCHROMIUM(source_texture, 0, GL_TEXTURE_2D, texture, 0, 0, | |
| 824 0, dest_rect.x(), dest_rect.y(), | |
| 825 dest_rect.width(), dest_rect.height(), flip_y, | |
| 826 premultiply_alpha, false); | |
| 827 } | |
| 828 | |
| 787 gl->DeleteTextures(1, &source_texture); | 829 gl->DeleteTextures(1, &source_texture); |
| 788 gl->Flush(); | 830 gl->Flush(); |
| 789 | 831 |
| 790 SyncTokenClientImpl client(gl); | 832 SyncTokenClientImpl client(gl); |
| 791 video_frame->UpdateReleaseSyncToken(&client); | 833 video_frame->UpdateReleaseSyncToken(&client); |
| 792 } | 834 } |
| 793 | 835 |
| 794 bool SkCanvasVideoRenderer::CopyVideoFrameTexturesToGLTexture( | 836 bool SkCanvasVideoRenderer::CopyVideoFrameTexturesToGLTexture( |
| 795 const Context3D& context_3d, | 837 const Context3D& context_3d, |
| 796 gpu::gles2::GLES2Interface* destination_gl, | 838 gpu::gles2::GLES2Interface* destination_gl, |
| 797 const scoped_refptr<VideoFrame>& video_frame, | 839 const scoped_refptr<VideoFrame>& video_frame, |
| 798 unsigned int texture, | 840 unsigned int texture, |
| 841 unsigned int internal_format, | |
| 842 unsigned int type, | |
| 799 bool premultiply_alpha, | 843 bool premultiply_alpha, |
| 800 bool flip_y) { | 844 bool flip_y) { |
| 801 DCHECK(thread_checker_.CalledOnValidThread()); | 845 DCHECK(thread_checker_.CalledOnValidThread()); |
| 802 DCHECK(video_frame); | 846 DCHECK(video_frame); |
| 803 DCHECK(video_frame->HasTextures()); | 847 DCHECK(video_frame->HasTextures()); |
| 804 if (media::VideoFrame::NumPlanes(video_frame->format()) > 1) { | 848 if (media::VideoFrame::NumPlanes(video_frame->format()) > 1) { |
| 805 if (!context_3d.gr_context) | 849 if (!context_3d.gr_context) |
| 806 return false; | 850 return false; |
| 807 if (!UpdateLastImage(video_frame, context_3d)) | 851 if (!UpdateLastImage(video_frame, context_3d)) |
| 808 return false; | 852 return false; |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 825 canvas_gl->ShallowFlushCHROMIUM(); | 869 canvas_gl->ShallowFlushCHROMIUM(); |
| 826 canvas_gl->GenSyncTokenCHROMIUM(fence_sync, | 870 canvas_gl->GenSyncTokenCHROMIUM(fence_sync, |
| 827 mailbox_holder.sync_token.GetData()); | 871 mailbox_holder.sync_token.GetData()); |
| 828 | 872 |
| 829 destination_gl->WaitSyncTokenCHROMIUM( | 873 destination_gl->WaitSyncTokenCHROMIUM( |
| 830 mailbox_holder.sync_token.GetConstData()); | 874 mailbox_holder.sync_token.GetConstData()); |
| 831 uint32_t intermediate_texture = | 875 uint32_t intermediate_texture = |
| 832 destination_gl->CreateAndConsumeTextureCHROMIUM( | 876 destination_gl->CreateAndConsumeTextureCHROMIUM( |
| 833 mailbox_holder.texture_target, mailbox_holder.mailbox.name); | 877 mailbox_holder.texture_target, mailbox_holder.mailbox.name); |
| 834 | 878 |
| 835 // The video's texture might be larger than the natural size because | 879 // See whether the source video texture must be clipped. |
| 836 // the encoder might have had to round up to the size of a macroblock. | 880 if (VideoTextureNeedsClipping(video_frame.get())) { |
| 837 // Make sure to only copy the natural size to avoid putting garbage | 881 // Reallocate destination texture and copy only valid region. |
| 838 // into the bottom of the destination texture. | 882 gfx::Size dest_rect = video_frame->visible_rect(); |
| 839 const gfx::Size& natural_size = video_frame->natural_size(); | 883 #if DCHECK_IS_ON() |
| 840 destination_gl->CopySubTextureCHROMIUM( | 884 // The caller should have bound _texture_ to the GL_TEXTURE_2D |
| 841 intermediate_texture, 0, GL_TEXTURE_2D, texture, 0, 0, 0, 0, 0, | 885 // binding point already. |
| 842 natural_size.width(), natural_size.height(), flip_y, premultiply_alpha, | 886 GLuint current_texture = 0; |
| 843 false); | 887 destination_gl->GetIntegerv(GL_TEXTURE_BINDING_2D, |
| 888 reinterpret_cast<GLint*>(¤t_texture)); | |
| 889 DCHECK_EQ(current_texture, texture); | |
| 890 // There should always be enough data in the source texture to | |
| 891 // cover this copy. | |
| 892 DCHECK_LE(dest_rect.width(), video_frame->coded_size().width()); | |
| 893 DCHECK_LE(dest_rect.height(), video_frame->coded_size().height()); | |
| 894 #endif | |
| 895 destination_gl->TexImage2D(GL_TEXTURE_2D, 0, internal_format, | |
| 896 dest_rect.width(), dest_rect.height(), 0, | |
| 897 internal_format, type, nullptr); | |
| 898 destination_gl->CopySubTextureCHROMIUM( | |
| 899 intermediate_texture, 0, GL_TEXTURE_2D, texture, 0, 0, 0, | |
| 900 dest_rect.x(), dest_rect.y(), dest_rect.width(), dest_rect.height(), | |
| 901 flip_y, premultiply_alpha, false); | |
| 902 } else { | |
| 903 destination_gl->CopyTextureCHROMIUM( | |
| 904 intermediate_texture, 0, GL_TEXTURE_2D, texture, 0, internal_format, | |
| 905 type, flip_y, premultiply_alpha, false); | |
| 906 } | |
| 907 | |
| 844 destination_gl->DeleteTextures(1, &intermediate_texture); | 908 destination_gl->DeleteTextures(1, &intermediate_texture); |
| 845 | 909 |
| 846 // Wait for destination context to consume mailbox before deleting it in | 910 // Wait for destination context to consume mailbox before deleting it in |
| 847 // canvas context. | 911 // canvas context. |
| 848 const GLuint64 dest_fence_sync = destination_gl->InsertFenceSyncCHROMIUM(); | 912 const GLuint64 dest_fence_sync = destination_gl->InsertFenceSyncCHROMIUM(); |
| 849 destination_gl->ShallowFlushCHROMIUM(); | 913 destination_gl->ShallowFlushCHROMIUM(); |
| 850 gpu::SyncToken dest_sync_token; | 914 gpu::SyncToken dest_sync_token; |
| 851 destination_gl->GenSyncTokenCHROMIUM(dest_fence_sync, | 915 destination_gl->GenSyncTokenCHROMIUM(dest_fence_sync, |
| 852 dest_sync_token.GetData()); | 916 dest_sync_token.GetData()); |
| 853 canvas_gl->WaitSyncTokenCHROMIUM(dest_sync_token.GetConstData()); | 917 canvas_gl->WaitSyncTokenCHROMIUM(dest_sync_token.GetConstData()); |
| 854 | 918 |
| 855 SyncTokenClientImpl client(canvas_gl); | 919 SyncTokenClientImpl client(canvas_gl); |
| 856 video_frame->UpdateReleaseSyncToken(&client); | 920 video_frame->UpdateReleaseSyncToken(&client); |
| 857 } else { | 921 } else { |
| 858 CopyVideoFrameSingleTextureToGLTexture(destination_gl, video_frame.get(), | 922 CopyVideoFrameSingleTextureToGLTexture( |
| 859 texture, premultiply_alpha, flip_y); | 923 destination_gl, video_frame.get(), SingleFrameForWebGL, texture, |
| 924 internal_format, type, premultiply_alpha, flip_y); | |
| 860 } | 925 } |
| 861 | 926 |
| 862 return true; | 927 return true; |
| 863 } | 928 } |
| 864 | 929 |
| 865 bool SkCanvasVideoRenderer::TexImage2D(unsigned target, | 930 bool SkCanvasVideoRenderer::TexImage2D(unsigned target, |
| 866 gpu::gles2::GLES2Interface* gl, | 931 gpu::gles2::GLES2Interface* gl, |
| 867 VideoFrame* frame, | 932 VideoFrame* frame, |
| 868 int level, | 933 int level, |
| 869 int internalformat, | 934 int internalformat, |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 957 last_image_->bounds().contains(visible_rect)) { | 1022 last_image_->bounds().contains(visible_rect)) { |
| 958 last_image_ = last_image_->makeSubset(visible_rect); | 1023 last_image_ = last_image_->makeSubset(visible_rect); |
| 959 } | 1024 } |
| 960 } | 1025 } |
| 961 | 1026 |
| 962 SkISize SkCanvasVideoRenderer::LastImageDimensionsForTesting() { | 1027 SkISize SkCanvasVideoRenderer::LastImageDimensionsForTesting() { |
| 963 return last_image_dimensions_for_testing_; | 1028 return last_image_dimensions_for_testing_; |
| 964 } | 1029 } |
| 965 | 1030 |
| 966 } // namespace media | 1031 } // namespace media |
| OLD | NEW |