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(); |
| 175 } |
| 176 |
163 // Creates a SkImage from a |video_frame| backed by native resources. | 177 // Creates a SkImage from a |video_frame| backed by native resources. |
164 // The SkImage will take ownership of the underlying resource. | 178 // The SkImage will take ownership of the underlying resource. |
165 sk_sp<SkImage> NewSkImageFromVideoFrameNative(VideoFrame* video_frame, | 179 sk_sp<SkImage> NewSkImageFromVideoFrameNative(VideoFrame* video_frame, |
166 const Context3D& context_3d) { | 180 const Context3D& context_3d) { |
167 DCHECK(PIXEL_FORMAT_ARGB == video_frame->format() || | 181 DCHECK(PIXEL_FORMAT_ARGB == video_frame->format() || |
168 PIXEL_FORMAT_XRGB == video_frame->format() || | 182 PIXEL_FORMAT_XRGB == video_frame->format() || |
169 PIXEL_FORMAT_NV12 == video_frame->format() || | 183 PIXEL_FORMAT_NV12 == video_frame->format() || |
170 PIXEL_FORMAT_UYVY == video_frame->format()); | 184 PIXEL_FORMAT_UYVY == video_frame->format()); |
171 | 185 |
172 const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0); | 186 const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0); |
173 DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D || | 187 DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D || |
174 mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB || | 188 mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB || |
175 mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES) | 189 mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES) |
176 << mailbox_holder.texture_target; | 190 << mailbox_holder.texture_target; |
177 | 191 |
178 gpu::gles2::GLES2Interface* gl = context_3d.gl; | 192 gpu::gles2::GLES2Interface* gl = context_3d.gl; |
179 unsigned source_texture = 0; | 193 unsigned source_texture = 0; |
180 if (mailbox_holder.texture_target != GL_TEXTURE_2D) { | 194 if (mailbox_holder.texture_target != GL_TEXTURE_2D) { |
181 // TODO(dcastagna): At the moment Skia doesn't support targets different | 195 // TODO(dcastagna): At the moment Skia doesn't support targets different |
182 // than GL_TEXTURE_2D. Avoid this copy once | 196 // than GL_TEXTURE_2D. Avoid this copy once |
183 // https://code.google.com/p/skia/issues/detail?id=3868 is addressed. | 197 // https://code.google.com/p/skia/issues/detail?id=3868 is addressed. |
184 gl->GenTextures(1, &source_texture); | 198 gl->GenTextures(1, &source_texture); |
185 DCHECK(source_texture); | 199 DCHECK(source_texture); |
186 gl->BindTexture(GL_TEXTURE_2D, source_texture); | 200 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( | 201 SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture( |
192 gl, video_frame, source_texture, true, false); | 202 gl, video_frame, |
| 203 SkCanvasVideoRenderer::SingleFrameForVideoElementOrCanvas, |
| 204 source_texture, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, true, false); |
193 } else { | 205 } else { |
194 gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); | 206 gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); |
195 source_texture = gl->CreateAndConsumeTextureCHROMIUM( | 207 source_texture = gl->CreateAndConsumeTextureCHROMIUM( |
196 mailbox_holder.texture_target, mailbox_holder.mailbox.name); | 208 mailbox_holder.texture_target, mailbox_holder.mailbox.name); |
197 } | 209 } |
198 GrBackendTextureDesc desc; | 210 GrBackendTextureDesc desc; |
199 desc.fFlags = kRenderTarget_GrBackendTextureFlag; | 211 desc.fFlags = kRenderTarget_GrBackendTextureFlag; |
200 desc.fOrigin = kTopLeft_GrSurfaceOrigin; | 212 desc.fOrigin = kTopLeft_GrSurfaceOrigin; |
201 desc.fWidth = video_frame->coded_size().width(); | 213 desc.fWidth = video_frame->coded_size().width(); |
202 desc.fHeight = video_frame->coded_size().height(); | 214 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: | 758 case PIXEL_FORMAT_Y8: |
747 case PIXEL_FORMAT_UNKNOWN: | 759 case PIXEL_FORMAT_UNKNOWN: |
748 NOTREACHED() << "Only YUV formats and Y16 are supported."; | 760 NOTREACHED() << "Only YUV formats and Y16 are supported."; |
749 } | 761 } |
750 } | 762 } |
751 | 763 |
752 // static | 764 // static |
753 void SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture( | 765 void SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture( |
754 gpu::gles2::GLES2Interface* gl, | 766 gpu::gles2::GLES2Interface* gl, |
755 VideoFrame* video_frame, | 767 VideoFrame* video_frame, |
| 768 SingleFrameCopyMode copy_mode, |
756 unsigned int texture, | 769 unsigned int texture, |
| 770 unsigned int internal_format, |
| 771 unsigned int format, |
| 772 unsigned int type, |
757 bool premultiply_alpha, | 773 bool premultiply_alpha, |
758 bool flip_y) { | 774 bool flip_y) { |
759 DCHECK(video_frame); | 775 DCHECK(video_frame); |
760 DCHECK(video_frame->HasTextures()); | 776 DCHECK(video_frame->HasTextures()); |
761 | 777 |
762 const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0); | 778 const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(0); |
763 DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D || | 779 DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D || |
764 mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB || | 780 mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB || |
765 mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES) | 781 mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES) |
766 << mailbox_holder.texture_target; | 782 << mailbox_holder.texture_target; |
767 | 783 |
768 gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); | 784 gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); |
769 uint32_t source_texture = gl->CreateAndConsumeTextureCHROMIUM( | 785 uint32_t source_texture = gl->CreateAndConsumeTextureCHROMIUM( |
770 mailbox_holder.texture_target, mailbox_holder.mailbox.name); | 786 mailbox_holder.texture_target, mailbox_holder.mailbox.name); |
771 | 787 |
772 // The video is stored in a unmultiplied format, so premultiply | 788 // The video is stored in a unmultiplied format, so premultiply |
773 // if necessary. | 789 // if necessary. |
774 // Application itself needs to take care of setting the right |flip_y| | 790 // Application itself needs to take care of setting the right |flip_y| |
775 // value down to get the expected result. | 791 // value down to get the expected result. |
776 // "flip_y == true" means to reverse the video orientation while | 792 // "flip_y == true" means to reverse the video orientation while |
777 // "flip_y == false" means to keep the intrinsic orientation. | 793 // "flip_y == false" means to keep the intrinsic orientation. |
778 | 794 |
779 // The video's texture might be larger than the natural size because | 795 if (copy_mode == SingleFrameForVideoElementOrCanvas || |
780 // the encoder might have had to round up to the size of a macroblock. | 796 !VideoTextureNeedsClipping(video_frame)) { |
781 // Make sure to only copy the natural size to avoid putting garbage | 797 // No need to clip the source video texture. |
782 // into the bottom of the destination texture. | 798 gl->CopyTextureCHROMIUM(source_texture, 0, GL_TEXTURE_2D, texture, 0, |
783 const gfx::Size& natural_size = video_frame->natural_size(); | 799 internal_format, type, flip_y, premultiply_alpha, |
784 gl->CopySubTextureCHROMIUM(source_texture, 0, GL_TEXTURE_2D, texture, 0, 0, 0, | 800 false); |
785 0, 0, natural_size.width(), natural_size.height(), | 801 } else { |
786 flip_y, premultiply_alpha, false); | 802 // Must reallocate the destination texture and copy only a sub-portion. |
| 803 gfx::Rect dest_rect = video_frame->visible_rect(); |
| 804 #if DCHECK_IS_ON() |
| 805 // The caller should have bound _texture_ to the GL_TEXTURE_2D |
| 806 // binding point already. |
| 807 GLuint current_texture = 0; |
| 808 gl->GetIntegerv(GL_TEXTURE_BINDING_2D, |
| 809 reinterpret_cast<GLint*>(¤t_texture)); |
| 810 DCHECK_EQ(current_texture, texture); |
| 811 // There should always be enough data in the source texture to |
| 812 // cover this copy. |
| 813 DCHECK_LE(dest_rect.width(), video_frame->coded_size().width()); |
| 814 DCHECK_LE(dest_rect.height(), video_frame->coded_size().height()); |
| 815 #endif |
| 816 gl->TexImage2D(GL_TEXTURE_2D, 0, internal_format, dest_rect.width(), |
| 817 dest_rect.height(), 0, format, type, nullptr); |
| 818 gl->CopySubTextureCHROMIUM(source_texture, 0, GL_TEXTURE_2D, texture, 0, 0, |
| 819 0, dest_rect.x(), dest_rect.y(), |
| 820 dest_rect.width(), dest_rect.height(), flip_y, |
| 821 premultiply_alpha, false); |
| 822 } |
| 823 |
787 gl->DeleteTextures(1, &source_texture); | 824 gl->DeleteTextures(1, &source_texture); |
788 gl->Flush(); | 825 gl->Flush(); |
789 | 826 |
790 SyncTokenClientImpl client(gl); | 827 SyncTokenClientImpl client(gl); |
791 video_frame->UpdateReleaseSyncToken(&client); | 828 video_frame->UpdateReleaseSyncToken(&client); |
792 } | 829 } |
793 | 830 |
794 bool SkCanvasVideoRenderer::CopyVideoFrameTexturesToGLTexture( | 831 bool SkCanvasVideoRenderer::CopyVideoFrameTexturesToGLTexture( |
795 const Context3D& context_3d, | 832 const Context3D& context_3d, |
796 gpu::gles2::GLES2Interface* destination_gl, | 833 gpu::gles2::GLES2Interface* destination_gl, |
797 const scoped_refptr<VideoFrame>& video_frame, | 834 const scoped_refptr<VideoFrame>& video_frame, |
798 unsigned int texture, | 835 unsigned int texture, |
| 836 unsigned int internal_format, |
| 837 unsigned int format, |
| 838 unsigned int type, |
799 bool premultiply_alpha, | 839 bool premultiply_alpha, |
800 bool flip_y) { | 840 bool flip_y) { |
801 DCHECK(thread_checker_.CalledOnValidThread()); | 841 DCHECK(thread_checker_.CalledOnValidThread()); |
802 DCHECK(video_frame); | 842 DCHECK(video_frame); |
803 DCHECK(video_frame->HasTextures()); | 843 DCHECK(video_frame->HasTextures()); |
804 if (media::VideoFrame::NumPlanes(video_frame->format()) > 1) { | 844 if (media::VideoFrame::NumPlanes(video_frame->format()) > 1) { |
805 if (!context_3d.gr_context) | 845 if (!context_3d.gr_context) |
806 return false; | 846 return false; |
807 if (!UpdateLastImage(video_frame, context_3d)) | 847 if (!UpdateLastImage(video_frame, context_3d)) |
808 return false; | 848 return false; |
(...skipping 16 matching lines...) Expand all Loading... |
825 canvas_gl->ShallowFlushCHROMIUM(); | 865 canvas_gl->ShallowFlushCHROMIUM(); |
826 canvas_gl->GenSyncTokenCHROMIUM(fence_sync, | 866 canvas_gl->GenSyncTokenCHROMIUM(fence_sync, |
827 mailbox_holder.sync_token.GetData()); | 867 mailbox_holder.sync_token.GetData()); |
828 | 868 |
829 destination_gl->WaitSyncTokenCHROMIUM( | 869 destination_gl->WaitSyncTokenCHROMIUM( |
830 mailbox_holder.sync_token.GetConstData()); | 870 mailbox_holder.sync_token.GetConstData()); |
831 uint32_t intermediate_texture = | 871 uint32_t intermediate_texture = |
832 destination_gl->CreateAndConsumeTextureCHROMIUM( | 872 destination_gl->CreateAndConsumeTextureCHROMIUM( |
833 mailbox_holder.texture_target, mailbox_holder.mailbox.name); | 873 mailbox_holder.texture_target, mailbox_holder.mailbox.name); |
834 | 874 |
835 // The video's texture might be larger than the natural size because | 875 // See whether the source video texture must be clipped. |
836 // the encoder might have had to round up to the size of a macroblock. | 876 if (VideoTextureNeedsClipping(video_frame.get())) { |
837 // Make sure to only copy the natural size to avoid putting garbage | 877 // Reallocate destination texture and copy only valid region. |
838 // into the bottom of the destination texture. | 878 gfx::Rect dest_rect = video_frame->visible_rect(); |
839 const gfx::Size& natural_size = video_frame->natural_size(); | 879 #if DCHECK_IS_ON() |
840 destination_gl->CopySubTextureCHROMIUM( | 880 // The caller should have bound _texture_ to the GL_TEXTURE_2D |
841 intermediate_texture, 0, GL_TEXTURE_2D, texture, 0, 0, 0, 0, 0, | 881 // binding point already. |
842 natural_size.width(), natural_size.height(), flip_y, premultiply_alpha, | 882 GLuint current_texture = 0; |
843 false); | 883 destination_gl->GetIntegerv(GL_TEXTURE_BINDING_2D, |
| 884 reinterpret_cast<GLint*>(¤t_texture)); |
| 885 DCHECK_EQ(current_texture, texture); |
| 886 // There should always be enough data in the source texture to |
| 887 // cover this copy. |
| 888 DCHECK_LE(dest_rect.width(), video_frame->coded_size().width()); |
| 889 DCHECK_LE(dest_rect.height(), video_frame->coded_size().height()); |
| 890 #endif |
| 891 destination_gl->TexImage2D(GL_TEXTURE_2D, 0, internal_format, |
| 892 dest_rect.width(), dest_rect.height(), 0, |
| 893 format, type, nullptr); |
| 894 destination_gl->CopySubTextureCHROMIUM( |
| 895 intermediate_texture, 0, GL_TEXTURE_2D, texture, 0, 0, 0, |
| 896 dest_rect.x(), dest_rect.y(), dest_rect.width(), dest_rect.height(), |
| 897 flip_y, premultiply_alpha, false); |
| 898 } else { |
| 899 destination_gl->CopyTextureCHROMIUM( |
| 900 intermediate_texture, 0, GL_TEXTURE_2D, texture, 0, internal_format, |
| 901 type, flip_y, premultiply_alpha, false); |
| 902 } |
| 903 |
844 destination_gl->DeleteTextures(1, &intermediate_texture); | 904 destination_gl->DeleteTextures(1, &intermediate_texture); |
845 | 905 |
846 // Wait for destination context to consume mailbox before deleting it in | 906 // Wait for destination context to consume mailbox before deleting it in |
847 // canvas context. | 907 // canvas context. |
848 const GLuint64 dest_fence_sync = destination_gl->InsertFenceSyncCHROMIUM(); | 908 const GLuint64 dest_fence_sync = destination_gl->InsertFenceSyncCHROMIUM(); |
849 destination_gl->ShallowFlushCHROMIUM(); | 909 destination_gl->ShallowFlushCHROMIUM(); |
850 gpu::SyncToken dest_sync_token; | 910 gpu::SyncToken dest_sync_token; |
851 destination_gl->GenSyncTokenCHROMIUM(dest_fence_sync, | 911 destination_gl->GenSyncTokenCHROMIUM(dest_fence_sync, |
852 dest_sync_token.GetData()); | 912 dest_sync_token.GetData()); |
853 canvas_gl->WaitSyncTokenCHROMIUM(dest_sync_token.GetConstData()); | 913 canvas_gl->WaitSyncTokenCHROMIUM(dest_sync_token.GetConstData()); |
854 | 914 |
855 SyncTokenClientImpl client(canvas_gl); | 915 SyncTokenClientImpl client(canvas_gl); |
856 video_frame->UpdateReleaseSyncToken(&client); | 916 video_frame->UpdateReleaseSyncToken(&client); |
857 } else { | 917 } else { |
858 CopyVideoFrameSingleTextureToGLTexture(destination_gl, video_frame.get(), | 918 CopyVideoFrameSingleTextureToGLTexture( |
859 texture, premultiply_alpha, flip_y); | 919 destination_gl, video_frame.get(), SingleFrameForWebGL, texture, |
| 920 internal_format, format, type, premultiply_alpha, flip_y); |
860 } | 921 } |
861 | 922 |
862 return true; | 923 return true; |
863 } | 924 } |
864 | 925 |
865 bool SkCanvasVideoRenderer::TexImage2D(unsigned target, | 926 bool SkCanvasVideoRenderer::TexImage2D(unsigned target, |
866 gpu::gles2::GLES2Interface* gl, | 927 gpu::gles2::GLES2Interface* gl, |
867 VideoFrame* frame, | 928 VideoFrame* frame, |
868 int level, | 929 int level, |
869 int internalformat, | 930 int internalformat, |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
957 last_image_->bounds().contains(visible_rect)) { | 1018 last_image_->bounds().contains(visible_rect)) { |
958 last_image_ = last_image_->makeSubset(visible_rect); | 1019 last_image_ = last_image_->makeSubset(visible_rect); |
959 } | 1020 } |
960 } | 1021 } |
961 | 1022 |
962 SkISize SkCanvasVideoRenderer::LastImageDimensionsForTesting() { | 1023 SkISize SkCanvasVideoRenderer::LastImageDimensionsForTesting() { |
963 return last_image_dimensions_for_testing_; | 1024 return last_image_dimensions_for_testing_; |
964 } | 1025 } |
965 | 1026 |
966 } // namespace media | 1027 } // namespace media |
OLD | NEW |