OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 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. | |
4 | |
5 #include "cc/resources/video_resource_updater.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "cc/output/gl_renderer.h" | |
9 #include "cc/resources/resource_provider.h" | |
10 #include "gpu/GLES2/gl2extchromium.h" | |
11 #include "media/base/video_frame.h" | |
12 #include "media/filters/skcanvas_video_renderer.h" | |
13 #include "third_party/khronos/GLES2/gl2.h" | |
14 #include "third_party/khronos/GLES2/gl2ext.h" | |
15 #include "ui/gfx/size_conversions.h" | |
16 | |
17 const unsigned kYUVResourceFormat = GL_LUMINANCE; | |
18 const unsigned kRGBResourceFormat = GL_RGBA; | |
19 | |
20 namespace cc { | |
21 | |
22 VideoFrameExternalResources::VideoFrameExternalResources() : type(NONE) {} | |
23 | |
24 VideoFrameExternalResources::~VideoFrameExternalResources() {} | |
25 | |
26 VideoResourceUpdater::VideoResourceUpdater(ResourceProvider* resource_provider) | |
27 : resource_provider_(resource_provider) { | |
28 } | |
29 | |
30 VideoResourceUpdater::~VideoResourceUpdater() {} | |
31 | |
32 bool VideoResourceUpdater::VerifyFrame( | |
33 const scoped_refptr<media::VideoFrame>& video_frame) { | |
34 // If these fail, we'll have to add logic that handles offset bitmap/texture | |
35 // UVs. For now, just expect (0, 0) offset, since all our decoders so far | |
36 // don't offset. | |
37 DCHECK_EQ(video_frame->visible_rect().x(), 0); | |
38 DCHECK_EQ(video_frame->visible_rect().y(), 0); | |
39 | |
40 switch (video_frame->format()) { | |
41 // Acceptable inputs. | |
42 case media::VideoFrame::YV12: | |
43 case media::VideoFrame::YV16: | |
44 case media::VideoFrame::NATIVE_TEXTURE: | |
45 #if defined(GOOGLE_TV) | |
46 case media::VideoFrame::HOLE: | |
47 #endif | |
48 return true; | |
49 | |
50 // Unacceptable inputs. ¯\(°_o)/¯ | |
51 case media::VideoFrame::INVALID: | |
52 case media::VideoFrame::RGB32: | |
53 case media::VideoFrame::EMPTY: | |
54 case media::VideoFrame::I420: | |
55 break; | |
56 } | |
57 return false; | |
58 } | |
59 | |
60 // For frames that we receive in software format, determine the dimensions of | |
61 // each plane in the frame. | |
62 static gfx::Size SoftwarePlaneDimension( | |
63 media::VideoFrame::Format input_frame_format, | |
64 gfx::Size coded_size, | |
65 GLenum output_resource_format, | |
66 int plane_index) { | |
67 if (output_resource_format == kYUVResourceFormat) { | |
68 if (plane_index == media::VideoFrame::kYPlane) | |
69 return coded_size; | |
70 | |
71 switch (input_frame_format) { | |
72 case media::VideoFrame::YV12: | |
73 return gfx::ToFlooredSize(gfx::ScaleSize(coded_size, 0.5f, 0.5f)); | |
74 case media::VideoFrame::YV16: | |
75 return gfx::ToFlooredSize(gfx::ScaleSize(coded_size, 0.5f, 1.f)); | |
76 | |
77 case media::VideoFrame::INVALID: | |
78 case media::VideoFrame::RGB32: | |
79 case media::VideoFrame::EMPTY: | |
80 case media::VideoFrame::I420: | |
81 case media::VideoFrame::NATIVE_TEXTURE: | |
82 #if defined(GOOGLE_TV) | |
83 case media::VideoFrame::HOLE: | |
84 #endif | |
85 NOTREACHED(); | |
86 } | |
87 } | |
88 | |
89 DCHECK_EQ(output_resource_format, static_cast<unsigned>(kRGBResourceFormat)); | |
90 return coded_size; | |
91 } | |
92 | |
93 static void ReleaseResource(base::WeakPtr<ResourceProvider> resource_provider, | |
94 ResourceProvider::ResourceId resource_id, | |
95 unsigned sync_point) { | |
96 if (resource_provider) | |
97 resource_provider->DeleteResource(resource_id); | |
98 } | |
99 | |
100 VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( | |
101 const scoped_refptr<media::VideoFrame>& video_frame) { | |
102 if (!VerifyFrame(video_frame)) | |
103 return VideoFrameExternalResources(); | |
104 | |
105 media::VideoFrame::Format input_frame_format = video_frame->format(); | |
106 | |
107 #if defined(GOOGLE_TV) | |
108 if (input_frame_format == media::VideoFrame::HOLE) { | |
109 VideoFrameExternalResources external_resources; | |
110 external_resources.type = VideoFrameExternalResources::HOLE; | |
111 return external_resources; | |
112 } | |
113 #endif | |
114 | |
115 // Only YUV software video frames are supported. | |
116 DCHECK(input_frame_format == media::VideoFrame::YV12 || | |
117 input_frame_format == media::VideoFrame::YV16); | |
118 if (input_frame_format != media::VideoFrame::YV12 && | |
119 input_frame_format != media::VideoFrame::YV16) | |
120 return VideoFrameExternalResources(); | |
121 | |
122 bool software_compositor = !resource_provider_->GraphicsContext3D(); | |
123 | |
124 GLenum output_resource_format = kYUVResourceFormat; | |
125 size_t output_plane_count = 3; | |
126 | |
127 // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB | |
128 // conversion here. That involves an extra copy of each frame to a bitmap. | |
129 // Obviously, this is suboptimal and should be addressed once ubercompositor | |
130 // starts shaping up. | |
piman
2013/04/08 21:03:47
(not for this CL) would it make sense to move the
danakj
2013/04/08 21:44:56
I think we should only be generating 1 mailbox vid
| |
131 if (software_compositor) { | |
132 output_resource_format = kRGBResourceFormat; | |
133 output_plane_count = 1; | |
134 } | |
135 | |
136 int max_resource_size = resource_provider_->max_texture_size(); | |
137 gfx::Size coded_frame_size = video_frame->coded_size(); | |
138 | |
139 ResourceProvider::ResourceIdArray plane_resources; | |
140 bool allocation_success = true; | |
141 | |
142 for (size_t i = 0; i < output_plane_count; ++i) { | |
143 gfx::Size plane_size = | |
144 SoftwarePlaneDimension(input_frame_format, | |
145 coded_frame_size, | |
146 output_resource_format, | |
147 i); | |
148 if (plane_size.IsEmpty() || | |
149 plane_size.width() > max_resource_size || | |
150 plane_size.height() > max_resource_size) { | |
151 allocation_success = false; | |
152 break; | |
153 } | |
154 | |
155 // TODO(danakj): Could recycle resources that we previously allocated and | |
156 // were returned to us. | |
piman
2013/04/08 21:03:47
I think this is important. genMailboxCHROMIUM is a
danakj
2013/04/08 21:44:56
OK, I will do this in my next CL, is that sufficie
| |
157 ResourceProvider::ResourceId resource_id = | |
158 resource_provider_->CreateResource(plane_size, | |
159 output_resource_format, | |
160 ResourceProvider::TextureUsageAny); | |
161 if (resource_id == 0) { | |
162 allocation_success = false; | |
163 break; | |
164 } | |
165 | |
166 plane_resources.push_back(resource_id); | |
167 } | |
168 | |
169 if (!allocation_success) { | |
170 for (size_t i = 0; i < plane_resources.size(); ++i) | |
171 resource_provider_->DeleteResource(plane_resources[i]); | |
172 return VideoFrameExternalResources(); | |
173 } | |
174 | |
175 VideoFrameExternalResources external_resources; | |
176 | |
177 if (software_compositor) { | |
178 DCHECK_EQ(output_resource_format, kRGBResourceFormat); | |
179 DCHECK_EQ(plane_resources.size(), 1u); | |
180 | |
181 if (!video_renderer_) | |
182 video_renderer_.reset(new media::SkCanvasVideoRenderer); | |
183 | |
184 { | |
185 ResourceProvider::ScopedWriteLockSoftware lock( | |
186 resource_provider_, plane_resources[0]); | |
187 video_renderer_->Paint(video_frame, | |
188 lock.sk_canvas(), | |
189 video_frame->visible_rect(), | |
190 0xff); | |
191 } | |
192 | |
193 TextureMailbox::ReleaseCallback callback_to_free_resource = | |
194 base::Bind(&ReleaseResource, | |
195 resource_provider_->AsWeakPtr(), | |
196 plane_resources[0]); | |
197 external_resources.software_resources.push_back(plane_resources[0]); | |
198 external_resources.software_release_callback = callback_to_free_resource; | |
199 | |
200 external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE; | |
201 return external_resources; | |
202 } | |
203 | |
204 DCHECK_EQ(output_resource_format, | |
205 static_cast<unsigned>(kYUVResourceFormat)); | |
206 | |
207 WebKit::WebGraphicsContext3D* context = | |
208 resource_provider_->GraphicsContext3D(); | |
209 DCHECK(context); | |
210 | |
211 for (size_t plane = 0; plane < plane_resources.size(); ++plane) { | |
212 // Update each plane's resource id with its content. | |
213 ResourceProvider::ResourceId output_plane_resource_id = | |
214 plane_resources[plane]; | |
215 gfx::Size plane_size = | |
216 SoftwarePlaneDimension(input_frame_format, | |
217 coded_frame_size, | |
218 output_resource_format, | |
219 plane); | |
220 const uint8_t* input_plane_pixels = video_frame->data(plane); | |
221 | |
222 gfx::Rect image_rect( | |
223 0, 0, video_frame->stride(plane), plane_size.height()); | |
224 gfx::Rect source_rect(plane_size); | |
225 resource_provider_->SetPixels(output_plane_resource_id, | |
226 input_plane_pixels, | |
227 image_rect, | |
228 source_rect, | |
229 gfx::Vector2d()); | |
230 | |
231 gpu::Mailbox mailbox; | |
232 { | |
233 ResourceProvider::ScopedWriteLockGL lock( | |
234 resource_provider_, output_plane_resource_id); | |
235 | |
236 GLC(context, context->genMailboxCHROMIUM(mailbox.name)); | |
237 GLC(context, context->bindTexture(GL_TEXTURE_2D, lock.texture_id())); | |
238 GLC(context, context->produceTextureCHROMIUM(GL_TEXTURE_2D, | |
239 mailbox.name)); | |
240 GLC(context, context->bindTexture(GL_TEXTURE_2D, 0)); | |
241 } | |
242 | |
243 TextureMailbox::ReleaseCallback callback_to_free_resource = | |
244 base::Bind(&ReleaseResource, | |
245 resource_provider_->AsWeakPtr(), | |
246 output_plane_resource_id); | |
247 external_resources.mailboxes.push_back( | |
248 TextureMailbox(mailbox, callback_to_free_resource)); | |
249 } | |
250 | |
251 external_resources.type = VideoFrameExternalResources::YUV_RESOURCE; | |
252 return external_resources; | |
253 } | |
254 | |
255 VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes( | |
256 const scoped_refptr<media::VideoFrame>& video_frame, | |
257 const TextureMailbox::ReleaseCallback& release_callback) { | |
258 if (!VerifyFrame(video_frame)) | |
259 return VideoFrameExternalResources(); | |
260 | |
261 media::VideoFrame::Format frame_format = video_frame->format(); | |
262 | |
263 DCHECK_EQ(frame_format, media::VideoFrame::NATIVE_TEXTURE); | |
264 if (frame_format != media::VideoFrame::NATIVE_TEXTURE) | |
265 return VideoFrameExternalResources(); | |
266 | |
267 WebKit::WebGraphicsContext3D* context = | |
268 resource_provider_->GraphicsContext3D(); | |
269 if (!context) | |
270 return VideoFrameExternalResources(); | |
271 | |
272 VideoFrameExternalResources external_resources; | |
273 switch (video_frame->texture_target()) { | |
274 case GL_TEXTURE_2D: | |
275 external_resources.type = VideoFrameExternalResources::RGB_RESOURCE; | |
276 break; | |
277 case GL_TEXTURE_EXTERNAL_OES: | |
278 external_resources.type = | |
279 VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE; | |
280 break; | |
281 case GL_TEXTURE_RECTANGLE_ARB: | |
282 external_resources.type = VideoFrameExternalResources::IO_SURFACE; | |
283 break; | |
284 default: | |
285 NOTREACHED(); | |
286 return VideoFrameExternalResources(); | |
287 } | |
288 | |
289 gpu::Mailbox mailbox; | |
290 GLC(context, context->genMailboxCHROMIUM(mailbox.name)); | |
291 GLC(context, context->bindTexture(GL_TEXTURE_2D, video_frame->texture_id())); | |
292 GLC(context, context->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name)); | |
293 GLC(context, context->bindTexture(GL_TEXTURE_2D, 0)); | |
294 | |
295 external_resources.mailboxes.push_back( | |
296 TextureMailbox(mailbox, release_callback)); | |
297 return external_resources; | |
298 } | |
299 | |
300 } // namespace cc | |
OLD | NEW |