Upstream version 10.38.208.0
[platform/framework/web/crosswalk.git] / src / cc / resources / video_resource_updater.cc
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 "base/debug/trace_event.h"
9 #include "cc/output/gl_renderer.h"
10 #include "cc/resources/resource_provider.h"
11 #include "gpu/GLES2/gl2extchromium.h"
12 #include "gpu/command_buffer/client/gles2_interface.h"
13 #include "media/base/video_frame.h"
14 #include "media/filters/skcanvas_video_renderer.h"
15 #include "third_party/khronos/GLES2/gl2.h"
16 #include "third_party/khronos/GLES2/gl2ext.h"
17 #include "ui/gfx/size_conversions.h"
18
19 namespace cc {
20
21 namespace {
22
23 const ResourceFormat kYUVResourceFormat = LUMINANCE_8;
24 const ResourceFormat kRGBResourceFormat = RGBA_8888;
25
26 class SyncPointClientImpl : public media::VideoFrame::SyncPointClient {
27  public:
28   explicit SyncPointClientImpl(gpu::gles2::GLES2Interface* gl) : gl_(gl) {}
29   virtual ~SyncPointClientImpl() {}
30   virtual uint32 InsertSyncPoint() OVERRIDE {
31     return GLC(gl_, gl_->InsertSyncPointCHROMIUM());
32   }
33   virtual void WaitSyncPoint(uint32 sync_point) OVERRIDE {
34     GLC(gl_, gl_->WaitSyncPointCHROMIUM(sync_point));
35   }
36
37  private:
38   gpu::gles2::GLES2Interface* gl_;
39 };
40
41 }  // namespace
42
43 VideoFrameExternalResources::VideoFrameExternalResources() : type(NONE) {}
44
45 VideoFrameExternalResources::~VideoFrameExternalResources() {}
46
47 VideoResourceUpdater::VideoResourceUpdater(ContextProvider* context_provider,
48                                            ResourceProvider* resource_provider)
49     : context_provider_(context_provider),
50       resource_provider_(resource_provider) {
51 }
52
53 VideoResourceUpdater::~VideoResourceUpdater() {
54   while (!all_resources_.empty()) {
55     resource_provider_->DeleteResource(all_resources_.back());
56     all_resources_.pop_back();
57   }
58 }
59
60 void VideoResourceUpdater::DeleteResource(unsigned resource_id) {
61   resource_provider_->DeleteResource(resource_id);
62   all_resources_.erase(std::remove(all_resources_.begin(),
63                                    all_resources_.end(),
64                                    resource_id));
65 }
66
67 VideoFrameExternalResources VideoResourceUpdater::
68     CreateExternalResourcesFromVideoFrame(
69         const scoped_refptr<media::VideoFrame>& video_frame) {
70   if (!VerifyFrame(video_frame))
71     return VideoFrameExternalResources();
72
73   if (video_frame->format() == media::VideoFrame::NATIVE_TEXTURE)
74     return CreateForHardwarePlanes(video_frame);
75   else
76     return CreateForSoftwarePlanes(video_frame);
77 }
78
79 bool VideoResourceUpdater::VerifyFrame(
80     const scoped_refptr<media::VideoFrame>& video_frame) {
81   switch (video_frame->format()) {
82     // Acceptable inputs.
83     case media::VideoFrame::YV12:
84     case media::VideoFrame::I420:
85     case media::VideoFrame::YV12A:
86     case media::VideoFrame::YV16:
87     case media::VideoFrame::YV12J:
88     case media::VideoFrame::YV24:
89     case media::VideoFrame::NATIVE_TEXTURE:
90 #if defined(VIDEO_HOLE)
91     case media::VideoFrame::HOLE:
92 #endif  // defined(VIDEO_HOLE)
93       return true;
94
95     // Unacceptable inputs. ¯\(°_o)/¯
96     case media::VideoFrame::UNKNOWN:
97     case media::VideoFrame::NV12:
98       break;
99   }
100   return false;
101 }
102
103 // For frames that we receive in software format, determine the dimensions of
104 // each plane in the frame.
105 static gfx::Size SoftwarePlaneDimension(
106     const scoped_refptr<media::VideoFrame>& input_frame,
107     ResourceFormat output_resource_format,
108     size_t plane_index) {
109   if (output_resource_format == kYUVResourceFormat) {
110     return media::VideoFrame::PlaneSize(
111         input_frame->format(), plane_index, input_frame->coded_size());
112   }
113
114   DCHECK_EQ(output_resource_format, kRGBResourceFormat);
115   return input_frame->coded_size();
116 }
117
118 VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes(
119     const scoped_refptr<media::VideoFrame>& video_frame) {
120   TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForSoftwarePlanes");
121   media::VideoFrame::Format input_frame_format = video_frame->format();
122
123 #if defined(VIDEO_HOLE)
124   if (input_frame_format == media::VideoFrame::HOLE) {
125     VideoFrameExternalResources external_resources;
126     external_resources.type = VideoFrameExternalResources::HOLE;
127     return external_resources;
128   }
129 #endif  // defined(VIDEO_HOLE)
130
131   // Only YUV software video frames are supported.
132   DCHECK(input_frame_format == media::VideoFrame::YV12 ||
133          input_frame_format == media::VideoFrame::I420 ||
134          input_frame_format == media::VideoFrame::YV12A ||
135          input_frame_format == media::VideoFrame::YV12J ||
136          input_frame_format == media::VideoFrame::YV16 ||
137          input_frame_format == media::VideoFrame::YV24);
138   if (input_frame_format != media::VideoFrame::YV12 &&
139       input_frame_format != media::VideoFrame::I420 &&
140       input_frame_format != media::VideoFrame::YV12A &&
141       input_frame_format != media::VideoFrame::YV12J &&
142       input_frame_format != media::VideoFrame::YV16 &&
143       input_frame_format != media::VideoFrame::YV24)
144     return VideoFrameExternalResources();
145
146   bool software_compositor = context_provider_ == NULL;
147
148   ResourceFormat output_resource_format = kYUVResourceFormat;
149   size_t output_plane_count = media::VideoFrame::NumPlanes(input_frame_format);
150
151   // TODO(skaslev): If we're in software compositing mode, we do the YUV -> RGB
152   // conversion here. That involves an extra copy of each frame to a bitmap.
153   // Obviously, this is suboptimal and should be addressed once ubercompositor
154   // starts shaping up.
155   if (software_compositor) {
156     output_resource_format = kRGBResourceFormat;
157     output_plane_count = 1;
158   }
159
160   int max_resource_size = resource_provider_->max_texture_size();
161   std::vector<PlaneResource> plane_resources;
162   bool allocation_success = true;
163
164   for (size_t i = 0; i < output_plane_count; ++i) {
165     gfx::Size output_plane_resource_size =
166         SoftwarePlaneDimension(video_frame, output_resource_format, i);
167     if (output_plane_resource_size.IsEmpty() ||
168         output_plane_resource_size.width() > max_resource_size ||
169         output_plane_resource_size.height() > max_resource_size) {
170       allocation_success = false;
171       break;
172     }
173
174     ResourceProvider::ResourceId resource_id = 0;
175     gpu::Mailbox mailbox;
176
177     // Try recycle a previously-allocated resource.
178     for (size_t i = 0; i < recycled_resources_.size(); ++i) {
179       bool resource_matches =
180           recycled_resources_[i].resource_format == output_resource_format &&
181           recycled_resources_[i].resource_size == output_plane_resource_size;
182       bool not_in_use =
183           !software_compositor || !resource_provider_->InUseByConsumer(
184                                        recycled_resources_[i].resource_id);
185       if (resource_matches && not_in_use) {
186         resource_id = recycled_resources_[i].resource_id;
187         mailbox = recycled_resources_[i].mailbox;
188         recycled_resources_.erase(recycled_resources_.begin() + i);
189         break;
190       }
191     }
192
193     if (resource_id == 0) {
194       // TODO(danakj): Abstract out hw/sw resource create/delete from
195       // ResourceProvider and stop using ResourceProvider in this class.
196       resource_id =
197           resource_provider_->CreateResource(output_plane_resource_size,
198                                              GL_CLAMP_TO_EDGE,
199                                              ResourceProvider::TextureUsageAny,
200                                              output_resource_format);
201
202       DCHECK(mailbox.IsZero());
203
204       if (!software_compositor) {
205         DCHECK(context_provider_);
206
207         gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
208
209         GLC(gl, gl->GenMailboxCHROMIUM(mailbox.name));
210         ResourceProvider::ScopedWriteLockGL lock(resource_provider_,
211                                                  resource_id);
212         GLC(gl,
213             gl->ProduceTextureDirectCHROMIUM(
214                 lock.texture_id(), GL_TEXTURE_2D, mailbox.name));
215       }
216
217       if (resource_id)
218         all_resources_.push_back(resource_id);
219     }
220
221     if (resource_id == 0) {
222       allocation_success = false;
223       break;
224     }
225
226     DCHECK(software_compositor || !mailbox.IsZero());
227     plane_resources.push_back(PlaneResource(resource_id,
228                                             output_plane_resource_size,
229                                             output_resource_format,
230                                             mailbox));
231   }
232
233   if (!allocation_success) {
234     for (size_t i = 0; i < plane_resources.size(); ++i)
235       DeleteResource(plane_resources[i].resource_id);
236     return VideoFrameExternalResources();
237   }
238
239   VideoFrameExternalResources external_resources;
240
241   if (software_compositor) {
242     DCHECK_EQ(plane_resources.size(), 1u);
243     DCHECK_EQ(plane_resources[0].resource_format, kRGBResourceFormat);
244     DCHECK(plane_resources[0].mailbox.IsZero());
245
246     if (!video_renderer_)
247       video_renderer_.reset(new media::SkCanvasVideoRenderer);
248
249     {
250       ResourceProvider::ScopedWriteLockSoftware lock(
251           resource_provider_, plane_resources[0].resource_id);
252       video_renderer_->Copy(video_frame.get(), lock.sk_canvas());
253     }
254
255     RecycleResourceData recycle_data = {
256       plane_resources[0].resource_id,
257       plane_resources[0].resource_size,
258       plane_resources[0].resource_format,
259       gpu::Mailbox()
260     };
261     external_resources.software_resources.push_back(
262         plane_resources[0].resource_id);
263     external_resources.software_release_callback =
264         base::Bind(&RecycleResource, AsWeakPtr(), recycle_data);
265     external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE;
266
267     return external_resources;
268   }
269
270   for (size_t i = 0; i < plane_resources.size(); ++i) {
271     // Update each plane's resource id with its content.
272     DCHECK_EQ(plane_resources[i].resource_format, kYUVResourceFormat);
273
274     const uint8_t* input_plane_pixels = video_frame->data(i);
275
276     gfx::Rect image_rect(0,
277                          0,
278                          video_frame->stride(i),
279                          plane_resources[i].resource_size.height());
280     gfx::Rect source_rect(plane_resources[i].resource_size);
281     resource_provider_->SetPixels(plane_resources[i].resource_id,
282                                   input_plane_pixels,
283                                   image_rect,
284                                   source_rect,
285                                   gfx::Vector2d());
286
287     RecycleResourceData recycle_data = {
288       plane_resources[i].resource_id,
289       plane_resources[i].resource_size,
290       plane_resources[i].resource_format,
291       plane_resources[i].mailbox
292     };
293
294     external_resources.mailboxes.push_back(
295         TextureMailbox(plane_resources[i].mailbox, GL_TEXTURE_2D, 0));
296     external_resources.release_callbacks.push_back(
297         base::Bind(&RecycleResource, AsWeakPtr(), recycle_data));
298   }
299
300   external_resources.type = VideoFrameExternalResources::YUV_RESOURCE;
301   return external_resources;
302 }
303
304 // static
305 void VideoResourceUpdater::ReturnTexture(
306     base::WeakPtr<VideoResourceUpdater> updater,
307     const scoped_refptr<media::VideoFrame>& video_frame,
308     uint32 sync_point,
309     bool lost_resource) {
310   // TODO(dshwang) this case should be forwarded to the decoder as lost
311   // resource.
312   if (lost_resource || !updater.get())
313     return;
314   // VideoFrame::UpdateReleaseSyncPoint() creates new sync point using the same
315   // GL context which created the given |sync_point|, so discard the
316   // |sync_point|.
317   SyncPointClientImpl client(updater->context_provider_->ContextGL());
318   video_frame->UpdateReleaseSyncPoint(&client);
319 }
320
321 VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes(
322     const scoped_refptr<media::VideoFrame>& video_frame) {
323   TRACE_EVENT0("cc", "VideoResourceUpdater::CreateForHardwarePlanes");
324   media::VideoFrame::Format frame_format = video_frame->format();
325
326   DCHECK_EQ(frame_format, media::VideoFrame::NATIVE_TEXTURE);
327   if (frame_format != media::VideoFrame::NATIVE_TEXTURE)
328       return VideoFrameExternalResources();
329
330   if (!context_provider_)
331     return VideoFrameExternalResources();
332
333   const gpu::MailboxHolder* mailbox_holder = video_frame->mailbox_holder();
334   VideoFrameExternalResources external_resources;
335   switch (mailbox_holder->texture_target) {
336     case GL_TEXTURE_2D:
337       external_resources.type = VideoFrameExternalResources::RGB_RESOURCE;
338       break;
339     case GL_TEXTURE_EXTERNAL_OES:
340       external_resources.type =
341           VideoFrameExternalResources::STREAM_TEXTURE_RESOURCE;
342       break;
343     case GL_TEXTURE_RECTANGLE_ARB:
344       external_resources.type = VideoFrameExternalResources::IO_SURFACE;
345       break;
346     default:
347       NOTREACHED();
348       return VideoFrameExternalResources();
349   }
350
351   external_resources.mailboxes.push_back(
352       TextureMailbox(mailbox_holder->mailbox,
353                      mailbox_holder->texture_target,
354                      mailbox_holder->sync_point));
355   external_resources.release_callbacks.push_back(
356       base::Bind(&ReturnTexture, AsWeakPtr(), video_frame));
357   return external_resources;
358 }
359
360 // static
361 void VideoResourceUpdater::RecycleResource(
362     base::WeakPtr<VideoResourceUpdater> updater,
363     RecycleResourceData data,
364     uint32 sync_point,
365     bool lost_resource) {
366   if (!updater.get()) {
367     // Resource was already deleted.
368     return;
369   }
370
371   ContextProvider* context_provider = updater->context_provider_;
372   if (context_provider && sync_point) {
373     GLC(context_provider->ContextGL(),
374         context_provider->ContextGL()->WaitSyncPointCHROMIUM(sync_point));
375   }
376
377   if (lost_resource) {
378     updater->DeleteResource(data.resource_id);
379     return;
380   }
381
382   // Drop recycled resources that are the wrong format.
383   while (!updater->recycled_resources_.empty() &&
384          updater->recycled_resources_.back().resource_format !=
385          data.resource_format) {
386     updater->DeleteResource(updater->recycled_resources_.back().resource_id);
387     updater->recycled_resources_.pop_back();
388   }
389
390   PlaneResource recycled_resource(data.resource_id,
391                                   data.resource_size,
392                                   data.resource_format,
393                                   data.mailbox);
394   updater->recycled_resources_.push_back(recycled_resource);
395 }
396
397 }  // namespace cc