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 |