| Index: media/video/gpu_memory_buffer_video_frame_pool.cc
 | 
| diff --git a/media/video/gpu_memory_buffer_video_frame_pool.cc b/media/video/gpu_memory_buffer_video_frame_pool.cc
 | 
| index 777a1fc279892fa6afdf8db4071094bdc4469f80..6119e0e72c34b6087949398f3987b51b2228b8df 100644
 | 
| --- a/media/video/gpu_memory_buffer_video_frame_pool.cc
 | 
| +++ b/media/video/gpu_memory_buffer_video_frame_pool.cc
 | 
| @@ -4,180 +4,27 @@
 | 
|  
 | 
|  #include "media/video/gpu_memory_buffer_video_frame_pool.h"
 | 
|  
 | 
| -#include <GLES2/gl2.h>
 | 
| -#include <GLES2/gl2ext.h>
 | 
| -#include <stddef.h>
 | 
| -#include <stdint.h>
 | 
| -
 | 
|  #include <algorithm>
 | 
|  #include <list>
 | 
| -#include <utility>
 | 
|  
 | 
| -#include "base/barrier_closure.h"
 | 
|  #include "base/bind.h"
 | 
| -#include "base/containers/stack_container.h"
 | 
| -#include "base/location.h"
 | 
| -#include "base/macros.h"
 | 
| -#include "base/memory/linked_ptr.h"
 | 
|  #include "base/strings/stringprintf.h"
 | 
|  #include "base/trace_event/memory_dump_provider.h"
 | 
| -#include "base/trace_event/trace_event.h"
 | 
|  #include "gpu/GLES2/gl2extchromium.h"
 | 
| -#include "gpu/command_buffer/client/gles2_interface.h"
 | 
|  #include "media/base/bind_to_current_loop.h"
 | 
|  #include "media/renderers/gpu_video_accelerator_factories.h"
 | 
| -#include "third_party/libyuv/include/libyuv.h"
 | 
|  #include "ui/gfx/buffer_format_util.h"
 | 
| -#include "ui/gl/trace_util.h"
 | 
|  
 | 
|  namespace media {
 | 
|  
 | 
| -// Implementation of a pool of GpuMemoryBuffers used to back VideoFrames.
 | 
| -class GpuMemoryBufferVideoFramePool::PoolImpl
 | 
| -    : public base::RefCountedThreadSafe<
 | 
| -          GpuMemoryBufferVideoFramePool::PoolImpl>,
 | 
| -      public base::trace_event::MemoryDumpProvider {
 | 
| - public:
 | 
| -  // |media_task_runner| is the media task runner associated with the
 | 
| -  // GL context provided by |gpu_factories|
 | 
| -  // |worker_task_runner| is a task runner used to asynchronously copy
 | 
| -  // video frame's planes.
 | 
| -  // |gpu_factories| is an interface to GPU related operation and can be
 | 
| -  // null if a GL context is not available.
 | 
| -  PoolImpl(const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
 | 
| -           const scoped_refptr<base::TaskRunner>& worker_task_runner,
 | 
| -           GpuVideoAcceleratorFactories* gpu_factories)
 | 
| -      : media_task_runner_(media_task_runner),
 | 
| -        worker_task_runner_(worker_task_runner),
 | 
| -        gpu_factories_(gpu_factories),
 | 
| -        output_format_(PIXEL_FORMAT_UNKNOWN) {
 | 
| -    DCHECK(media_task_runner_);
 | 
| -    DCHECK(worker_task_runner_);
 | 
| -  }
 | 
| -
 | 
| -  // Takes a software VideoFrame and calls |frame_ready_cb| with a VideoFrame
 | 
| -  // backed by native textures if possible.
 | 
| -  // The data contained in video_frame is copied into the returned frame
 | 
| -  // asynchronously posting tasks to |worker_task_runner_|, while
 | 
| -  // |frame_ready_cb| will be called on |media_task_runner_| once all the data
 | 
| -  // has been copied.
 | 
| -  void CreateHardwareFrame(const scoped_refptr<VideoFrame>& video_frame,
 | 
| -                           const FrameReadyCB& cb);
 | 
| -
 | 
| -  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
 | 
| -                    base::trace_event::ProcessMemoryDump* pmd) override;
 | 
| -
 | 
| - private:
 | 
| -  friend class base::RefCountedThreadSafe<
 | 
| -      GpuMemoryBufferVideoFramePool::PoolImpl>;
 | 
| -  ~PoolImpl() override;
 | 
| -
 | 
| -  // Resource to represent a plane.
 | 
| -  struct PlaneResource {
 | 
| -    gfx::Size size;
 | 
| -    scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
 | 
| -    unsigned texture_id = 0u;
 | 
| -    unsigned image_id = 0u;
 | 
| -    gpu::Mailbox mailbox;
 | 
| -  };
 | 
| -
 | 
| -  // All the resources needed to compose a frame.
 | 
| -  struct FrameResources {
 | 
| -    explicit FrameResources(const gfx::Size& size) : size(size) {}
 | 
| -    void SetIsInUse(bool in_use) { in_use_ = in_use; }
 | 
| -    bool IsInUse() const {
 | 
| -      if (in_use_)
 | 
| -        return true;
 | 
| -      for (const PlaneResource& plane_resource : plane_resources) {
 | 
| -        if (plane_resource.gpu_memory_buffer &&
 | 
| -            plane_resource.gpu_memory_buffer->IsInUseByMacOSWindowServer()) {
 | 
| -          return true;
 | 
| -        }
 | 
| -      }
 | 
| -      return false;
 | 
| -    }
 | 
| -
 | 
| -    const gfx::Size size;
 | 
| -    PlaneResource plane_resources[VideoFrame::kMaxPlanes];
 | 
| -
 | 
| -   private:
 | 
| -    bool in_use_ = true;
 | 
| -  };
 | 
| -
 | 
| -  // Copy |video_frame| data into |frame_resouces|
 | 
| -  // and calls |done| when done.
 | 
| -  void CopyVideoFrameToGpuMemoryBuffers(
 | 
| -      const scoped_refptr<VideoFrame>& video_frame,
 | 
| -      FrameResources* frame_resources,
 | 
| -      const FrameReadyCB& frame_ready_cb);
 | 
| -
 | 
| -  // Called when all the data has been copied.
 | 
| -  void OnCopiesDone(const scoped_refptr<VideoFrame>& video_frame,
 | 
| -                    FrameResources* frame_resources,
 | 
| -                    const FrameReadyCB& frame_ready_cb);
 | 
| -
 | 
| -  // Prepares GL resources, mailboxes and calls |frame_ready_cb| with the new
 | 
| -  // VideoFrame.
 | 
| -  // This has to be run on |media_task_runner_| where |frame_ready_cb| will also
 | 
| -  // be run.
 | 
| -  void BindAndCreateMailboxesHardwareFrameResources(
 | 
| -      const scoped_refptr<VideoFrame>& video_frame,
 | 
| -      FrameResources* frame_resources,
 | 
| -      const FrameReadyCB& frame_ready_cb);
 | 
| -
 | 
| -  // Return true if |resources| can be used to represent a frame for
 | 
| -  // specific |format| and |size|.
 | 
| -  static bool AreFrameResourcesCompatible(const FrameResources* resources,
 | 
| -                                          const gfx::Size& size) {
 | 
| -    return size == resources->size;
 | 
| -  }
 | 
| -
 | 
| -  // Get the resources needed for a frame out of the pool, or create them if
 | 
| -  // necessary.
 | 
| -  // This also drops the LRU resources that can't be reuse for this frame.
 | 
| -  FrameResources* GetOrCreateFrameResources(const gfx::Size& size,
 | 
| -                                            VideoPixelFormat format);
 | 
| -
 | 
| -  // Callback called when a VideoFrame generated with GetFrameResources is no
 | 
| -  // longer referenced.
 | 
| -  // This must be called on the thread where |media_task_runner_| is current.
 | 
| -  void MailboxHoldersReleased(FrameResources* frame_resources,
 | 
| -                              const gpu::SyncToken& sync_token);
 | 
| -
 | 
| -  // Delete resources. This has to be called on the thread where |task_runner|
 | 
| -  // is current.
 | 
| -  static void DeleteFrameResources(GpuVideoAcceleratorFactories* gpu_factories,
 | 
| -                                   FrameResources* frame_resources);
 | 
| -
 | 
| -  // Task runner associated to the GL context provided by |gpu_factories_|.
 | 
| -  scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
 | 
| -  // Task runner used to asynchronously copy planes.
 | 
| -  scoped_refptr<base::TaskRunner> worker_task_runner_;
 | 
| -
 | 
| -  // Interface to GPU related operations.
 | 
| -  GpuVideoAcceleratorFactories* gpu_factories_;
 | 
| -
 | 
| -  // Pool of resources.
 | 
| -  std::list<FrameResources*> resources_pool_;
 | 
| -
 | 
| -  // TODO(dcastagna): change the following type from VideoPixelFormat to
 | 
| -  // BufferFormat.
 | 
| -  VideoPixelFormat output_format_;
 | 
| -
 | 
| -  DISALLOW_COPY_AND_ASSIGN(PoolImpl);
 | 
| -};
 | 
