Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(84)

Unified Diff: media/video/gpu_memory_buffer_video_frame_pool.cc

Issue 1874733002: media: split GpuMemoryBufferVideoFramePool into GpuMemoryBufferVideoFrameCopier/Pool Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: add GpuMemoryBufferVideoFramePoolTest Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « media/video/gpu_memory_buffer_video_frame_pool.h ('k') | media/video/gpu_memory_buffer_video_frame_pool_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698