| -
 | 
|  namespace {
 | 
|  
 | 
| -// VideoFrame copies to GpuMemoryBuffers will be split in copies where the
 | 
| -// output size is |kBytesPerCopyTarget| bytes and run in parallel.
 | 
| -const size_t kBytesPerCopyTarget = 1024 * 1024;  // 1MB
 | 
| -
 | 
|  // Return the GpuMemoryBuffer format to use for a specific VideoPixelFormat
 | 
|  // and plane.
 | 
|  gfx::BufferFormat GpuMemoryBufferFormat(VideoPixelFormat format, size_t plane) {
 | 
|    switch (format) {
 | 
|      case PIXEL_FORMAT_I420:
 | 
| +    case PIXEL_FORMAT_YV12:
 | 
|        DCHECK_LE(plane, 2u);
 | 
|        return gfx::BufferFormat::R_8;
 | 
|      case PIXEL_FORMAT_NV12:
 | 
| @@ -195,9 +42,11 @@ gfx::BufferFormat GpuMemoryBufferFormat(VideoPixelFormat format, size_t plane) {
 | 
|  unsigned ImageInternalFormat(VideoPixelFormat format, size_t plane) {
 | 
|    switch (format) {
 | 
|      case PIXEL_FORMAT_I420:
 | 
| +    case PIXEL_FORMAT_YV12:
 | 
|        DCHECK_LE(plane, 2u);
 | 
|        return GL_RED_EXT;
 | 
|      case PIXEL_FORMAT_NV12:
 | 
| +      // TODO(dshwang): support ChromeOS using GL_RG_EXT. crbug.com/356871
 | 
|        DCHECK_LE(plane, 1u);
 | 
|        return GL_RGB_YCBCR_420V_CHROMIUM;
 | 
|      case PIXEL_FORMAT_UYVY:
 | 
| @@ -209,568 +58,564 @@ unsigned ImageInternalFormat(VideoPixelFormat format, size_t plane) {
 | 
|    }
 | 
|  }
 | 
|  
 | 
| -// The number of output planes to be copied in each iteration.
 | 
| -size_t PlanesPerCopy(VideoPixelFormat format) {
 | 
| +bool IsValidPlane(size_t plane, VideoPixelFormat format) {
 | 
| +  DCHECK_LE(VideoFrame::NumPlanes(format),
 | 
| +            static_cast<size_t>(VideoFrame::kMaxPlanes));
 | 
| +  return (plane < VideoFrame::NumPlanes(format));
 | 
| +}
 | 
| +
 | 
| +bool IsPowerOfTwo(size_t x) {
 | 
| +  return x != 0 && (x & (x - 1)) == 0;
 | 
| +}
 | 
| +
 | 
| +inline size_t RoundUp(size_t value, size_t alignment) {
 | 
| +  DCHECK(IsPowerOfTwo(alignment));
 | 
| +  return ((value + (alignment - 1)) & ~(alignment - 1));
 | 
| +}
 | 
| +
 | 
| +gfx::Size CodedSize(const gfx::Size& size, VideoPixelFormat format) {
 | 
|    switch (format) {
 | 
|      case PIXEL_FORMAT_I420:
 | 
| -    case PIXEL_FORMAT_UYVY:
 | 
| -      return 1;
 | 
| +    case PIXEL_FORMAT_YV12:
 | 
|      case PIXEL_FORMAT_NV12:
 | 
| -      return 2;
 | 
| +      return gfx::Size(RoundUp(size.width(), 2), RoundUp(size.height(), 2));
 | 
| +    case PIXEL_FORMAT_UYVY:
 | 
| +      return gfx::Size(RoundUp(size.width(), 2), size.height());
 | 
|      default:
 | 
|        NOTREACHED();
 | 
| -      return 0;
 | 
|    }
 | 
| +  return gfx::Size();
 | 
|  }
 | 
|  
 | 
| -// The number of output rows to be copied in each iteration.
 | 
| -int RowsPerCopy(size_t plane, VideoPixelFormat format, int width) {
 | 
| -  int bytes_per_row = VideoFrame::RowBytes(plane, format, width);
 | 
| -  if (format == PIXEL_FORMAT_NV12) {
 | 
| -    DCHECK_EQ(0u, plane);
 | 
| -    bytes_per_row += VideoFrame::RowBytes(1, format, width);
 | 
| +// All the resources needed to compose a frame.
 | 
| +class FrameResources : public base::RefCountedThreadSafe<FrameResources> {
 | 
| + public:
 | 
| +  static scoped_refptr<FrameResources> Create(
 | 
| +      GpuVideoAcceleratorFactories* gpu_factories,
 | 
| +      VideoPixelFormat format,
 | 
| +      const gfx::Size& size) {
 | 
| +    // Create the resources.
 | 
| +    scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock(
 | 
| +        gpu_factories->GetGLContextLock());
 | 
| +    if (!lock)
 | 
| +      return nullptr;
 | 
| +
 | 
| +    scoped_refptr<FrameResources> frame_resources(
 | 
| +        new FrameResources(gpu_factories, format, size));
 | 
| +
 | 
| +    gpu::gles2::GLES2Interface* gles2 = lock->ContextGL();
 | 
| +    gles2->ActiveTexture(GL_TEXTURE0);
 | 
| +    size_t num_planes = VideoFrame::NumPlanes(format);
 | 
| +    for (size_t i = 0; i < num_planes;
 | 
| +         i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, i)) {
 | 
| +      PlaneResource& plane_resource = frame_resources->plane_resources_[i];
 | 
| +      const size_t width = VideoFrame::Columns(i, format, size.width());
 | 
| +      const size_t height = VideoFrame::Rows(i, format, size.height());
 | 
| +      plane_resource.size = gfx::Size(width, height);
 | 
| +
 | 
| +      const gfx::BufferFormat buffer_format = GpuMemoryBufferFormat(format, i);
 | 
| +      plane_resource.gpu_memory_buffer = gpu_factories->AllocateGpuMemoryBuffer(
 | 
| +          plane_resource.size, buffer_format,
 | 
| +          gfx::BufferUsage::GPU_READ_CPU_READ_WRITE);
 | 
| +      if (!plane_resource.gpu_memory_buffer) {
 | 
| +        DLOG(ERROR) << "Could not create GpuMemoryBuffer.";
 | 
| +        return nullptr;
 | 
| +      }
 | 
| +      unsigned texture_target =
 | 
| +          gpu_factories->ImageTextureTarget(buffer_format);
 | 
| +      gles2->GenTextures(1, &plane_resource.texture_id);
 | 
| +      gles2->BindTexture(texture_target, plane_resource.texture_id);
 | 
| +      gles2->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 | 
| +      gles2->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 | 
| +      gles2->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 | 
| +      gles2->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 | 
| +      gles2->GenMailboxCHROMIUM(plane_resource.mailbox.name);
 | 
| +      gles2->ProduceTextureCHROMIUM(texture_target,
 | 
| +                                    plane_resource.mailbox.name);
 | 
| +    }
 | 
| +    return frame_resources;
 | 
|    }
 | 
| -  // Copy an even number of lines, and at least one.
 | 
| -  return std::max<size_t>((kBytesPerCopyTarget / bytes_per_row) & ~1, 1);
 | 
| -}
 | 
|  
 | 
| -void CopyRowsToI420Buffer(int first_row,
 | 
| -                          int rows,
 | 
| -                          int bytes_per_row,
 | 
| -                          const uint8_t* source,
 | 
| -                          int source_stride,
 | 
| -                          uint8_t* output,
 | 
| -                          int dest_stride,
 | 
| -                          const base::Closure& done) {
 | 
| -  TRACE_EVENT2("media", "CopyRowsToI420Buffer", "bytes_per_row", bytes_per_row,
 | 
| -               "rows", rows);
 | 
| -  if (output) {
 | 
| -    DCHECK_NE(dest_stride, 0);
 | 
| -    DCHECK_LE(bytes_per_row, std::abs(dest_stride));
 | 
| -    DCHECK_LE(bytes_per_row, source_stride);
 | 
| -
 | 
| -    libyuv::CopyPlane(source + source_stride * first_row, source_stride,
 | 
| -                      output + dest_stride * first_row, dest_stride,
 | 
| -                      bytes_per_row, rows);
 | 
| -  }
 | 
| -  done.Run();
 | 
| -}
 | 
| +  VideoPixelFormat format() const { return format_; }
 | 
| +  const gfx::Size& size() const { return size_; }
 | 
|  
 | 
| -void CopyRowsToNV12Buffer(int first_row,
 | 
| -                          int rows,
 | 
| -                          int bytes_per_row,
 | 
| -                          const scoped_refptr<VideoFrame>& source_frame,
 | 
| -                          uint8_t* dest_y,
 | 
| -                          int dest_stride_y,
 | 
| -                          uint8_t* dest_uv,
 | 
| -                          int dest_stride_uv,
 | 
| -                          const base::Closure& done) {
 | 
| -  TRACE_EVENT2("media", "CopyRowsToNV12Buffer", "bytes_per_row", bytes_per_row,
 | 
| -               "rows", rows);
 | 
| -  if (dest_y && dest_uv) {
 | 
| -    DCHECK_NE(dest_stride_y, 0);
 | 
| -    DCHECK_NE(dest_stride_uv, 0);
 | 
| -    DCHECK_LE(bytes_per_row, std::abs(dest_stride_y));
 | 
| -    DCHECK_LE(bytes_per_row, std::abs(dest_stride_uv));
 | 
| -    DCHECK_EQ(0, first_row % 2);
 | 
| -
 | 
| -    libyuv::I420ToNV12(
 | 
| -        source_frame->visible_data(VideoFrame::kYPlane) +
 | 
| -            first_row * source_frame->stride(VideoFrame::kYPlane),
 | 
| -        source_frame->stride(VideoFrame::kYPlane),
 | 
| -        source_frame->visible_data(VideoFrame::kUPlane) +
 | 
| -            first_row / 2 * source_frame->stride(VideoFrame::kUPlane),
 | 
| -        source_frame->stride(VideoFrame::kUPlane),
 | 
| -        source_frame->visible_data(VideoFrame::kVPlane) +
 | 
| -            first_row / 2 * source_frame->stride(VideoFrame::kVPlane),
 | 
| -        source_frame->stride(VideoFrame::kVPlane),
 | 
| -        dest_y + first_row * dest_stride_y, dest_stride_y,
 | 
| -        dest_uv + first_row / 2 * dest_stride_uv, dest_stride_uv, bytes_per_row,
 | 
| -        rows);
 | 
| +  // Resource to represent a plane.
 | 
| +  struct PlaneResource {
 | 
| +    gfx::Size size;
 | 
| +    std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
 | 
| +    unsigned texture_id = 0u;
 | 
| +    unsigned image_id = 0u;
 | 
| +    gpu::Mailbox mailbox;
 | 
| +  };
 | 
| +  PlaneResource& GetPlaneResource(size_t plane) {
 | 
| +    DCHECK(IsValidPlane(plane, format_));
 | 
| +    return plane_resources_[plane];
 | 
|    }
 | 
| -  done.Run();
 | 
| -}
 | 
|  
 | 
| -void CopyRowsToUYVYBuffer(int first_row,
 | 
| -                          int rows,
 | 
| -                          int width,
 | 
| -                          const scoped_refptr<VideoFrame>& source_frame,
 | 
| -                          uint8_t* output,
 | 
| -                          int dest_stride,
 | 
| -                          const base::Closure& done) {
 | 
| -  TRACE_EVENT2("media", "CopyRowsToUYVYBuffer", "bytes_per_row", width * 2,
 | 
| -               "rows", rows);
 | 
| -  if (output) {
 | 
| -    DCHECK_NE(dest_stride, 0);
 | 
| -    DCHECK_LE(width, std::abs(dest_stride / 2));
 | 
| -    DCHECK_EQ(0, first_row % 2);
 | 
| -    libyuv::I420ToUYVY(
 | 
| -        source_frame->visible_data(VideoFrame::kYPlane) +
 | 
| -            first_row * source_frame->stride(VideoFrame::kYPlane),
 | 
| -        source_frame->stride(VideoFrame::kYPlane),
 | 
| -        source_frame->visible_data(VideoFrame::kUPlane) +
 | 
| -            first_row / 2 * source_frame->stride(VideoFrame::kUPlane),
 | 
| -        source_frame->stride(VideoFrame::kUPlane),
 | 
| -        source_frame->visible_data(VideoFrame::kVPlane) +
 | 
| -            first_row / 2 * source_frame->stride(VideoFrame::kVPlane),
 | 
| -        source_frame->stride(VideoFrame::kVPlane),
 | 
| -        output + first_row * dest_stride, dest_stride, width, rows);
 | 
| +  void SetIsInUse(bool in_use) { in_use_ = in_use; }
 | 
| +  bool IsInUse() const {
 | 
| +    if (in_use_)
 | 
| +      return true;
 | 
| +    for (const PlaneResource& plane_resource : plane_resources_) {
 | 
| +      if (plane_resource.gpu_memory_buffer->IsInUseByMacOSWindowServer()) {
 | 
| +        return true;
 | 
| +      }
 | 
| +    }
 | 
| +    return false;
 | 
|    }
 | 
| -  done.Run();
 | 
| -}
 | 
|  
 | 
| -gfx::Size CodedSize(const scoped_refptr<VideoFrame>& video_frame,
 | 
| -                    VideoPixelFormat output_format) {
 | 
| -  DCHECK(gfx::Rect(video_frame->coded_size())
 | 
| -             .Contains(video_frame->visible_rect()));
 | 
| -  DCHECK((video_frame->visible_rect().x() & 1) == 0);
 | 
| -  gfx::Size output;
 | 
| -  switch (output_format) {
 | 
| -    case PIXEL_FORMAT_I420:
 | 
| -    case PIXEL_FORMAT_NV12:
 | 
| -      DCHECK((video_frame->visible_rect().y() & 1) == 0);
 | 
| -      output = gfx::Size((video_frame->visible_rect().width() + 1) & ~1,
 | 
| -                         (video_frame->visible_rect().height() + 1) & ~1);
 | 
| -      break;
 | 
| -    case PIXEL_FORMAT_UYVY:
 | 
| -      output = gfx::Size((video_frame->visible_rect().width() + 1) & ~1,
 | 
| -                         video_frame->visible_rect().height());
 | 
| -      break;
 | 
| -    default:
 | 
| -      NOTREACHED();
 | 
| +  bool IsCompatible(VideoPixelFormat format, const gfx::Size& size) {
 | 
| +    return format == format_ && size == size_;
 | 
|    }
 | 
| -  DCHECK(gfx::Rect(video_frame->coded_size()).Contains(gfx::Rect(output)));
 | 
| -  return output;
 | 
| -}
 | 
| -}  // unnamed namespace
 | 
|  
 | 
| -// Creates a VideoFrame backed by native textures starting from a software
 | 
| -// VideoFrame.
 | 
| -// The data contained in |video_frame| is copied into the VideoFrame passed to
 | 
| -// |frame_ready_cb|.
 | 
| -// This has to be called on the thread where |media_task_runner_| is current.
 | 
| -void GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame(
 | 
| -    const scoped_refptr<VideoFrame>& video_frame,
 | 
| -    const FrameReadyCB& frame_ready_cb) {
 | 
| -  DCHECK(media_task_runner_->BelongsToCurrentThread());
 | 
| -  // Lazily initialize output_format_ since VideoFrameOutputFormat() has to be
 | 
| -  // called on the media_thread while this object might be instantiated on any.
 | 
| -  if (output_format_ == PIXEL_FORMAT_UNKNOWN)
 | 
| -    output_format_ = gpu_factories_->VideoFrameOutputFormat();
 | 
| -
 | 
| -  if (output_format_ == PIXEL_FORMAT_UNKNOWN) {
 | 
| -    frame_ready_cb.Run(video_frame);
 | 
| -    return;
 | 
| +  void OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
 | 
| +                    base::trace_event::ProcessMemoryDump* pmd) {
 | 
| +    const uint64_t tracing_process_id =
 | 
| +        base::trace_event::MemoryDumpManager::GetInstance()
 | 
| +            ->GetTracingProcessId();
 | 
| +    const int kImportance = 2;
 | 
| +    for (const PlaneResource& plane_resource : plane_resources_) {
 | 
| +      gfx::GpuMemoryBufferId buffer_id =
 | 
| +          plane_resource.gpu_memory_buffer->GetId();
 | 
| +      std::string dump_name = base::StringPrintf(
 | 
| +          "media/video_frame_memory/buffer_%d", buffer_id.id);
 | 
| +      base::trace_event::MemoryAllocatorDump* dump =
 | 
| +          pmd->CreateAllocatorDump(dump_name);
 | 
| +      size_t buffer_size_in_bytes = gfx::BufferSizeForBufferFormat(
 | 
| +          plane_resource.size, plane_resource.gpu_memory_buffer->GetFormat());
 | 
| +      dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
 | 
| +                      base::trace_event::MemoryAllocatorDump::kUnitsBytes,
 | 
| +                      buffer_size_in_bytes);
 | 
| +      dump->AddScalar("free_size",
 | 
| +                      base::trace_event::MemoryAllocatorDump::kUnitsBytes,
 | 
| +                      IsInUse() ? 0 : buffer_size_in_bytes);
 | 
| +      base::trace_event::MemoryAllocatorDumpGuid shared_buffer_guid =
 | 
| +          gfx::GetGpuMemoryBufferGUIDForTracing(tracing_process_id, buffer_id);
 | 
| +      pmd->CreateSharedGlobalAllocatorDump(shared_buffer_guid);
 | 
| +      pmd->AddOwnershipEdge(dump->guid(), shared_buffer_guid, kImportance);
 | 
| +    }
 | 
|    }
 | 
| -  switch (video_frame->format()) {
 | 
| -    // Supported cases.
 | 
| -    case PIXEL_FORMAT_YV12:
 | 
| -    case PIXEL_FORMAT_I420:
 | 
| -      break;
 | 
| -    // Unsupported cases.
 | 
| -    case PIXEL_FORMAT_YV12A:
 | 
| -    case PIXEL_FORMAT_YV16:
 | 
| -    case PIXEL_FORMAT_YV24:
 | 
| -    case PIXEL_FORMAT_NV12:
 | 
| -    case PIXEL_FORMAT_NV21:
 | 
| -    case PIXEL_FORMAT_UYVY:
 | 
| -    case PIXEL_FORMAT_YUY2:
 | 
| -    case PIXEL_FORMAT_ARGB:
 | 
| -    case PIXEL_FORMAT_XRGB:
 | 
| -    case PIXEL_FORMAT_RGB24:
 | 
| -    case PIXEL_FORMAT_RGB32:
 | 
| -    case PIXEL_FORMAT_MJPEG:
 | 
| -    case PIXEL_FORMAT_MT21:
 | 
| -    case PIXEL_FORMAT_YUV420P9:
 | 
| -    case PIXEL_FORMAT_YUV422P9:
 | 
| -    case PIXEL_FORMAT_YUV444P9:
 | 
| -    case PIXEL_FORMAT_YUV420P10:
 | 
| -    case PIXEL_FORMAT_YUV422P10:
 | 
| -    case PIXEL_FORMAT_YUV444P10:
 | 
| -    case PIXEL_FORMAT_UNKNOWN:
 | 
| -      frame_ready_cb.Run(video_frame);
 | 
| -      return;
 | 
| +
 | 
| + private:
 | 
| +  friend class base::RefCountedThreadSafe<FrameResources>;
 | 
| +  FrameResources(GpuVideoAcceleratorFactories* gpu_factories,
 | 
| +                 VideoPixelFormat format,
 | 
| +                 const gfx::Size& size)
 | 
| +      : gpu_factories_(gpu_factories),
 | 
| +        format_(format),
 | 
| +        size_(size),
 | 
| +        plane_resources_(NumPlanResources(format)) {}
 | 
| +
 | 
| +  ~FrameResources() {
 | 
| +    scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock(
 | 
| +        gpu_factories_->GetGLContextLock());
 | 
| +    DCHECK(lock);
 | 
| +    gpu::gles2::GLES2Interface* gles2 = lock->ContextGL();
 | 
| +
 | 
| +    for (PlaneResource& plane_resource : plane_resources_) {
 | 
| +      if (plane_resource.image_id)
 | 
| +        gles2->DestroyImageCHROMIUM(plane_resource.image_id);
 | 
| +      if (plane_resource.texture_id)
 | 
| +        gles2->DeleteTextures(1, &plane_resource.texture_id);
 | 
| +    }
 | 
|    }
 | 
|  
 | 
| -  const gfx::Size coded_size = CodedSize(video_frame, output_format_);
 | 
| -  // Acquire resources. Incompatible ones will be dropped from the pool.
 | 
| -  FrameResources* frame_resources =
 | 
| -      GetOrCreateFrameResources(coded_size, output_format_);
 | 
| -  if (!frame_resources) {
 | 
| -    frame_ready_cb.Run(video_frame);
 | 
| -    return;
 | 
| +  static int NumPlanResources(VideoPixelFormat format) {
 | 
| +    return VideoFrame::NumPlanes(format) /
 | 
| +           GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, 0);
 | 
|    }
 | 
|  
 | 
| -  worker_task_runner_->PostTask(
 | 
| -      FROM_HERE, base::Bind(&PoolImpl::CopyVideoFrameToGpuMemoryBuffers, this,
 | 
| -                            video_frame, frame_resources, frame_ready_cb));
 | 
| -}
 | 
| +  GpuVideoAcceleratorFactories* const gpu_factories_;
 | 
| +  VideoPixelFormat format_;
 | 
| +  const gfx::Size size_;
 | 
| +
 | 
| +  std::vector<PlaneResource> plane_resources_;
 | 
| +  bool in_use_ = true;
 | 
| +};
 | 
|  
 | 
| -bool GpuMemoryBufferVideoFramePool::PoolImpl::OnMemoryDump(
 | 
| -    const base::trace_event::MemoryDumpArgs& args,
 | 
| -    base::trace_event::ProcessMemoryDump* pmd) {
 | 
| -  const uint64_t tracing_process_id =
 | 
| -      base::trace_event::MemoryDumpManager::GetInstance()
 | 
| -          ->GetTracingProcessId();
 | 
| -  const int kImportance = 2;
 | 
| -  for (const FrameResources* frame_resources : resources_pool_) {
 | 
| -    for (const PlaneResource& plane_resource :
 | 
| -         frame_resources->plane_resources) {
 | 
| -      if (plane_resource.gpu_memory_buffer) {
 | 
| -        gfx::GpuMemoryBufferId buffer_id =
 | 
| -            plane_resource.gpu_memory_buffer->GetId();
 | 
| -        std::string dump_name = base::StringPrintf(
 | 
| -            "media/video_frame_memory/buffer_%d", buffer_id.id);
 | 
| -        base::trace_event::MemoryAllocatorDump* dump =
 | 
| -            pmd->CreateAllocatorDump(dump_name);
 | 
| -        size_t buffer_size_in_bytes = gfx::BufferSizeForBufferFormat(
 | 
| -            plane_resource.size, plane_resource.gpu_memory_buffer->GetFormat());
 | 
| -        dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
 | 
| -                        base::trace_event::MemoryAllocatorDump::kUnitsBytes,
 | 
| -                        buffer_size_in_bytes);
 | 
| -        dump->AddScalar("free_size",
 | 
| -                        base::trace_event::MemoryAllocatorDump::kUnitsBytes,
 | 
| -                        frame_resources->IsInUse() ? 0 : buffer_size_in_bytes);
 | 
| -        base::trace_event::MemoryAllocatorDumpGuid shared_buffer_guid =
 | 
| -            gfx::GetGpuMemoryBufferGUIDForTracing(tracing_process_id,
 | 
| -                                                  buffer_id);
 | 
| -        pmd->CreateSharedGlobalAllocatorDump(shared_buffer_guid);
 | 
| -        pmd->AddOwnershipEdge(dump->guid(), shared_buffer_guid, kImportance);
 | 
| +class GpuMemoryBufferVideoFrameFuture : public VideoFrameFuture {
 | 
| + public:
 | 
| +  typedef base::Callback<void(const scoped_refptr<FrameResources>&,
 | 
| +                              const gpu::SyncToken&)>
 | 
| +      ReturnFrameResourcesCB;
 | 
| +
 | 
| +  static std::unique_ptr<GpuMemoryBufferVideoFrameFuture> Create(
 | 
| +      GpuVideoAcceleratorFactories* gpu_factories,
 | 
| +      const scoped_refptr<FrameResources>& frame_resources,
 | 
| +      const gfx::Rect& visible_rect,
 | 
| +      const gfx::Size& natural_size,
 | 
| +      base::TimeDelta timestamp,
 | 
| +      const ReturnFrameResourcesCB& frame_resources_return_cb) {
 | 
| +    std::unique_ptr<GpuMemoryBufferVideoFrameFuture> future(
 | 
| +        new GpuMemoryBufferVideoFrameFuture(
 | 
| +            gpu_factories, frame_resources, visible_rect, natural_size,
 | 
| +            timestamp, frame_resources_return_cb));
 | 
| +
 | 
| +    const size_t num_planes = VideoFrame::NumPlanes(frame_resources->format());
 | 
| +    for (size_t i = 0; i < num_planes;
 | 
| +         i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(
 | 
| +             frame_resources->format(), i)) {
 | 
| +      FrameResources::PlaneResource& plane_resource =
 | 
| +          frame_resources->GetPlaneResource(i);
 | 
| +      if (!plane_resource.gpu_memory_buffer->Map()) {
 | 
| +        // Prevent the destructor from unmapping the |gpu_memory_buffer|.
 | 
| +        future->released_video_frame_ = VideoFrame::CreateEOSFrame();
 | 
| +        DLOG(ERROR) << "Could not Map() buffer";
 | 
| +        return nullptr;
 | 
|        }
 | 
|      }
 | 
| -  }
 | 
| -  return true;
 | 
| -}
 | 
|  
 | 
| -void GpuMemoryBufferVideoFramePool::PoolImpl::OnCopiesDone(
 | 
| -    const scoped_refptr<VideoFrame>& video_frame,
 | 
| -    FrameResources* frame_resources,
 | 
| -    const FrameReadyCB& frame_ready_cb) {
 | 
| -  for (const auto& plane_resource : frame_resources->plane_resources) {
 | 
| -    if (plane_resource.gpu_memory_buffer)
 | 
| -      plane_resource.gpu_memory_buffer->Unmap();
 | 
| +    return future;
 | 
|    }
 | 
|  
 | 
| -  media_task_runner_->PostTask(
 | 
| -      FROM_HERE,
 | 
| -      base::Bind(&PoolImpl::BindAndCreateMailboxesHardwareFrameResources, this,
 | 
| -                 video_frame, frame_resources, frame_ready_cb));
 | 
| -}
 | 
| -
 | 
| -// Copies |video_frame| into |frame_resources| asynchronously, posting n tasks
 | 
| -// that will be synchronized by a barrier.
 | 
| -// After the barrier is passed OnCopiesDone will be called.
 | 
| -void GpuMemoryBufferVideoFramePool::PoolImpl::CopyVideoFrameToGpuMemoryBuffers(
 | 
| -    const scoped_refptr<VideoFrame>& video_frame,
 | 
| -    FrameResources* frame_resources,
 | 
| -    const FrameReadyCB& frame_ready_cb) {
 | 
| -  // Compute the number of tasks to post and create the barrier.
 | 
| -  const size_t num_planes = VideoFrame::NumPlanes(output_format_);
 | 
| -  const size_t planes_per_copy = PlanesPerCopy(output_format_);
 | 
| -  const gfx::Size coded_size = CodedSize(video_frame, output_format_);
 | 
| -  size_t copies = 0;
 | 
| -  for (size_t i = 0; i < num_planes; i += planes_per_copy) {
 | 
| -    const int rows = VideoFrame::Rows(i, output_format_, coded_size.height());
 | 
| -    const int rows_per_copy =
 | 
| -        RowsPerCopy(i, output_format_, coded_size.width());
 | 
| -    copies += rows / rows_per_copy;
 | 
| -    if (rows % rows_per_copy)
 | 
| -      ++copies;
 | 
| -  }
 | 
| -  const base::Closure copies_done =
 | 
| -      base::Bind(&PoolImpl::OnCopiesDone, this, video_frame, frame_resources,
 | 
| -                 frame_ready_cb);
 | 
| -  const base::Closure barrier = base::BarrierClosure(copies, copies_done);
 | 
| -
 | 
| -  // Post all the async tasks.
 | 
| -  for (size_t i = 0; i < num_planes; i += planes_per_copy) {
 | 
| -    gfx::GpuMemoryBuffer* buffer =
 | 
| -        frame_resources->plane_resources[i].gpu_memory_buffer.get();
 | 
| -
 | 
| -    if (!buffer || !buffer->Map()) {
 | 
| -      DLOG(ERROR) << "Could not get or Map() buffer";
 | 
| +  ~GpuMemoryBufferVideoFrameFuture() override {
 | 
| +    if (released_video_frame_)
 | 
|        return;
 | 
| +
 | 
| +    // Decoder can be destructed before completing to decode this frame.
 | 
| +    VideoPixelFormat format = frame_resources_->format();
 | 
| +    const size_t num_planes = VideoFrame::NumPlanes(format);
 | 
| +    for (size_t i = 0; i < num_planes;
 | 
| +         i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, i)) {
 | 
| +      FrameResources::PlaneResource& plane_resource =
 | 
| +          frame_resources_->GetPlaneResource(i);
 | 
| +      plane_resource.gpu_memory_buffer->Unmap();
 | 
|      }
 | 
| -    DCHECK_EQ(planes_per_copy,
 | 
| -              gfx::NumberOfPlanesForBufferFormat(buffer->GetFormat()));
 | 
| -
 | 
| -    const int rows = VideoFrame::Rows(i, output_format_, coded_size.height());
 | 
| -    const int rows_per_copy =
 | 
| -        RowsPerCopy(i, output_format_, coded_size.width());
 | 
| -
 | 
| -    for (int row = 0; row < rows; row += rows_per_copy) {
 | 
| -      const int rows_to_copy = std::min(rows_per_copy, rows - row);
 | 
| -      switch (output_format_) {
 | 
| -        case PIXEL_FORMAT_I420: {
 | 
| -          const int bytes_per_row =
 | 
| -              VideoFrame::RowBytes(i, output_format_, coded_size.width());
 | 
| -          worker_task_runner_->PostTask(
 | 
| -              FROM_HERE, base::Bind(&CopyRowsToI420Buffer, row, rows_to_copy,
 | 
| -                                    bytes_per_row, video_frame->visible_data(i),
 | 
| -                                    video_frame->stride(i),
 | 
| -                                    static_cast<uint8_t*>(buffer->memory(0)),
 | 
| -                                    buffer->stride(0), barrier));
 | 
| -          break;
 | 
| -        }
 | 
| -        case PIXEL_FORMAT_NV12:
 | 
| -          worker_task_runner_->PostTask(
 | 
| -              FROM_HERE, base::Bind(&CopyRowsToNV12Buffer, row, rows_to_copy,
 | 
| -                                    coded_size.width(), video_frame,
 | 
| -                                    static_cast<uint8_t*>(buffer->memory(0)),
 | 
| -                                    buffer->stride(0),
 | 
| -                                    static_cast<uint8_t*>(buffer->memory(1)),
 | 
| -                                    buffer->stride(1), barrier));
 | 
| -          break;
 | 
| -        case PIXEL_FORMAT_UYVY:
 | 
| -          worker_task_runner_->PostTask(
 | 
| -              FROM_HERE, base::Bind(&CopyRowsToUYVYBuffer, row, rows_to_copy,
 | 
| -                                    coded_size.width(), video_frame,
 | 
| -                                    static_cast<uint8_t*>(buffer->memory(0)),
 | 
| -                                    buffer->stride(0), barrier));
 | 
| -          break;
 | 
| -        default:
 | 
| -          NOTREACHED();
 | 
| +  }
 | 
| +
 | 
| +  scoped_refptr<VideoFrame> Release() override {
 | 
| +    DCHECK(!released_video_frame_);
 | 
| +    scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock(
 | 
| +        gpu_factories_->GetGLContextLock());
 | 
| +    DCHECK(lock);
 | 
| +    gpu::gles2::GLES2Interface* gles2 = lock->ContextGL();
 | 
| +
 | 
| +    VideoPixelFormat format = frame_resources_->format();
 | 
| +    const size_t num_planes = VideoFrame::NumPlanes(format);
 | 
| +    const gfx::Size coded_size = frame_resources_->size();
 | 
| +    gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
 | 
| +    // Set up the planes creating the mailboxes needed to refer to the textures.
 | 
| +    for (size_t i = 0; i < num_planes;
 | 
| +         i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, i)) {
 | 
| +      FrameResources::PlaneResource& plane_resource =
 | 
| +          frame_resources_->GetPlaneResource(i);
 | 
| +      plane_resource.gpu_memory_buffer->Unmap();
 | 
| +      const gfx::BufferFormat buffer_format = GpuMemoryBufferFormat(format, i);
 | 
| +      unsigned texture_target =
 | 
| +          gpu_factories_->ImageTextureTarget(buffer_format);
 | 
| +      // Bind the texture and create or rebind the image.
 | 
| +      gles2->BindTexture(texture_target, plane_resource.texture_id);
 | 
| +
 | 
| +      if (plane_resource.gpu_memory_buffer && !plane_resource.image_id) {
 | 
| +        plane_resource.image_id = gles2->CreateImageCHROMIUM(
 | 
| +            plane_resource.gpu_memory_buffer->AsClientBuffer(),
 | 
| +            plane_resource.gpu_memory_buffer->GetSize().width(),
 | 
| +            plane_resource.gpu_memory_buffer->GetSize().height(),
 | 
| +            ImageInternalFormat(format, i));
 | 
| +      } else if (plane_resource.image_id) {
 | 
| +        gles2->ReleaseTexImage2DCHROMIUM(texture_target,
 | 
| +                                         plane_resource.image_id);
 | 
|        }
 | 
| +      if (plane_resource.image_id)
 | 
| +        gles2->BindTexImage2DCHROMIUM(texture_target, plane_resource.image_id);
 | 
| +      mailbox_holders[i] = gpu::MailboxHolder(plane_resource.mailbox,
 | 
| +                                              gpu::SyncToken(), texture_target);
 | 
| +    }
 | 
| +
 | 
| +    // Insert a sync_token, this is needed to make sure that the textures the
 | 
| +    // mailboxes refer to will be used only after all the previous commands
 | 
| +    // posted
 | 
| +    // in the command buffer have been processed.
 | 
| +    const GLuint64 fence_sync = gles2->InsertFenceSyncCHROMIUM();
 | 
| +    gles2->OrderingBarrierCHROMIUM();
 | 
| +
 | 
| +    gpu::SyncToken sync_token;
 | 
| +    gles2->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
 | 
| +    for (size_t i = 0; i < num_planes;
 | 
| +         i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, i))
 | 
| +      mailbox_holders[i].sync_token = sync_token;
 | 
| +
 | 
| +    auto release_video_frame_callback = BindToCurrentLoop(
 | 
| +        base::Bind(&GpuMemoryBufferVideoFrameFuture::VideoFrameReleased,
 | 
| +                   frame_resources_return_cb_, frame_resources_));
 | 
| +
 | 
| +    // Create the VideoFrame backed by native textures.
 | 
| +    switch (format) {
 | 
| +      case PIXEL_FORMAT_I420:
 | 
| +      case PIXEL_FORMAT_YV12:
 | 
| +        released_video_frame_ = VideoFrame::WrapYUV420NativeTextures(
 | 
| +            mailbox_holders[VideoFrame::kYPlane],
 | 
| +            mailbox_holders[VideoFrame::kUPlane],
 | 
| +            mailbox_holders[VideoFrame::kVPlane], release_video_frame_callback,
 | 
| +            coded_size, visible_rect_, natural_size_, timestamp_);
 | 
| +        break;
 | 
| +      case PIXEL_FORMAT_NV12:
 | 
| +      case PIXEL_FORMAT_UYVY:
 | 
| +        released_video_frame_ = VideoFrame::WrapNativeTexture(
 | 
| +            format, mailbox_holders[VideoFrame::kYPlane],
 | 
| +            release_video_frame_callback, coded_size, visible_rect_,
 | 
| +            natural_size_, timestamp_);
 | 
| +        released_video_frame_->metadata()->SetBoolean(
 | 
| +            VideoFrameMetadata::ALLOW_OVERLAY, true);
 | 
| +        break;
 | 
| +      default:
 | 
| +        NOTREACHED();
 | 
|      }
 | 
| +
 | 
| +    released_video_frame_->metadata()->SetBoolean(
 | 
| +        VideoFrameMetadata::READ_LOCK_FENCES_ENABLED, true);
 | 
| +
 | 
| +    frame_resources_ = nullptr;
 | 
| +    return released_video_frame_;
 | 
|    }
 | 
| -}
 | 
|  
 | 
| -void GpuMemoryBufferVideoFramePool::PoolImpl::
 | 
| -    BindAndCreateMailboxesHardwareFrameResources(
 | 
| -        const scoped_refptr<VideoFrame>& video_frame,
 | 
| -        FrameResources* frame_resources,
 | 
| -        const FrameReadyCB& frame_ready_cb) {
 | 
| -  scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock(
 | 
| -      gpu_factories_->GetGLContextLock());
 | 
| -  if (!lock) {
 | 
| -    frame_ready_cb.Run(video_frame);
 | 
| -    return;
 | 
| +  // static because GpuMemoryBufferVideoFrameFuture can be destroyed before
 | 
| +  // this callback.
 | 
| +  static void VideoFrameReleased(
 | 
| +      const ReturnFrameResourcesCB& frame_resources_return_cb,
 | 
| +      const scoped_refptr<FrameResources>& frame_resources,
 | 
| +      const gpu::SyncToken& release_sync_token) {
 | 
| +    frame_resources_return_cb.Run(frame_resources, release_sync_token);
 | 
|    }
 | 
| -  gpu::gles2::GLES2Interface* gles2 = lock->ContextGL();
 | 
| -
 | 
| -  const size_t num_planes = VideoFrame::NumPlanes(output_format_);
 | 
| -  const size_t planes_per_copy = PlanesPerCopy(output_format_);
 | 
| -  const gfx::Size coded_size = CodedSize(video_frame, output_format_);
 | 
| -  gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
 | 
| -  // Set up the planes creating the mailboxes needed to refer to the textures.
 | 
| -  for (size_t i = 0; i < num_planes; i += planes_per_copy) {
 | 
| -    PlaneResource& plane_resource = frame_resources->plane_resources[i];
 | 
| -    const gfx::BufferFormat buffer_format =
 | 
| -        GpuMemoryBufferFormat(output_format_, i);
 | 
| -    unsigned texture_target = gpu_factories_->ImageTextureTarget(buffer_format);
 | 
| -    // Bind the texture and create or rebind the image.
 | 
| -    gles2->BindTexture(texture_target, plane_resource.texture_id);
 | 
| -
 | 
| -    if (plane_resource.gpu_memory_buffer && !plane_resource.image_id) {
 | 
| -      const size_t width =
 | 
| -          VideoFrame::Columns(i, output_format_, coded_size.width());
 | 
| -      const size_t height =
 | 
| -          VideoFrame::Rows(i, output_format_, coded_size.height());
 | 
| -      plane_resource.image_id = gles2->CreateImageCHROMIUM(
 | 
| -          plane_resource.gpu_memory_buffer->AsClientBuffer(), width, height,
 | 
| -          ImageInternalFormat(output_format_, i));
 | 
| -    } else if (plane_resource.image_id) {
 | 
| -      gles2->ReleaseTexImage2DCHROMIUM(texture_target, plane_resource.image_id);
 | 
| +
 | 
| +  uint8_t* data(size_t plane) const override {
 | 
| +    DCHECK(!released_video_frame_);
 | 
| +    DCHECK(IsValidPlane(plane, frame_resources_->format()));
 | 
| +    switch (frame_resources_->format()) {
 | 
| +      case PIXEL_FORMAT_I420:
 | 
| +      case PIXEL_FORMAT_YV12:
 | 
| +      case PIXEL_FORMAT_UYVY:
 | 
| +        return static_cast<uint8_t*>(
 | 
| +            frame_resources_->GetPlaneResource(plane).gpu_memory_buffer->memory(
 | 
| +                0));
 | 
| +      case PIXEL_FORMAT_NV12:
 | 
| +        return static_cast<uint8_t*>(
 | 
| +            frame_resources_->GetPlaneResource(0).gpu_memory_buffer->memory(
 | 
| +                plane));
 | 
| +      default:
 | 
| +        NOTREACHED();
 | 
| +        return 0;
 | 
|      }
 | 
| -    if (plane_resource.image_id)
 | 
| -      gles2->BindTexImage2DCHROMIUM(texture_target, plane_resource.image_id);
 | 
| -    mailbox_holders[i] = gpu::MailboxHolder(plane_resource.mailbox,
 | 
| -                                            gpu::SyncToken(), texture_target);
 | 
|    }
 | 
|  
 | 
| -  // Insert a sync_token, this is needed to make sure that the textures the
 | 
| -  // mailboxes refer to will be used only after all the previous commands posted
 | 
| -  // in the command buffer have been processed.
 | 
| -  const GLuint64 fence_sync = gles2->InsertFenceSyncCHROMIUM();
 | 
| -  gles2->OrderingBarrierCHROMIUM();
 | 
| +  int stride(size_t plane) const override {
 | 
| +    DCHECK(!released_video_frame_);
 | 
| +    DCHECK(IsValidPlane(plane, frame_resources_->format()));
 | 
| +    switch (frame_resources_->format()) {
 | 
| +      case PIXEL_FORMAT_I420:
 | 
| +      case PIXEL_FORMAT_YV12:
 | 
| +      case PIXEL_FORMAT_UYVY:
 | 
| +        return frame_resources_->GetPlaneResource(plane)
 | 
| +            .gpu_memory_buffer->stride(0);
 | 
| +      case PIXEL_FORMAT_NV12:
 | 
| +        return frame_resources_->GetPlaneResource(0).gpu_memory_buffer->stride(
 | 
| +            plane);
 | 
| +      default:
 | 
| +        NOTREACHED();
 | 
| +        return 0;
 | 
| +    }
 | 
| +  }
 | 
|  
 | 
| -  gpu::SyncToken sync_token;
 | 
| -  gles2->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData());
 | 
| -  for (size_t i = 0; i < num_planes; i += planes_per_copy)
 | 
| -    mailbox_holders[i].sync_token = sync_token;
 | 
| +  const gfx::Size& coded_size() const override {
 | 
| +    DCHECK(!released_video_frame_);
 | 
| +    return frame_resources_->size();
 | 
| +  }
 | 
|  
 | 
| -  scoped_refptr<VideoFrame> frame;
 | 
| + private:
 | 
| +  GpuMemoryBufferVideoFrameFuture(
 | 
| +      GpuVideoAcceleratorFactories* gpu_factories,
 | 
| +      const scoped_refptr<FrameResources>& frame_resources,
 | 
| +      const gfx::Rect& visible_rect,
 | 
| +      const gfx::Size& natural_size,
 | 
| +      base::TimeDelta timestamp,
 | 
| +      const ReturnFrameResourcesCB& frame_resources_return_cb)
 | 
| +      : VideoFrameFuture(),
 | 
| +        gpu_factories_(gpu_factories),
 | 
| +        frame_resources_(frame_resources),
 | 
| +        visible_rect_(visible_rect),
 | 
| +        natural_size_(natural_size),
 | 
| +        timestamp_(timestamp),
 | 
| +        frame_resources_return_cb_(frame_resources_return_cb) {}
 | 
| +
 | 
| +  GpuVideoAcceleratorFactories* const gpu_factories_;
 | 
| +  scoped_refptr<FrameResources> frame_resources_;
 | 
| +
 | 
| +  const gfx::Rect visible_rect_;
 | 
| +  const gfx::Size natural_size_;
 | 
| +  base::TimeDelta timestamp_;
 | 
| +  ReturnFrameResourcesCB frame_resources_return_cb_;
 | 
| +
 | 
| +  // FrameResources must be recycled after GpuMemoryBufferVideoFrameFuture is
 | 
| +  // destructed.
 | 
| +  scoped_refptr<VideoFrame> released_video_frame_;
 | 
| +};
 | 
|  
 | 
| -  auto release_mailbox_callback = BindToCurrentLoop(
 | 
| -      base::Bind(&PoolImpl::MailboxHoldersReleased, this, frame_resources));
 | 
| +}  // unnamed namespace
 | 
|  
 | 
| -  // Create the VideoFrame backed by native textures.
 | 
| -  gfx::Size visible_size = video_frame->visible_rect().size();
 | 
| -  switch (output_format_) {
 | 
| -    case PIXEL_FORMAT_I420:
 | 
| -      frame = VideoFrame::WrapYUV420NativeTextures(
 | 
| -          mailbox_holders[VideoFrame::kYPlane],
 | 
| -          mailbox_holders[VideoFrame::kUPlane],
 | 
| -          mailbox_holders[VideoFrame::kVPlane], release_mailbox_callback,
 | 
| -          coded_size, gfx::Rect(visible_size), video_frame->natural_size(),
 | 
| -          video_frame->timestamp());
 | 
| -      if (frame &&
 | 
| -          video_frame->metadata()->IsTrue(VideoFrameMetadata::ALLOW_OVERLAY))
 | 
| -        frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
 | 
| -      break;
 | 
| -    case PIXEL_FORMAT_NV12:
 | 
| -    case PIXEL_FORMAT_UYVY:
 | 
| -      frame = VideoFrame::WrapNativeTexture(
 | 
| -          output_format_, mailbox_holders[VideoFrame::kYPlane],
 | 
| -          release_mailbox_callback, coded_size, gfx::Rect(visible_size),
 | 
| -          video_frame->natural_size(), video_frame->timestamp());
 | 
| -      if (frame)
 | 
| -        frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
 | 
| -      break;
 | 
| -    default:
 | 
| +// Implementation of a pool of GpuMemoryBuffers used to back VideoFrames.
 | 
| +class GpuMemoryBufferVideoFramePool::PoolImpl
 | 
| +    : public base::RefCountedThreadSafe<
 | 
| +          GpuMemoryBufferVideoFramePool::PoolImpl>,
 | 
| +      public base::trace_event::MemoryDumpProvider {
 | 
| + public:
 | 
| +  // |media_task_runner| is the media task runner associated with the
 | 
| +  // GL context provided by |gpu_factories|
 | 
| +  // |gpu_factories| is an interface to GPU related operation and can be
 | 
| +  // null if a GL context is not available.
 | 
| +  PoolImpl(const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
 | 
| +           GpuVideoAcceleratorFactories* gpu_factories)
 | 
| +      : media_task_runner_(media_task_runner), gpu_factories_(gpu_factories) {
 | 
| +    DCHECK(media_task_runner_);
 | 
| +    base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
 | 
| +        this, "GpuMemoryBufferVideoFramePool", media_task_runner_);
 | 
| +  }
 | 
| +
 | 
| +  std::unique_ptr<VideoFrameFuture> CreateFrame(VideoPixelFormat format,
 | 
| +                                                const gfx::Size& coded_size,
 | 
| +                                                const gfx::Rect& visible_rect,
 | 
| +                                                const gfx::Size& natural_size,
 | 
| +                                                base::TimeDelta timestamp) {
 | 
| +    DCHECK(media_task_runner_->BelongsToCurrentThread());
 | 
| +    if (!IsSupported(format)) {
 | 
|        NOTREACHED();
 | 
| +      return nullptr;
 | 
| +    }
 | 
| +
 | 
| +    gfx::Size new_coded_size = CodedSize(coded_size, format);
 | 
| +    scoped_refptr<FrameResources> frame_resources =
 | 
| +        GetOrCreateFrameResources(format, new_coded_size);
 | 
| +    if (!frame_resources) {
 | 
| +      return nullptr;
 | 
| +    }
 | 
| +
 | 
| +    return GpuMemoryBufferVideoFrameFuture::Create(
 | 
| +        gpu_factories_, frame_resources, visible_rect, natural_size, timestamp,
 | 
| +        base::Bind(&PoolImpl::FrameResourcesReturned, this));
 | 
|    }
 | 
|  
 | 
| -  if (!frame) {
 | 
| -    release_mailbox_callback.Run(gpu::SyncToken());
 | 
| -    frame_ready_cb.Run(video_frame);
 | 
| -    return;
 | 
| +  // Put back the resources in the pool.
 | 
| +  void FrameResourcesReturned(
 | 
| +      const scoped_refptr<FrameResources>& frame_resources,
 | 
| +      const gpu::SyncToken& release_sync_token) {
 | 
| +    DCHECK(media_task_runner_->BelongsToCurrentThread());
 | 
| +    auto it = std::find(resources_pool_.begin(), resources_pool_.end(),
 | 
| +                        frame_resources);
 | 
| +    DCHECK(it != resources_pool_.end());
 | 
| +    // We want the pool to behave in a FIFO way.
 | 
| +    // This minimizes the chances of locking the buffer that might be
 | 
| +    // still needed for drawing.
 | 
| +    std::swap(*it, resources_pool_.back());
 | 
| +    frame_resources->SetIsInUse(false);
 | 
|    }
 | 
|  
 | 
| -  base::TimeTicks render_time;
 | 
| -  if (video_frame->metadata()->GetTimeTicks(VideoFrameMetadata::REFERENCE_TIME,
 | 
| -                                            &render_time)) {
 | 
| -    frame->metadata()->SetTimeTicks(VideoFrameMetadata::REFERENCE_TIME,
 | 
| -                                    render_time);
 | 
| +  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
 | 
| +                    base::trace_event::ProcessMemoryDump* pmd) override {
 | 
| +    for (auto frame_resources : resources_pool_) {
 | 
| +      frame_resources->OnMemoryDump(args, pmd);
 | 
| +    }
 | 
| +    return true;
 | 
|    }
 | 
|  
 | 
| -  frame->metadata()->SetBoolean(VideoFrameMetadata::READ_LOCK_FENCES_ENABLED,
 | 
| -                                true);
 | 
| +  size_t GetPoolSizeForTesting() const { return resources_pool_.size(); }
 | 
|  
 | 
| -  frame_ready_cb.Run(frame);
 | 
| -}
 | 
| + private:
 | 
| +  friend class base::RefCountedThreadSafe<
 | 
| +      GpuMemoryBufferVideoFramePool::PoolImpl>;
 | 
|  
 | 
| -// Destroy all the resources posting one task per FrameResources
 | 
| -// to the |media_task_runner_|.
 | 
| -GpuMemoryBufferVideoFramePool::PoolImpl::~PoolImpl() {
 | 
| -  // Delete all the resources on the media thread.
 | 
| -  while (!resources_pool_.empty()) {
 | 
| -    FrameResources* frame_resources = resources_pool_.front();
 | 
| -    resources_pool_.pop_front();
 | 
| +  // Destroy all the resources posting one task per FrameResources
 | 
| +  // to the |media_task_runner_|.
 | 
| +  ~PoolImpl() override {
 | 
|      media_task_runner_->PostTask(
 | 
| -        FROM_HERE, base::Bind(&PoolImpl::DeleteFrameResources, gpu_factories_,
 | 
| -                              base::Owned(frame_resources)));
 | 
| +        FROM_HERE, base::Bind(&DeleteFrameResources,
 | 
| +                              base::Passed(std::move(resources_pool_))));
 | 
| +    base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
 | 
| +        this);
 | 
| +  }
 | 
| +
 | 
| +  static void DeleteFrameResources(
 | 
| +      std::list<scoped_refptr<FrameResources>> resources_pool) {
 | 
| +    resources_pool.clear();
 | 
|    }
 | 
| -}
 | 
|  
 | 
| -// Tries to find the resources in the pool or create them.
 | 
| -// Incompatible resources will be dropped.
 | 
| -GpuMemoryBufferVideoFramePool::PoolImpl::FrameResources*
 | 
| -GpuMemoryBufferVideoFramePool::PoolImpl::GetOrCreateFrameResources(
 | 
| -    const gfx::Size& size,
 | 
| -    VideoPixelFormat format) {
 | 
| -  auto it = resources_pool_.begin();
 | 
| -  while (it != resources_pool_.end()) {
 | 
| -    FrameResources* frame_resources = *it;
 | 
| -    if (!frame_resources->IsInUse()) {
 | 
| -      if (AreFrameResourcesCompatible(frame_resources, size)) {
 | 
| -        frame_resources->SetIsInUse(true);
 | 
| -        return frame_resources;
 | 
| +  // Get the resources needed for a frame out of the pool, or create them if
 | 
| +  // necessary.
 | 
| +  // This also drops the LRU resources that can't be reuse for this frame.
 | 
| +  scoped_refptr<FrameResources> GetOrCreateFrameResources(
 | 
| +      VideoPixelFormat format,
 | 
| +      const gfx::Size& size) {
 | 
| +    auto it = resources_pool_.begin();
 | 
| +    while (it != resources_pool_.end()) {
 | 
| +      scoped_refptr<FrameResources> frame_resources = *it;
 | 
| +      if (!frame_resources->IsInUse()) {
 | 
| +        if (frame_resources->IsCompatible(format, size)) {
 | 
| +          frame_resources->SetIsInUse(true);
 | 
| +          return frame_resources;
 | 
| +        } else {
 | 
| +          resources_pool_.erase(it++);
 | 
| +        }
 | 
|        } else {
 | 
| -        resources_pool_.erase(it++);
 | 
| -        DeleteFrameResources(gpu_factories_, frame_resources);
 | 
| -        delete frame_resources;
 | 
| +        it++;
 | 
|        }
 | 
| -    } else {
 | 
| -      it++;
 | 
|      }
 | 
| -  }
 | 
|  
 | 
| -  // Create the resources.
 | 
| -  scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock(
 | 
| -      gpu_factories_->GetGLContextLock());
 | 
| -  if (!lock)
 | 
| -    return nullptr;
 | 
| -
 | 
| -  gpu::gles2::GLES2Interface* gles2 = lock->ContextGL();
 | 
| -  gles2->ActiveTexture(GL_TEXTURE0);
 | 
| -  size_t num_planes = VideoFrame::NumPlanes(format);
 | 
| -  FrameResources* frame_resources = new FrameResources(size);
 | 
| -  resources_pool_.push_back(frame_resources);
 | 
| -  for (size_t i = 0; i < num_planes; i += PlanesPerCopy(format)) {
 | 
| -    PlaneResource& plane_resource = frame_resources->plane_resources[i];
 | 
| -    const size_t width = VideoFrame::Columns(i, format, size.width());
 | 
| -    const size_t height = VideoFrame::Rows(i, format, size.height());
 | 
| -    plane_resource.size = gfx::Size(width, height);
 | 
| -
 | 
| -    const gfx::BufferFormat buffer_format = GpuMemoryBufferFormat(format, i);
 | 
| -    plane_resource.gpu_memory_buffer = gpu_factories_->AllocateGpuMemoryBuffer(
 | 
| -        plane_resource.size, buffer_format,
 | 
| -        gfx::BufferUsage::GPU_READ_CPU_READ_WRITE);
 | 
| -
 | 
| -    unsigned texture_target = gpu_factories_->ImageTextureTarget(buffer_format);
 | 
| -    gles2->GenTextures(1, &plane_resource.texture_id);
 | 
| -    gles2->BindTexture(texture_target, plane_resource.texture_id);
 | 
| -    gles2->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 | 
| -    gles2->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 | 
| -    gles2->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 | 
| -    gles2->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 | 
| -    gles2->GenMailboxCHROMIUM(plane_resource.mailbox.name);
 | 
| -    gles2->ProduceTextureCHROMIUM(texture_target, plane_resource.mailbox.name);
 | 
| +    scoped_refptr<FrameResources> frame_resources =
 | 
| +        FrameResources::Create(gpu_factories_, format, size);
 | 
| +    resources_pool_.push_back(frame_resources);
 | 
| +    return frame_resources;
 | 
|    }
 | 
| -  return frame_resources;
 | 
| -}
 | 
|  
 | 
| -// static
 | 
| -void GpuMemoryBufferVideoFramePool::PoolImpl::DeleteFrameResources(
 | 
| -    GpuVideoAcceleratorFactories* gpu_factories,
 | 
| -    FrameResources* frame_resources) {
 | 
| -  // TODO(dcastagna): As soon as the context lost is dealt with in media,
 | 
| -  // make sure that we won't execute this callback (use a weak pointer to
 | 
| -  // the old context).
 | 
| -
 | 
| -  scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock(
 | 
| -      gpu_factories->GetGLContextLock());
 | 
| -  if (!lock)
 | 
| -    return;
 | 
| -  gpu::gles2::GLES2Interface* gles2 = lock->ContextGL();
 | 
| -
 | 
| -  for (PlaneResource& plane_resource : frame_resources->plane_resources) {
 | 
| -    if (plane_resource.image_id)
 | 
| -      gles2->DestroyImageCHROMIUM(plane_resource.image_id);
 | 
| -    if (plane_resource.texture_id)
 | 
| -      gles2->DeleteTextures(1, &plane_resource.texture_id);
 | 
| +  static bool IsSupported(VideoPixelFormat format) {
 | 
| +    switch (format) {
 | 
| +      // TODO(dshwang): support more format. crbug.com/356871
 | 
| +      case PIXEL_FORMAT_I420:
 | 
| +      case PIXEL_FORMAT_YV12:
 | 
| +      case PIXEL_FORMAT_NV12:
 | 
| +      case PIXEL_FORMAT_UYVY:
 | 
| +        return true;
 | 
| +      default:
 | 
| +        return false;
 | 
| +    }
 | 
|    }
 | 
| -}
 | 
|  
 | 
| -// Called when a VideoFrame is no longer referenced.
 | 
| -// Put back the resources in the pool.
 | 
| -void GpuMemoryBufferVideoFramePool::PoolImpl::MailboxHoldersReleased(
 | 
| -    FrameResources* frame_resources,
 | 
| -    const gpu::SyncToken& release_sync_token) {
 | 
| -  DCHECK(media_task_runner_->BelongsToCurrentThread());
 | 
| -  auto it = std::find(resources_pool_.begin(), resources_pool_.end(),
 | 
| -                      frame_resources);
 | 
| -  DCHECK(it != resources_pool_.end());
 | 
| -  // We want the pool to behave in a FIFO way.
 | 
| -  // This minimizes the chances of locking the buffer that might be
 | 
| -  // still needed for drawing.
 | 
| -  std::swap(*it, resources_pool_.back());
 | 
| -  frame_resources->SetIsInUse(false);
 | 
| -}
 | 
| +  // Task runner associated to the GL context provided by |gpu_factories_|.
 | 
| +  scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
 | 
| +
 | 
| +  // Interface to GPU related operations.
 | 
| +  GpuVideoAcceleratorFactories* gpu_factories_;
 | 
| +
 | 
| +  // Pool of resources.
 | 
| +  std::list<scoped_refptr<FrameResources>> resources_pool_;
 | 
|  
 | 
| -GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool() {}
 | 
| +  DISALLOW_COPY_AND_ASSIGN(PoolImpl);
 | 
| +};
 | 
|  
 | 
|  GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool(
 | 
|      const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
 | 
| -    const scoped_refptr<base::TaskRunner>& worker_task_runner,
 | 
|      GpuVideoAcceleratorFactories* gpu_factories)
 | 
| -    : pool_impl_(
 | 
| -          new PoolImpl(media_task_runner, worker_task_runner, gpu_factories)) {
 | 
| -  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
 | 
| -      pool_impl_.get(), "GpuMemoryBufferVideoFramePool", media_task_runner);
 | 
| -}
 | 
| +    : pool_impl_(new PoolImpl(media_task_runner, gpu_factories)) {}
 | 
|  
 | 
|  GpuMemoryBufferVideoFramePool::~GpuMemoryBufferVideoFramePool() {
 | 
| -  base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
 | 
| -      pool_impl_.get());
 | 
|  }
 | 
|  
 | 
| -void GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame(
 | 
| -    const scoped_refptr<VideoFrame>& video_frame,
 | 
| -    const FrameReadyCB& frame_ready_cb) {
 | 
| -  DCHECK(video_frame);
 | 
| -  pool_impl_->CreateHardwareFrame(video_frame, frame_ready_cb);
 | 
| +std::unique_ptr<VideoFrameFuture> GpuMemoryBufferVideoFramePool::CreateFrame(
 | 
| +    VideoPixelFormat format,
 | 
| +    const gfx::Size& coded_size,
 | 
| +    const gfx::Rect& visible_rect,
 | 
| +    const gfx::Size& natural_size,
 | 
| +    base::TimeDelta timestamp) {
 | 
| +  return pool_impl_->CreateFrame(format, coded_size, visible_rect, natural_size,
 | 
| +                                 timestamp);
 | 
| +}
 | 
| +
 | 
| +size_t GpuMemoryBufferVideoFramePool::GetPoolSizeForTesting() const {
 | 
| +  return pool_impl_->GetPoolSizeForTesting();
 | 
| +}
 | 
| +
 | 
| +// static
 | 
| +size_t GpuMemoryBufferVideoFramePool::PlanesPerCopy(VideoPixelFormat format,
 | 
| +                                                    size_t plane) {
 | 
| +  return gfx::NumberOfPlanesForBufferFormat(
 | 
| +      GpuMemoryBufferFormat(format, plane));
 | 
|  }
 | 
|  
 | 
|  }  // namespace media
 | 
| 
 |