| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/video/gpu_memory_buffer_video_frame_pool.h" | 5 #include "media/video/gpu_memory_buffer_video_frame_pool.h" |
| 6 | 6 |
| 7 #include <GLES2/gl2.h> | |
| 8 #include <GLES2/gl2ext.h> | |
| 9 #include <stddef.h> | |
| 10 #include <stdint.h> | |
| 11 | |
| 12 #include <algorithm> | 7 #include <algorithm> |
| 13 #include <list> | 8 #include <list> |
| 14 #include <utility> | |
| 15 | 9 |
| 16 #include "base/barrier_closure.h" | |
| 17 #include "base/bind.h" | 10 #include "base/bind.h" |
| 18 #include "base/containers/stack_container.h" | |
| 19 #include "base/location.h" | |
| 20 #include "base/macros.h" | |
| 21 #include "base/memory/linked_ptr.h" | |
| 22 #include "base/strings/stringprintf.h" | 11 #include "base/strings/stringprintf.h" |
| 23 #include "base/trace_event/memory_dump_provider.h" | 12 #include "base/trace_event/memory_dump_provider.h" |
| 24 #include "base/trace_event/trace_event.h" | |
| 25 #include "gpu/GLES2/gl2extchromium.h" | 13 #include "gpu/GLES2/gl2extchromium.h" |
| 26 #include "gpu/command_buffer/client/gles2_interface.h" | |
| 27 #include "media/base/bind_to_current_loop.h" | 14 #include "media/base/bind_to_current_loop.h" |
| 28 #include "media/renderers/gpu_video_accelerator_factories.h" | 15 #include "media/renderers/gpu_video_accelerator_factories.h" |
| 29 #include "third_party/libyuv/include/libyuv.h" | |
| 30 #include "ui/gfx/buffer_format_util.h" | 16 #include "ui/gfx/buffer_format_util.h" |
| 31 #include "ui/gl/trace_util.h" | |
| 32 | 17 |
| 33 namespace media { | 18 namespace media { |
| 34 | 19 |
| 35 // Implementation of a pool of GpuMemoryBuffers used to back VideoFrames. | |
| 36 class GpuMemoryBufferVideoFramePool::PoolImpl | |
| 37 : public base::RefCountedThreadSafe< | |
| 38 GpuMemoryBufferVideoFramePool::PoolImpl>, | |
| 39 public base::trace_event::MemoryDumpProvider { | |
| 40 public: | |
| 41 // |media_task_runner| is the media task runner associated with the | |
| 42 // GL context provided by |gpu_factories| | |
| 43 // |worker_task_runner| is a task runner used to asynchronously copy | |
| 44 // video frame's planes. | |
| 45 // |gpu_factories| is an interface to GPU related operation and can be | |
| 46 // null if a GL context is not available. | |
| 47 PoolImpl(const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, | |
| 48 const scoped_refptr<base::TaskRunner>& worker_task_runner, | |
| 49 GpuVideoAcceleratorFactories* gpu_factories) | |
| 50 : media_task_runner_(media_task_runner), | |
| 51 worker_task_runner_(worker_task_runner), | |
| 52 gpu_factories_(gpu_factories), | |
| 53 output_format_(PIXEL_FORMAT_UNKNOWN) { | |
| 54 DCHECK(media_task_runner_); | |
| 55 DCHECK(worker_task_runner_); | |
| 56 } | |
| 57 | |
| 58 // Takes a software VideoFrame and calls |frame_ready_cb| with a VideoFrame | |
| 59 // backed by native textures if possible. | |
| 60 // The data contained in video_frame is copied into the returned frame | |
| 61 // asynchronously posting tasks to |worker_task_runner_|, while | |
| 62 // |frame_ready_cb| will be called on |media_task_runner_| once all the data | |
| 63 // has been copied. | |
| 64 void CreateHardwareFrame(const scoped_refptr<VideoFrame>& video_frame, | |
| 65 const FrameReadyCB& cb); | |
| 66 | |
| 67 bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, | |
| 68 base::trace_event::ProcessMemoryDump* pmd) override; | |
| 69 | |
| 70 private: | |
| 71 friend class base::RefCountedThreadSafe< | |
| 72 GpuMemoryBufferVideoFramePool::PoolImpl>; | |
| 73 ~PoolImpl() override; | |
| 74 | |
| 75 // Resource to represent a plane. | |
| 76 struct PlaneResource { | |
| 77 gfx::Size size; | |
| 78 scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer; | |
| 79 unsigned texture_id = 0u; | |
| 80 unsigned image_id = 0u; | |
| 81 gpu::Mailbox mailbox; | |
| 82 }; | |
| 83 | |
| 84 // All the resources needed to compose a frame. | |
| 85 struct FrameResources { | |
| 86 explicit FrameResources(const gfx::Size& size) : size(size) {} | |
| 87 void SetIsInUse(bool in_use) { in_use_ = in_use; } | |
| 88 bool IsInUse() const { | |
| 89 if (in_use_) | |
| 90 return true; | |
| 91 for (const PlaneResource& plane_resource : plane_resources) { | |
| 92 if (plane_resource.gpu_memory_buffer && | |
| 93 plane_resource.gpu_memory_buffer->IsInUseByMacOSWindowServer()) { | |
| 94 return true; | |
| 95 } | |
| 96 } | |
| 97 return false; | |
| 98 } | |
| 99 | |
| 100 const gfx::Size size; | |
| 101 PlaneResource plane_resources[VideoFrame::kMaxPlanes]; | |
| 102 | |
| 103 private: | |
| 104 bool in_use_ = true; | |
| 105 }; | |
| 106 | |
| 107 // Copy |video_frame| data into |frame_resouces| | |
| 108 // and calls |done| when done. | |
| 109 void CopyVideoFrameToGpuMemoryBuffers( | |
| 110 const scoped_refptr<VideoFrame>& video_frame, | |
| 111 FrameResources* frame_resources, | |
| 112 const FrameReadyCB& frame_ready_cb); | |
| 113 | |
| 114 // Called when all the data has been copied. | |
| 115 void OnCopiesDone(const scoped_refptr<VideoFrame>& video_frame, | |
| 116 FrameResources* frame_resources, | |
| 117 const FrameReadyCB& frame_ready_cb); | |
| 118 | |
| 119 // Prepares GL resources, mailboxes and calls |frame_ready_cb| with the new | |
| 120 // VideoFrame. | |
| 121 // This has to be run on |media_task_runner_| where |frame_ready_cb| will also | |
| 122 // be run. | |
| 123 void BindAndCreateMailboxesHardwareFrameResources( | |
| 124 const scoped_refptr<VideoFrame>& video_frame, | |
| 125 FrameResources* frame_resources, | |
| 126 const FrameReadyCB& frame_ready_cb); | |
| 127 | |
| 128 // Return true if |resources| can be used to represent a frame for | |
| 129 // specific |format| and |size|. | |
| 130 static bool AreFrameResourcesCompatible(const FrameResources* resources, | |
| 131 const gfx::Size& size) { | |
| 132 return size == resources->size; | |
| 133 } | |
| 134 | |
| 135 // Get the resources needed for a frame out of the pool, or create them if | |
| 136 // necessary. | |
| 137 // This also drops the LRU resources that can't be reuse for this frame. | |
| 138 FrameResources* GetOrCreateFrameResources(const gfx::Size& size, | |
| 139 VideoPixelFormat format); | |
| 140 | |
| 141 // Callback called when a VideoFrame generated with GetFrameResources is no | |
| 142 // longer referenced. | |
| 143 // This must be called on the thread where |media_task_runner_| is current. | |
| 144 void MailboxHoldersReleased(FrameResources* frame_resources, | |
| 145 const gpu::SyncToken& sync_token); | |
| 146 | |
| 147 // Delete resources. This has to be called on the thread where |task_runner| | |
| 148 // is current. | |
| 149 static void DeleteFrameResources(GpuVideoAcceleratorFactories* gpu_factories, | |
| 150 FrameResources* frame_resources); | |
| 151 | |
| 152 // Task runner associated to the GL context provided by |gpu_factories_|. | |
| 153 scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; | |
| 154 // Task runner used to asynchronously copy planes. | |
| 155 scoped_refptr<base::TaskRunner> worker_task_runner_; | |
| 156 | |
| 157 // Interface to GPU related operations. | |
| 158 GpuVideoAcceleratorFactories* gpu_factories_; | |
| 159 | |
| 160 // Pool of resources. | |
| 161 std::list<FrameResources*> resources_pool_; | |
| 162 | |
| 163 // TODO(dcastagna): change the following type from VideoPixelFormat to | |
| 164 // BufferFormat. | |
| 165 VideoPixelFormat output_format_; | |
| 166 | |
| 167 DISALLOW_COPY_AND_ASSIGN(PoolImpl); | |
| 168 }; | |
| 169 | |
| 170 namespace { | 20 namespace { |
| 171 | 21 |
| 172 // VideoFrame copies to GpuMemoryBuffers will be split in copies where the | |
| 173 // output size is |kBytesPerCopyTarget| bytes and run in parallel. | |
| 174 const size_t kBytesPerCopyTarget = 1024 * 1024; // 1MB | |
| 175 | |
| 176 // Return the GpuMemoryBuffer format to use for a specific VideoPixelFormat | 22 // Return the GpuMemoryBuffer format to use for a specific VideoPixelFormat |
| 177 // and plane. | 23 // and plane. |
| 178 gfx::BufferFormat GpuMemoryBufferFormat(VideoPixelFormat format, size_t plane) { | 24 gfx::BufferFormat GpuMemoryBufferFormat(VideoPixelFormat format, size_t plane) { |
| 179 switch (format) { | 25 switch (format) { |
| 180 case PIXEL_FORMAT_I420: | 26 case PIXEL_FORMAT_I420: |
| 27 case PIXEL_FORMAT_YV12: |
| 181 DCHECK_LE(plane, 2u); | 28 DCHECK_LE(plane, 2u); |
| 182 return gfx::BufferFormat::R_8; | 29 return gfx::BufferFormat::R_8; |
| 183 case PIXEL_FORMAT_NV12: | 30 case PIXEL_FORMAT_NV12: |
| 184 DCHECK_LE(plane, 1u); | 31 DCHECK_LE(plane, 1u); |
| 185 return gfx::BufferFormat::YUV_420_BIPLANAR; | 32 return gfx::BufferFormat::YUV_420_BIPLANAR; |
| 186 case PIXEL_FORMAT_UYVY: | 33 case PIXEL_FORMAT_UYVY: |
| 187 DCHECK_EQ(0u, plane); | 34 DCHECK_EQ(0u, plane); |
| 188 return gfx::BufferFormat::UYVY_422; | 35 return gfx::BufferFormat::UYVY_422; |
| 189 default: | 36 default: |
| 190 NOTREACHED(); | 37 NOTREACHED(); |
| 191 return gfx::BufferFormat::BGRA_8888; | 38 return gfx::BufferFormat::BGRA_8888; |
| 192 } | 39 } |
| 193 } | 40 } |
| 194 | 41 |
| 195 unsigned ImageInternalFormat(VideoPixelFormat format, size_t plane) { | 42 unsigned ImageInternalFormat(VideoPixelFormat format, size_t plane) { |
| 196 switch (format) { | 43 switch (format) { |
| 197 case PIXEL_FORMAT_I420: | 44 case PIXEL_FORMAT_I420: |
| 45 case PIXEL_FORMAT_YV12: |
| 198 DCHECK_LE(plane, 2u); | 46 DCHECK_LE(plane, 2u); |
| 199 return GL_RED_EXT; | 47 return GL_RED_EXT; |
| 200 case PIXEL_FORMAT_NV12: | 48 case PIXEL_FORMAT_NV12: |
| 49 // TODO(dshwang): support ChromeOS using GL_RG_EXT. crbug.com/356871 |
| 201 DCHECK_LE(plane, 1u); | 50 DCHECK_LE(plane, 1u); |
| 202 return GL_RGB_YCBCR_420V_CHROMIUM; | 51 return GL_RGB_YCBCR_420V_CHROMIUM; |
| 203 case PIXEL_FORMAT_UYVY: | 52 case PIXEL_FORMAT_UYVY: |
| 204 DCHECK_EQ(0u, plane); | 53 DCHECK_EQ(0u, plane); |
| 205 return GL_RGB_YCBCR_422_CHROMIUM; | 54 return GL_RGB_YCBCR_422_CHROMIUM; |
| 206 default: | 55 default: |
| 207 NOTREACHED(); | 56 NOTREACHED(); |
| 208 return 0; | 57 return 0; |
| 209 } | 58 } |
| 210 } | 59 } |
| 211 | 60 |
| 212 // The number of output planes to be copied in each iteration. | 61 bool IsValidPlane(size_t plane, VideoPixelFormat format) { |
| 213 size_t PlanesPerCopy(VideoPixelFormat format) { | 62 DCHECK_LE(VideoFrame::NumPlanes(format), |
| 63 static_cast<size_t>(VideoFrame::kMaxPlanes)); |
| 64 return (plane < VideoFrame::NumPlanes(format)); |
| 65 } |
| 66 |
| 67 bool IsPowerOfTwo(size_t x) { |
| 68 return x != 0 && (x & (x - 1)) == 0; |
| 69 } |
| 70 |
| 71 inline size_t RoundUp(size_t value, size_t alignment) { |
| 72 DCHECK(IsPowerOfTwo(alignment)); |
| 73 return ((value + (alignment - 1)) & ~(alignment - 1)); |
| 74 } |
| 75 |
| 76 gfx::Size CodedSize(const gfx::Size& size, VideoPixelFormat format) { |
| 214 switch (format) { | 77 switch (format) { |
| 215 case PIXEL_FORMAT_I420: | 78 case PIXEL_FORMAT_I420: |
| 79 case PIXEL_FORMAT_YV12: |
| 80 case PIXEL_FORMAT_NV12: |
| 81 return gfx::Size(RoundUp(size.width(), 2), RoundUp(size.height(), 2)); |
| 216 case PIXEL_FORMAT_UYVY: | 82 case PIXEL_FORMAT_UYVY: |
| 217 return 1; | 83 return gfx::Size(RoundUp(size.width(), 2), size.height()); |
| 218 case PIXEL_FORMAT_NV12: | |
| 219 return 2; | |
| 220 default: | 84 default: |
| 221 NOTREACHED(); | 85 NOTREACHED(); |
| 222 return 0; | 86 } |
| 223 } | 87 return gfx::Size(); |
| 224 } | 88 } |
| 225 | 89 |
| 226 // The number of output rows to be copied in each iteration. | 90 // All the resources needed to compose a frame. |
| 227 int RowsPerCopy(size_t plane, VideoPixelFormat format, int width) { | 91 class FrameResources : public base::RefCountedThreadSafe<FrameResources> { |
| 228 int bytes_per_row = VideoFrame::RowBytes(plane, format, width); | 92 public: |
| 229 if (format == PIXEL_FORMAT_NV12) { | 93 static scoped_refptr<FrameResources> Create( |
| 230 DCHECK_EQ(0u, plane); | 94 GpuVideoAcceleratorFactories* gpu_factories, |
| 231 bytes_per_row += VideoFrame::RowBytes(1, format, width); | 95 VideoPixelFormat format, |
| 232 } | 96 const gfx::Size& size) { |
| 233 // Copy an even number of lines, and at least one. | 97 // Create the resources. |
| 234 return std::max<size_t>((kBytesPerCopyTarget / bytes_per_row) & ~1, 1); | 98 scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock( |
| 235 } | 99 gpu_factories->GetGLContextLock()); |
| 236 | 100 if (!lock) |
| 237 void CopyRowsToI420Buffer(int first_row, | 101 return nullptr; |
| 238 int rows, | 102 |
| 239 int bytes_per_row, | 103 scoped_refptr<FrameResources> frame_resources( |
| 240 const uint8_t* source, | 104 new FrameResources(gpu_factories, format, size)); |
| 241 int source_stride, | 105 |
| 242 uint8_t* output, | 106 gpu::gles2::GLES2Interface* gles2 = lock->ContextGL(); |
| 243 int dest_stride, | 107 gles2->ActiveTexture(GL_TEXTURE0); |
| 244 const base::Closure& done) { | 108 size_t num_planes = VideoFrame::NumPlanes(format); |
| 245 TRACE_EVENT2("media", "CopyRowsToI420Buffer", "bytes_per_row", bytes_per_row, | 109 for (size_t i = 0; i < num_planes; |
| 246 "rows", rows); | 110 i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, i)) { |
| 247 if (output) { | 111 PlaneResource& plane_resource = frame_resources->plane_resources_[i]; |
| 248 DCHECK_NE(dest_stride, 0); | 112 const size_t width = VideoFrame::Columns(i, format, size.width()); |
| 249 DCHECK_LE(bytes_per_row, std::abs(dest_stride)); | 113 const size_t height = VideoFrame::Rows(i, format, size.height()); |
| 250 DCHECK_LE(bytes_per_row, source_stride); | 114 plane_resource.size = gfx::Size(width, height); |
| 251 | 115 |
| 252 libyuv::CopyPlane(source + source_stride * first_row, source_stride, | 116 const gfx::BufferFormat buffer_format = GpuMemoryBufferFormat(format, i); |
| 253 output + dest_stride * first_row, dest_stride, | 117 plane_resource.gpu_memory_buffer = gpu_factories->AllocateGpuMemoryBuffer( |
| 254 bytes_per_row, rows); | 118 plane_resource.size, buffer_format, |
| 255 } | 119 gfx::BufferUsage::GPU_READ_CPU_READ_WRITE); |
| 256 done.Run(); | 120 if (!plane_resource.gpu_memory_buffer) { |
| 257 } | 121 DLOG(ERROR) << "Could not create GpuMemoryBuffer."; |
| 258 | 122 return nullptr; |
| 259 void CopyRowsToNV12Buffer(int first_row, | 123 } |
| 260 int rows, | 124 unsigned texture_target = |
| 261 int bytes_per_row, | 125 gpu_factories->ImageTextureTarget(buffer_format); |
| 262 const scoped_refptr<VideoFrame>& source_frame, | 126 gles2->GenTextures(1, &plane_resource.texture_id); |
| 263 uint8_t* dest_y, | 127 gles2->BindTexture(texture_target, plane_resource.texture_id); |
| 264 int dest_stride_y, | 128 gles2->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 265 uint8_t* dest_uv, | 129 gles2->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 266 int dest_stride_uv, | 130 gles2->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 267 const base::Closure& done) { | 131 gles2->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 268 TRACE_EVENT2("media", "CopyRowsToNV12Buffer", "bytes_per_row", bytes_per_row, | 132 gles2->GenMailboxCHROMIUM(plane_resource.mailbox.name); |
| 269 "rows", rows); | 133 gles2->ProduceTextureCHROMIUM(texture_target, |
| 270 if (dest_y && dest_uv) { | 134 plane_resource.mailbox.name); |
| 271 DCHECK_NE(dest_stride_y, 0); | 135 } |
| 272 DCHECK_NE(dest_stride_uv, 0); | 136 return frame_resources; |
| 273 DCHECK_LE(bytes_per_row, std::abs(dest_stride_y)); | 137 } |
| 274 DCHECK_LE(bytes_per_row, std::abs(dest_stride_uv)); | 138 |
| 275 DCHECK_EQ(0, first_row % 2); | 139 VideoPixelFormat format() const { return format_; } |
| 276 | 140 const gfx::Size& size() const { return size_; } |
| 277 libyuv::I420ToNV12( | 141 |
| 278 source_frame->visible_data(VideoFrame::kYPlane) + | 142 // Resource to represent a plane. |
| 279 first_row * source_frame->stride(VideoFrame::kYPlane), | 143 struct PlaneResource { |
| 280 source_frame->stride(VideoFrame::kYPlane), | 144 gfx::Size size; |
| 281 source_frame->visible_data(VideoFrame::kUPlane) + | 145 std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer; |
| 282 first_row / 2 * source_frame->stride(VideoFrame::kUPlane), | 146 unsigned texture_id = 0u; |
| 283 source_frame->stride(VideoFrame::kUPlane), | 147 unsigned image_id = 0u; |
| 284 source_frame->visible_data(VideoFrame::kVPlane) + | 148 gpu::Mailbox mailbox; |
| 285 first_row / 2 * source_frame->stride(VideoFrame::kVPlane), | 149 }; |
| 286 source_frame->stride(VideoFrame::kVPlane), | 150 PlaneResource& GetPlaneResource(size_t plane) { |
| 287 dest_y + first_row * dest_stride_y, dest_stride_y, | 151 DCHECK(IsValidPlane(plane, format_)); |
| 288 dest_uv + first_row / 2 * dest_stride_uv, dest_stride_uv, bytes_per_row, | 152 return plane_resources_[plane]; |
| 289 rows); | 153 } |
| 290 } | 154 |
| 291 done.Run(); | 155 void SetIsInUse(bool in_use) { in_use_ = in_use; } |
| 292 } | 156 bool IsInUse() const { |
| 293 | 157 if (in_use_) |
| 294 void CopyRowsToUYVYBuffer(int first_row, | 158 return true; |
| 295 int rows, | 159 for (const PlaneResource& plane_resource : plane_resources_) { |
| 296 int width, | 160 if (plane_resource.gpu_memory_buffer->IsInUseByMacOSWindowServer()) { |
| 297 const scoped_refptr<VideoFrame>& source_frame, | 161 return true; |
| 298 uint8_t* output, | 162 } |
| 299 int dest_stride, | 163 } |
| 300 const base::Closure& done) { | 164 return false; |
| 301 TRACE_EVENT2("media", "CopyRowsToUYVYBuffer", "bytes_per_row", width * 2, | 165 } |
| 302 "rows", rows); | 166 |
| 303 if (output) { | 167 bool IsCompatible(VideoPixelFormat format, const gfx::Size& size) { |
| 304 DCHECK_NE(dest_stride, 0); | 168 return format == format_ && size == size_; |
| 305 DCHECK_LE(width, std::abs(dest_stride / 2)); | 169 } |
| 306 DCHECK_EQ(0, first_row % 2); | 170 |
| 307 libyuv::I420ToUYVY( | 171 void OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, |
| 308 source_frame->visible_data(VideoFrame::kYPlane) + | 172 base::trace_event::ProcessMemoryDump* pmd) { |
| 309 first_row * source_frame->stride(VideoFrame::kYPlane), | 173 const uint64_t tracing_process_id = |
| 310 source_frame->stride(VideoFrame::kYPlane), | 174 base::trace_event::MemoryDumpManager::GetInstance() |
| 311 source_frame->visible_data(VideoFrame::kUPlane) + | 175 ->GetTracingProcessId(); |
| 312 first_row / 2 * source_frame->stride(VideoFrame::kUPlane), | 176 const int kImportance = 2; |
| 313 source_frame->stride(VideoFrame::kUPlane), | 177 for (const PlaneResource& plane_resource : plane_resources_) { |
| 314 source_frame->visible_data(VideoFrame::kVPlane) + | 178 gfx::GpuMemoryBufferId buffer_id = |
| 315 first_row / 2 * source_frame->stride(VideoFrame::kVPlane), | 179 plane_resource.gpu_memory_buffer->GetId(); |
| 316 source_frame->stride(VideoFrame::kVPlane), | 180 std::string dump_name = base::StringPrintf( |
| 317 output + first_row * dest_stride, dest_stride, width, rows); | 181 "media/video_frame_memory/buffer_%d", buffer_id.id); |
| 318 } | 182 base::trace_event::MemoryAllocatorDump* dump = |
| 319 done.Run(); | 183 pmd->CreateAllocatorDump(dump_name); |
| 320 } | 184 size_t buffer_size_in_bytes = gfx::BufferSizeForBufferFormat( |
| 321 | 185 plane_resource.size, plane_resource.gpu_memory_buffer->GetFormat()); |
| 322 gfx::Size CodedSize(const scoped_refptr<VideoFrame>& video_frame, | 186 dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, |
| 323 VideoPixelFormat output_format) { | 187 base::trace_event::MemoryAllocatorDump::kUnitsBytes, |
| 324 DCHECK(gfx::Rect(video_frame->coded_size()) | 188 buffer_size_in_bytes); |
| 325 .Contains(video_frame->visible_rect())); | 189 dump->AddScalar("free_size", |
| 326 DCHECK((video_frame->visible_rect().x() & 1) == 0); | 190 base::trace_event::MemoryAllocatorDump::kUnitsBytes, |
| 327 gfx::Size output; | 191 IsInUse() ? 0 : buffer_size_in_bytes); |
| 328 switch (output_format) { | 192 base::trace_event::MemoryAllocatorDumpGuid shared_buffer_guid = |
| 329 case PIXEL_FORMAT_I420: | 193 gfx::GetGpuMemoryBufferGUIDForTracing(tracing_process_id, buffer_id); |
| 330 case PIXEL_FORMAT_NV12: | 194 pmd->CreateSharedGlobalAllocatorDump(shared_buffer_guid); |
| 331 DCHECK((video_frame->visible_rect().y() & 1) == 0); | 195 pmd->AddOwnershipEdge(dump->guid(), shared_buffer_guid, kImportance); |
| 332 output = gfx::Size((video_frame->visible_rect().width() + 1) & ~1, | 196 } |
| 333 (video_frame->visible_rect().height() + 1) & ~1); | 197 } |
| 334 break; | 198 |
| 335 case PIXEL_FORMAT_UYVY: | 199 private: |
| 336 output = gfx::Size((video_frame->visible_rect().width() + 1) & ~1, | 200 friend class base::RefCountedThreadSafe<FrameResources>; |
| 337 video_frame->visible_rect().height()); | 201 FrameResources(GpuVideoAcceleratorFactories* gpu_factories, |
| 338 break; | 202 VideoPixelFormat format, |
| 339 default: | 203 const gfx::Size& size) |
| 204 : gpu_factories_(gpu_factories), |
| 205 format_(format), |
| 206 size_(size), |
| 207 plane_resources_(NumPlanResources(format)) {} |
| 208 |
| 209 ~FrameResources() { |
| 210 scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock( |
| 211 gpu_factories_->GetGLContextLock()); |
| 212 DCHECK(lock); |
| 213 gpu::gles2::GLES2Interface* gles2 = lock->ContextGL(); |
| 214 |
| 215 for (PlaneResource& plane_resource : plane_resources_) { |
| 216 if (plane_resource.image_id) |
| 217 gles2->DestroyImageCHROMIUM(plane_resource.image_id); |
| 218 if (plane_resource.texture_id) |
| 219 gles2->DeleteTextures(1, &plane_resource.texture_id); |
| 220 } |
| 221 } |
| 222 |
| 223 static int NumPlanResources(VideoPixelFormat format) { |
| 224 return VideoFrame::NumPlanes(format) / |
| 225 GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, 0); |
| 226 } |
| 227 |
| 228 GpuVideoAcceleratorFactories* const gpu_factories_; |
| 229 VideoPixelFormat format_; |
| 230 const gfx::Size size_; |
| 231 |
| 232 std::vector<PlaneResource> plane_resources_; |
| 233 bool in_use_ = true; |
| 234 }; |
| 235 |
| 236 class GpuMemoryBufferVideoFrameFuture : public VideoFrameFuture { |
| 237 public: |
| 238 typedef base::Callback<void(const scoped_refptr<FrameResources>&, |
| 239 const gpu::SyncToken&)> |
| 240 ReturnFrameResourcesCB; |
| 241 |
| 242 static std::unique_ptr<GpuMemoryBufferVideoFrameFuture> Create( |
| 243 GpuVideoAcceleratorFactories* gpu_factories, |
| 244 const scoped_refptr<FrameResources>& frame_resources, |
| 245 const gfx::Rect& visible_rect, |
| 246 const gfx::Size& natural_size, |
| 247 base::TimeDelta timestamp, |
| 248 const ReturnFrameResourcesCB& frame_resources_return_cb) { |
| 249 std::unique_ptr<GpuMemoryBufferVideoFrameFuture> future( |
| 250 new GpuMemoryBufferVideoFrameFuture( |
| 251 gpu_factories, frame_resources, visible_rect, natural_size, |
| 252 timestamp, frame_resources_return_cb)); |
| 253 |
| 254 const size_t num_planes = VideoFrame::NumPlanes(frame_resources->format()); |
| 255 for (size_t i = 0; i < num_planes; |
| 256 i += GpuMemoryBufferVideoFramePool::PlanesPerCopy( |
| 257 frame_resources->format(), i)) { |
| 258 FrameResources::PlaneResource& plane_resource = |
| 259 frame_resources->GetPlaneResource(i); |
| 260 if (!plane_resource.gpu_memory_buffer->Map()) { |
| 261 // Prevent the destructor from unmapping the |gpu_memory_buffer|. |
| 262 future->released_video_frame_ = VideoFrame::CreateEOSFrame(); |
| 263 DLOG(ERROR) << "Could not Map() buffer"; |
| 264 return nullptr; |
| 265 } |
| 266 } |
| 267 |
| 268 return future; |
| 269 } |
| 270 |
| 271 ~GpuMemoryBufferVideoFrameFuture() override { |
| 272 if (released_video_frame_) |
| 273 return; |
| 274 |
| 275 // Decoder can be destructed before completing to decode this frame. |
| 276 VideoPixelFormat format = frame_resources_->format(); |
| 277 const size_t num_planes = VideoFrame::NumPlanes(format); |
| 278 for (size_t i = 0; i < num_planes; |
| 279 i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, i)) { |
| 280 FrameResources::PlaneResource& plane_resource = |
| 281 frame_resources_->GetPlaneResource(i); |
| 282 plane_resource.gpu_memory_buffer->Unmap(); |
| 283 } |
| 284 } |
| 285 |
| 286 scoped_refptr<VideoFrame> Release() override { |
| 287 DCHECK(!released_video_frame_); |
| 288 scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock( |
| 289 gpu_factories_->GetGLContextLock()); |
| 290 DCHECK(lock); |
| 291 gpu::gles2::GLES2Interface* gles2 = lock->ContextGL(); |
| 292 |
| 293 VideoPixelFormat format = frame_resources_->format(); |
| 294 const size_t num_planes = VideoFrame::NumPlanes(format); |
| 295 const gfx::Size coded_size = frame_resources_->size(); |
| 296 gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes]; |
| 297 // Set up the planes creating the mailboxes needed to refer to the textures. |
| 298 for (size_t i = 0; i < num_planes; |
| 299 i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, i)) { |
| 300 FrameResources::PlaneResource& plane_resource = |
| 301 frame_resources_->GetPlaneResource(i); |
| 302 plane_resource.gpu_memory_buffer->Unmap(); |
| 303 const gfx::BufferFormat buffer_format = GpuMemoryBufferFormat(format, i); |
| 304 unsigned texture_target = |
| 305 gpu_factories_->ImageTextureTarget(buffer_format); |
| 306 // Bind the texture and create or rebind the image. |
| 307 gles2->BindTexture(texture_target, plane_resource.texture_id); |
| 308 |
| 309 if (plane_resource.gpu_memory_buffer && !plane_resource.image_id) { |
| 310 plane_resource.image_id = gles2->CreateImageCHROMIUM( |
| 311 plane_resource.gpu_memory_buffer->AsClientBuffer(), |
| 312 plane_resource.gpu_memory_buffer->GetSize().width(), |
| 313 plane_resource.gpu_memory_buffer->GetSize().height(), |
| 314 ImageInternalFormat(format, i)); |
| 315 } else if (plane_resource.image_id) { |
| 316 gles2->ReleaseTexImage2DCHROMIUM(texture_target, |
| 317 plane_resource.image_id); |
| 318 } |
| 319 if (plane_resource.image_id) |
| 320 gles2->BindTexImage2DCHROMIUM(texture_target, plane_resource.image_id); |
| 321 mailbox_holders[i] = gpu::MailboxHolder(plane_resource.mailbox, |
| 322 gpu::SyncToken(), texture_target); |
| 323 } |
| 324 |
| 325 // Insert a sync_token, this is needed to make sure that the textures the |
| 326 // mailboxes refer to will be used only after all the previous commands |
| 327 // posted |
| 328 // in the command buffer have been processed. |
| 329 const GLuint64 fence_sync = gles2->InsertFenceSyncCHROMIUM(); |
| 330 gles2->OrderingBarrierCHROMIUM(); |
| 331 |
| 332 gpu::SyncToken sync_token; |
| 333 gles2->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); |
| 334 for (size_t i = 0; i < num_planes; |
| 335 i += GpuMemoryBufferVideoFramePool::PlanesPerCopy(format, i)) |
| 336 mailbox_holders[i].sync_token = sync_token; |
| 337 |
| 338 auto release_video_frame_callback = BindToCurrentLoop( |
| 339 base::Bind(&GpuMemoryBufferVideoFrameFuture::VideoFrameReleased, |
| 340 frame_resources_return_cb_, frame_resources_)); |
| 341 |
| 342 // Create the VideoFrame backed by native textures. |
| 343 switch (format) { |
| 344 case PIXEL_FORMAT_I420: |
| 345 case PIXEL_FORMAT_YV12: |
| 346 released_video_frame_ = VideoFrame::WrapYUV420NativeTextures( |
| 347 mailbox_holders[VideoFrame::kYPlane], |
| 348 mailbox_holders[VideoFrame::kUPlane], |
| 349 mailbox_holders[VideoFrame::kVPlane], release_video_frame_callback, |
| 350 coded_size, visible_rect_, natural_size_, timestamp_); |
| 351 break; |
| 352 case PIXEL_FORMAT_NV12: |
| 353 case PIXEL_FORMAT_UYVY: |
| 354 released_video_frame_ = VideoFrame::WrapNativeTexture( |
| 355 format, mailbox_holders[VideoFrame::kYPlane], |
| 356 release_video_frame_callback, coded_size, visible_rect_, |
| 357 natural_size_, timestamp_); |
| 358 released_video_frame_->metadata()->SetBoolean( |
| 359 VideoFrameMetadata::ALLOW_OVERLAY, true); |
| 360 break; |
| 361 default: |
| 362 NOTREACHED(); |
| 363 } |
| 364 |
| 365 released_video_frame_->metadata()->SetBoolean( |
| 366 VideoFrameMetadata::READ_LOCK_FENCES_ENABLED, true); |
| 367 |
| 368 frame_resources_ = nullptr; |
| 369 return released_video_frame_; |
| 370 } |
| 371 |
| 372 // static because GpuMemoryBufferVideoFrameFuture can be destroyed before |
| 373 // this callback. |
| 374 static void VideoFrameReleased( |
| 375 const ReturnFrameResourcesCB& frame_resources_return_cb, |
| 376 const scoped_refptr<FrameResources>& frame_resources, |
| 377 const gpu::SyncToken& release_sync_token) { |
| 378 frame_resources_return_cb.Run(frame_resources, release_sync_token); |
| 379 } |
| 380 |
| 381 uint8_t* data(size_t plane) const override { |
| 382 DCHECK(!released_video_frame_); |
| 383 DCHECK(IsValidPlane(plane, frame_resources_->format())); |
| 384 switch (frame_resources_->format()) { |
| 385 case PIXEL_FORMAT_I420: |
| 386 case PIXEL_FORMAT_YV12: |
| 387 case PIXEL_FORMAT_UYVY: |
| 388 return static_cast<uint8_t*>( |
| 389 frame_resources_->GetPlaneResource(plane).gpu_memory_buffer->memory( |
| 390 0)); |
| 391 case PIXEL_FORMAT_NV12: |
| 392 return static_cast<uint8_t*>( |
| 393 frame_resources_->GetPlaneResource(0).gpu_memory_buffer->memory( |
| 394 plane)); |
| 395 default: |
| 396 NOTREACHED(); |
| 397 return 0; |
| 398 } |
| 399 } |
| 400 |
| 401 int stride(size_t plane) const override { |
| 402 DCHECK(!released_video_frame_); |
| 403 DCHECK(IsValidPlane(plane, frame_resources_->format())); |
| 404 switch (frame_resources_->format()) { |
| 405 case PIXEL_FORMAT_I420: |
| 406 case PIXEL_FORMAT_YV12: |
| 407 case PIXEL_FORMAT_UYVY: |
| 408 return frame_resources_->GetPlaneResource(plane) |
| 409 .gpu_memory_buffer->stride(0); |
| 410 case PIXEL_FORMAT_NV12: |
| 411 return frame_resources_->GetPlaneResource(0).gpu_memory_buffer->stride( |
| 412 plane); |
| 413 default: |
| 414 NOTREACHED(); |
| 415 return 0; |
| 416 } |
| 417 } |
| 418 |
| 419 const gfx::Size& coded_size() const override { |
| 420 DCHECK(!released_video_frame_); |
| 421 return frame_resources_->size(); |
| 422 } |
| 423 |
| 424 private: |
| 425 GpuMemoryBufferVideoFrameFuture( |
| 426 GpuVideoAcceleratorFactories* gpu_factories, |
| 427 const scoped_refptr<FrameResources>& frame_resources, |
| 428 const gfx::Rect& visible_rect, |
| 429 const gfx::Size& natural_size, |
| 430 base::TimeDelta timestamp, |
| 431 const ReturnFrameResourcesCB& frame_resources_return_cb) |
| 432 : VideoFrameFuture(), |
| 433 gpu_factories_(gpu_factories), |
| 434 frame_resources_(frame_resources), |
| 435 visible_rect_(visible_rect), |
| 436 natural_size_(natural_size), |
| 437 timestamp_(timestamp), |
| 438 frame_resources_return_cb_(frame_resources_return_cb) {} |
| 439 |
| 440 GpuVideoAcceleratorFactories* const gpu_factories_; |
| 441 scoped_refptr<FrameResources> frame_resources_; |
| 442 |
| 443 const gfx::Rect visible_rect_; |
| 444 const gfx::Size natural_size_; |
| 445 base::TimeDelta timestamp_; |
| 446 ReturnFrameResourcesCB frame_resources_return_cb_; |
| 447 |
| 448 // FrameResources must be recycled after GpuMemoryBufferVideoFrameFuture is |
| 449 // destructed. |
| 450 scoped_refptr<VideoFrame> released_video_frame_; |
| 451 }; |
| 452 |
| 453 } // unnamed namespace |
| 454 |
| 455 // Implementation of a pool of GpuMemoryBuffers used to back VideoFrames. |
| 456 class GpuMemoryBufferVideoFramePool::PoolImpl |
| 457 : public base::RefCountedThreadSafe< |
| 458 GpuMemoryBufferVideoFramePool::PoolImpl>, |
| 459 public base::trace_event::MemoryDumpProvider { |
| 460 public: |
| 461 // |media_task_runner| is the media task runner associated with the |
| 462 // GL context provided by |gpu_factories| |
| 463 // |gpu_factories| is an interface to GPU related operation and can be |
| 464 // null if a GL context is not available. |
| 465 PoolImpl(const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, |
| 466 GpuVideoAcceleratorFactories* gpu_factories) |
| 467 : media_task_runner_(media_task_runner), gpu_factories_(gpu_factories) { |
| 468 DCHECK(media_task_runner_); |
| 469 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( |
| 470 this, "GpuMemoryBufferVideoFramePool", media_task_runner_); |
| 471 } |
| 472 |
| 473 std::unique_ptr<VideoFrameFuture> CreateFrame(VideoPixelFormat format, |
| 474 const gfx::Size& coded_size, |
| 475 const gfx::Rect& visible_rect, |
| 476 const gfx::Size& natural_size, |
| 477 base::TimeDelta timestamp) { |
| 478 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 479 if (!IsSupported(format)) { |
| 340 NOTREACHED(); | 480 NOTREACHED(); |
| 341 } | 481 return nullptr; |
| 342 DCHECK(gfx::Rect(video_frame->coded_size()).Contains(gfx::Rect(output))); | 482 } |
| 343 return output; | 483 |
| 344 } | 484 gfx::Size new_coded_size = CodedSize(coded_size, format); |
| 345 } // unnamed namespace | 485 scoped_refptr<FrameResources> frame_resources = |
| 346 | 486 GetOrCreateFrameResources(format, new_coded_size); |
| 347 // Creates a VideoFrame backed by native textures starting from a software | 487 if (!frame_resources) { |
| 348 // VideoFrame. | 488 return nullptr; |
| 349 // The data contained in |video_frame| is copied into the VideoFrame passed to | 489 } |
| 350 // |frame_ready_cb|. | 490 |
| 351 // This has to be called on the thread where |media_task_runner_| is current. | 491 return GpuMemoryBufferVideoFrameFuture::Create( |
| 352 void GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame( | 492 gpu_factories_, frame_resources, visible_rect, natural_size, timestamp, |
| 353 const scoped_refptr<VideoFrame>& video_frame, | 493 base::Bind(&PoolImpl::FrameResourcesReturned, this)); |
| 354 const FrameReadyCB& frame_ready_cb) { | 494 } |
| 355 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 495 |
| 356 // Lazily initialize output_format_ since VideoFrameOutputFormat() has to be | 496 // Put back the resources in the pool. |
| 357 // called on the media_thread while this object might be instantiated on any. | 497 void FrameResourcesReturned( |
| 358 if (output_format_ == PIXEL_FORMAT_UNKNOWN) | 498 const scoped_refptr<FrameResources>& frame_resources, |
| 359 output_format_ = gpu_factories_->VideoFrameOutputFormat(); | 499 const gpu::SyncToken& release_sync_token) { |
| 360 | 500 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 361 if (output_format_ == PIXEL_FORMAT_UNKNOWN) { | 501 auto it = std::find(resources_pool_.begin(), resources_pool_.end(), |
| 362 frame_ready_cb.Run(video_frame); | 502 frame_resources); |
| 363 return; | 503 DCHECK(it != resources_pool_.end()); |
| 364 } | 504 // We want the pool to behave in a FIFO way. |
| 365 switch (video_frame->format()) { | 505 // This minimizes the chances of locking the buffer that might be |
| 366 // Supported cases. | 506 // still needed for drawing. |
| 367 case PIXEL_FORMAT_YV12: | 507 std::swap(*it, resources_pool_.back()); |
| 368 case PIXEL_FORMAT_I420: | 508 frame_resources->SetIsInUse(false); |
| 369 break; | 509 } |
| 370 // Unsupported cases. | 510 |
| 371 case PIXEL_FORMAT_YV12A: | 511 bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, |
| 372 case PIXEL_FORMAT_YV16: | 512 base::trace_event::ProcessMemoryDump* pmd) override { |
| 373 case PIXEL_FORMAT_YV24: | 513 for (auto frame_resources : resources_pool_) { |
| 374 case PIXEL_FORMAT_NV12: | 514 frame_resources->OnMemoryDump(args, pmd); |
| 375 case PIXEL_FORMAT_NV21: | 515 } |
| 376 case PIXEL_FORMAT_UYVY: | 516 return true; |
| 377 case PIXEL_FORMAT_YUY2: | 517 } |
| 378 case PIXEL_FORMAT_ARGB: | 518 |
| 379 case PIXEL_FORMAT_XRGB: | 519 size_t GetPoolSizeForTesting() const { return resources_pool_.size(); } |
| 380 case PIXEL_FORMAT_RGB24: | 520 |
| 381 case PIXEL_FORMAT_RGB32: | 521 private: |
| 382 case PIXEL_FORMAT_MJPEG: | 522 friend class base::RefCountedThreadSafe< |
| 383 case PIXEL_FORMAT_MT21: | 523 GpuMemoryBufferVideoFramePool::PoolImpl>; |
| 384 case PIXEL_FORMAT_YUV420P9: | 524 |
| 385 case PIXEL_FORMAT_YUV422P9: | 525 // Destroy all the resources posting one task per FrameResources |
| 386 case PIXEL_FORMAT_YUV444P9: | 526 // to the |media_task_runner_|. |
| 387 case PIXEL_FORMAT_YUV420P10: | 527 ~PoolImpl() override { |
| 388 case PIXEL_FORMAT_YUV422P10: | 528 media_task_runner_->PostTask( |
| 389 case PIXEL_FORMAT_YUV444P10: | 529 FROM_HERE, base::Bind(&DeleteFrameResources, |
| 390 case PIXEL_FORMAT_UNKNOWN: | 530 base::Passed(std::move(resources_pool_)))); |
| 391 frame_ready_cb.Run(video_frame); | 531 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( |
| 392 return; | 532 this); |
| 393 } | 533 } |
| 394 | 534 |
| 395 const gfx::Size coded_size = CodedSize(video_frame, output_format_); | 535 static void DeleteFrameResources( |
| 396 // Acquire resources. Incompatible ones will be dropped from the pool. | 536 std::list<scoped_refptr<FrameResources>> resources_pool) { |
| 397 FrameResources* frame_resources = | 537 resources_pool.clear(); |
| 398 GetOrCreateFrameResources(coded_size, output_format_); | 538 } |
| 399 if (!frame_resources) { | 539 |
| 400 frame_ready_cb.Run(video_frame); | 540 // Get the resources needed for a frame out of the pool, or create them if |
| 401 return; | 541 // necessary. |
| 402 } | 542 // This also drops the LRU resources that can't be reuse for this frame. |
| 403 | 543 scoped_refptr<FrameResources> GetOrCreateFrameResources( |
| 404 worker_task_runner_->PostTask( | 544 VideoPixelFormat format, |
| 405 FROM_HERE, base::Bind(&PoolImpl::CopyVideoFrameToGpuMemoryBuffers, this, | 545 const gfx::Size& size) { |
| 406 video_frame, frame_resources, frame_ready_cb)); | 546 auto it = resources_pool_.begin(); |
| 407 } | 547 while (it != resources_pool_.end()) { |
| 408 | 548 scoped_refptr<FrameResources> frame_resources = *it; |
| 409 bool GpuMemoryBufferVideoFramePool::PoolImpl::OnMemoryDump( | 549 if (!frame_resources->IsInUse()) { |
| 410 const base::trace_event::MemoryDumpArgs& args, | 550 if (frame_resources->IsCompatible(format, size)) { |
| 411 base::trace_event::ProcessMemoryDump* pmd) { | 551 frame_resources->SetIsInUse(true); |
| 412 const uint64_t tracing_process_id = | 552 return frame_resources; |
| 413 base::trace_event::MemoryDumpManager::GetInstance() | 553 } else { |
| 414 ->GetTracingProcessId(); | 554 resources_pool_.erase(it++); |
| 415 const int kImportance = 2; | 555 } |
| 416 for (const FrameResources* frame_resources : resources_pool_) { | 556 } else { |
| 417 for (const PlaneResource& plane_resource : | 557 it++; |
| 418 frame_resources->plane_resources) { | |
| 419 if (plane_resource.gpu_memory_buffer) { | |
| 420 gfx::GpuMemoryBufferId buffer_id = | |
| 421 plane_resource.gpu_memory_buffer->GetId(); | |
| 422 std::string dump_name = base::StringPrintf( | |
| 423 "media/video_frame_memory/buffer_%d", buffer_id.id); | |
| 424 base::trace_event::MemoryAllocatorDump* dump = | |
| 425 pmd->CreateAllocatorDump(dump_name); | |
| 426 size_t buffer_size_in_bytes = gfx::BufferSizeForBufferFormat( | |
| 427 plane_resource.size, plane_resource.gpu_memory_buffer->GetFormat()); | |
| 428 dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, | |
| 429 base::trace_event::MemoryAllocatorDump::kUnitsBytes, | |
| 430 buffer_size_in_bytes); | |
| 431 dump->AddScalar("free_size", | |
| 432 base::trace_event::MemoryAllocatorDump::kUnitsBytes, | |
| 433 frame_resources->IsInUse() ? 0 : buffer_size_in_bytes); | |
| 434 base::trace_event::MemoryAllocatorDumpGuid shared_buffer_guid = | |
| 435 gfx::GetGpuMemoryBufferGUIDForTracing(tracing_process_id, | |
| 436 buffer_id); | |
| 437 pmd->CreateSharedGlobalAllocatorDump(shared_buffer_guid); | |
| 438 pmd->AddOwnershipEdge(dump->guid(), shared_buffer_guid, kImportance); | |
| 439 } | 558 } |
| 440 } | 559 } |
| 441 } | 560 |
| 442 return true; | 561 scoped_refptr<FrameResources> frame_resources = |
| 443 } | 562 FrameResources::Create(gpu_factories_, format, size); |
| 444 | 563 resources_pool_.push_back(frame_resources); |
| 445 void GpuMemoryBufferVideoFramePool::PoolImpl::OnCopiesDone( | 564 return frame_resources; |
| 446 const scoped_refptr<VideoFrame>& video_frame, | 565 } |
| 447 FrameResources* frame_resources, | 566 |
| 448 const FrameReadyCB& frame_ready_cb) { | 567 static bool IsSupported(VideoPixelFormat format) { |
| 449 for (const auto& plane_resource : frame_resources->plane_resources) { | 568 switch (format) { |
| 450 if (plane_resource.gpu_memory_buffer) | 569 // TODO(dshwang): support more format. crbug.com/356871 |
| 451 plane_resource.gpu_memory_buffer->Unmap(); | 570 case PIXEL_FORMAT_I420: |
| 452 } | 571 case PIXEL_FORMAT_YV12: |
| 453 | 572 case PIXEL_FORMAT_NV12: |
| 454 media_task_runner_->PostTask( | 573 case PIXEL_FORMAT_UYVY: |
| 455 FROM_HERE, | 574 return true; |
| 456 base::Bind(&PoolImpl::BindAndCreateMailboxesHardwareFrameResources, this, | 575 default: |
| 457 video_frame, frame_resources, frame_ready_cb)); | 576 return false; |
| 458 } | 577 } |
| 459 | 578 } |
| 460 // Copies |video_frame| into |frame_resources| asynchronously, posting n tasks | 579 |
| 461 // that will be synchronized by a barrier. | 580 // Task runner associated to the GL context provided by |gpu_factories_|. |
| 462 // After the barrier is passed OnCopiesDone will be called. | 581 scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; |
| 463 void GpuMemoryBufferVideoFramePool::PoolImpl::CopyVideoFrameToGpuMemoryBuffers( | 582 |
| 464 const scoped_refptr<VideoFrame>& video_frame, | 583 // Interface to GPU related operations. |
| 465 FrameResources* frame_resources, | 584 GpuVideoAcceleratorFactories* gpu_factories_; |
| 466 const FrameReadyCB& frame_ready_cb) { | 585 |
| 467 // Compute the number of tasks to post and create the barrier. | 586 // Pool of resources. |
| 468 const size_t num_planes = VideoFrame::NumPlanes(output_format_); | 587 std::list<scoped_refptr<FrameResources>> resources_pool_; |
| 469 const size_t planes_per_copy = PlanesPerCopy(output_format_); | 588 |
| 470 const gfx::Size coded_size = CodedSize(video_frame, output_format_); | 589 DISALLOW_COPY_AND_ASSIGN(PoolImpl); |
| 471 size_t copies = 0; | 590 }; |
| 472 for (size_t i = 0; i < num_planes; i += planes_per_copy) { | |
| 473 const int rows = VideoFrame::Rows(i, output_format_, coded_size.height()); | |
| 474 const int rows_per_copy = | |
| 475 RowsPerCopy(i, output_format_, coded_size.width()); | |
| 476 copies += rows / rows_per_copy; | |
| 477 if (rows % rows_per_copy) | |
| 478 ++copies; | |
| 479 } | |
| 480 const base::Closure copies_done = | |
| 481 base::Bind(&PoolImpl::OnCopiesDone, this, video_frame, frame_resources, | |
| 482 frame_ready_cb); | |
| 483 const base::Closure barrier = base::BarrierClosure(copies, copies_done); | |
| 484 | |
| 485 // Post all the async tasks. | |
| 486 for (size_t i = 0; i < num_planes; i += planes_per_copy) { | |
| 487 gfx::GpuMemoryBuffer* buffer = | |
| 488 frame_resources->plane_resources[i].gpu_memory_buffer.get(); | |
| 489 | |
| 490 if (!buffer || !buffer->Map()) { | |
| 491 DLOG(ERROR) << "Could not get or Map() buffer"; | |
| 492 return; | |
| 493 } | |
| 494 DCHECK_EQ(planes_per_copy, | |
| 495 gfx::NumberOfPlanesForBufferFormat(buffer->GetFormat())); | |
| 496 | |
| 497 const int rows = VideoFrame::Rows(i, output_format_, coded_size.height()); | |
| 498 const int rows_per_copy = | |
| 499 RowsPerCopy(i, output_format_, coded_size.width()); | |
| 500 | |
| 501 for (int row = 0; row < rows; row += rows_per_copy) { | |
| 502 const int rows_to_copy = std::min(rows_per_copy, rows - row); | |
| 503 switch (output_format_) { | |
| 504 case PIXEL_FORMAT_I420: { | |
| 505 const int bytes_per_row = | |
| 506 VideoFrame::RowBytes(i, output_format_, coded_size.width()); | |
| 507 worker_task_runner_->PostTask( | |
| 508 FROM_HERE, base::Bind(&CopyRowsToI420Buffer, row, rows_to_copy, | |
| 509 bytes_per_row, video_frame->visible_data(i), | |
| 510 video_frame->stride(i), | |
| 511 static_cast<uint8_t*>(buffer->memory(0)), | |
| 512 buffer->stride(0), barrier)); | |
| 513 break; | |
| 514 } | |
| 515 case PIXEL_FORMAT_NV12: | |
| 516 worker_task_runner_->PostTask( | |
| 517 FROM_HERE, base::Bind(&CopyRowsToNV12Buffer, row, rows_to_copy, | |
| 518 coded_size.width(), video_frame, | |
| 519 static_cast<uint8_t*>(buffer->memory(0)), | |
| 520 buffer->stride(0), | |
| 521 static_cast<uint8_t*>(buffer->memory(1)), | |
| 522 buffer->stride(1), barrier)); | |
| 523 break; | |
| 524 case PIXEL_FORMAT_UYVY: | |
| 525 worker_task_runner_->PostTask( | |
| 526 FROM_HERE, base::Bind(&CopyRowsToUYVYBuffer, row, rows_to_copy, | |
| 527 coded_size.width(), video_frame, | |
| 528 static_cast<uint8_t*>(buffer->memory(0)), | |
| 529 buffer->stride(0), barrier)); | |
| 530 break; | |
| 531 default: | |
| 532 NOTREACHED(); | |
| 533 } | |
| 534 } | |
| 535 } | |
| 536 } | |
| 537 | |
| 538 void GpuMemoryBufferVideoFramePool::PoolImpl:: | |
| 539 BindAndCreateMailboxesHardwareFrameResources( | |
| 540 const scoped_refptr<VideoFrame>& video_frame, | |
| 541 FrameResources* frame_resources, | |
| 542 const FrameReadyCB& frame_ready_cb) { | |
| 543 scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock( | |
| 544 gpu_factories_->GetGLContextLock()); | |
| 545 if (!lock) { | |
| 546 frame_ready_cb.Run(video_frame); | |
| 547 return; | |
| 548 } | |
| 549 gpu::gles2::GLES2Interface* gles2 = lock->ContextGL(); | |
| 550 | |
| 551 const size_t num_planes = VideoFrame::NumPlanes(output_format_); | |
| 552 const size_t planes_per_copy = PlanesPerCopy(output_format_); | |
| 553 const gfx::Size coded_size = CodedSize(video_frame, output_format_); | |
| 554 gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes]; | |
| 555 // Set up the planes creating the mailboxes needed to refer to the textures. | |
| 556 for (size_t i = 0; i < num_planes; i += planes_per_copy) { | |
| 557 PlaneResource& plane_resource = frame_resources->plane_resources[i]; | |
| 558 const gfx::BufferFormat buffer_format = | |
| 559 GpuMemoryBufferFormat(output_format_, i); | |
| 560 unsigned texture_target = gpu_factories_->ImageTextureTarget(buffer_format); | |
| 561 // Bind the texture and create or rebind the image. | |
| 562 gles2->BindTexture(texture_target, plane_resource.texture_id); | |
| 563 | |
| 564 if (plane_resource.gpu_memory_buffer && !plane_resource.image_id) { | |
| 565 const size_t width = | |
| 566 VideoFrame::Columns(i, output_format_, coded_size.width()); | |
| 567 const size_t height = | |
| 568 VideoFrame::Rows(i, output_format_, coded_size.height()); | |
| 569 plane_resource.image_id = gles2->CreateImageCHROMIUM( | |
| 570 plane_resource.gpu_memory_buffer->AsClientBuffer(), width, height, | |
| 571 ImageInternalFormat(output_format_, i)); | |
| 572 } else if (plane_resource.image_id) { | |
| 573 gles2->ReleaseTexImage2DCHROMIUM(texture_target, plane_resource.image_id); | |
| 574 } | |
| 575 if (plane_resource.image_id) | |
| 576 gles2->BindTexImage2DCHROMIUM(texture_target, plane_resource.image_id); | |
| 577 mailbox_holders[i] = gpu::MailboxHolder(plane_resource.mailbox, | |
| 578 gpu::SyncToken(), texture_target); | |
| 579 } | |
| 580 | |
| 581 // Insert a sync_token, this is needed to make sure that the textures the | |
| 582 // mailboxes refer to will be used only after all the previous commands posted | |
| 583 // in the command buffer have been processed. | |
| 584 const GLuint64 fence_sync = gles2->InsertFenceSyncCHROMIUM(); | |
| 585 gles2->OrderingBarrierCHROMIUM(); | |
| 586 | |
| 587 gpu::SyncToken sync_token; | |
| 588 gles2->GenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token.GetData()); | |
| 589 for (size_t i = 0; i < num_planes; i += planes_per_copy) | |
| 590 mailbox_holders[i].sync_token = sync_token; | |
| 591 | |
| 592 scoped_refptr<VideoFrame> frame; | |
| 593 | |
| 594 auto release_mailbox_callback = BindToCurrentLoop( | |
| 595 base::Bind(&PoolImpl::MailboxHoldersReleased, this, frame_resources)); | |
| 596 | |
| 597 // Create the VideoFrame backed by native textures. | |
| 598 gfx::Size visible_size = video_frame->visible_rect().size(); | |
| 599 switch (output_format_) { | |
| 600 case PIXEL_FORMAT_I420: | |
| 601 frame = VideoFrame::WrapYUV420NativeTextures( | |
| 602 mailbox_holders[VideoFrame::kYPlane], | |
| 603 mailbox_holders[VideoFrame::kUPlane], | |
| 604 mailbox_holders[VideoFrame::kVPlane], release_mailbox_callback, | |
| 605 coded_size, gfx::Rect(visible_size), video_frame->natural_size(), | |
| 606 video_frame->timestamp()); | |
| 607 if (frame && | |
| 608 video_frame->metadata()->IsTrue(VideoFrameMetadata::ALLOW_OVERLAY)) | |
| 609 frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true); | |
| 610 break; | |
| 611 case PIXEL_FORMAT_NV12: | |
| 612 case PIXEL_FORMAT_UYVY: | |
| 613 frame = VideoFrame::WrapNativeTexture( | |
| 614 output_format_, mailbox_holders[VideoFrame::kYPlane], | |
| 615 release_mailbox_callback, coded_size, gfx::Rect(visible_size), | |
| 616 video_frame->natural_size(), video_frame->timestamp()); | |
| 617 if (frame) | |
| 618 frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true); | |
| 619 break; | |
| 620 default: | |
| 621 NOTREACHED(); | |
| 622 } | |
| 623 | |
| 624 if (!frame) { | |
| 625 release_mailbox_callback.Run(gpu::SyncToken()); | |
| 626 frame_ready_cb.Run(video_frame); | |
| 627 return; | |
| 628 } | |
| 629 | |
| 630 base::TimeTicks render_time; | |
| 631 if (video_frame->metadata()->GetTimeTicks(VideoFrameMetadata::REFERENCE_TIME, | |
| 632 &render_time)) { | |
| 633 frame->metadata()->SetTimeTicks(VideoFrameMetadata::REFERENCE_TIME, | |
| 634 render_time); | |
| 635 } | |
| 636 | |
| 637 frame->metadata()->SetBoolean(VideoFrameMetadata::READ_LOCK_FENCES_ENABLED, | |
| 638 true); | |
| 639 | |
| 640 frame_ready_cb.Run(frame); | |
| 641 } | |
| 642 | |
| 643 // Destroy all the resources posting one task per FrameResources | |
| 644 // to the |media_task_runner_|. | |
| 645 GpuMemoryBufferVideoFramePool::PoolImpl::~PoolImpl() { | |
| 646 // Delete all the resources on the media thread. | |
| 647 while (!resources_pool_.empty()) { | |
| 648 FrameResources* frame_resources = resources_pool_.front(); | |
| 649 resources_pool_.pop_front(); | |
| 650 media_task_runner_->PostTask( | |
| 651 FROM_HERE, base::Bind(&PoolImpl::DeleteFrameResources, gpu_factories_, | |
| 652 base::Owned(frame_resources))); | |
| 653 } | |
| 654 } | |
| 655 | |
| 656 // Tries to find the resources in the pool or create them. | |
| 657 // Incompatible resources will be dropped. | |
| 658 GpuMemoryBufferVideoFramePool::PoolImpl::FrameResources* | |
| 659 GpuMemoryBufferVideoFramePool::PoolImpl::GetOrCreateFrameResources( | |
| 660 const gfx::Size& size, | |
| 661 VideoPixelFormat format) { | |
| 662 auto it = resources_pool_.begin(); | |
| 663 while (it != resources_pool_.end()) { | |
| 664 FrameResources* frame_resources = *it; | |
| 665 if (!frame_resources->IsInUse()) { | |
| 666 if (AreFrameResourcesCompatible(frame_resources, size)) { | |
| 667 frame_resources->SetIsInUse(true); | |
| 668 return frame_resources; | |
| 669 } else { | |
| 670 resources_pool_.erase(it++); | |
| 671 DeleteFrameResources(gpu_factories_, frame_resources); | |
| 672 delete frame_resources; | |
| 673 } | |
| 674 } else { | |
| 675 it++; | |
| 676 } | |
| 677 } | |
| 678 | |
| 679 // Create the resources. | |
| 680 scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock( | |
| 681 gpu_factories_->GetGLContextLock()); | |
| 682 if (!lock) | |
| 683 return nullptr; | |
| 684 | |
| 685 gpu::gles2::GLES2Interface* gles2 = lock->ContextGL(); | |
| 686 gles2->ActiveTexture(GL_TEXTURE0); | |
| 687 size_t num_planes = VideoFrame::NumPlanes(format); | |
| 688 FrameResources* frame_resources = new FrameResources(size); | |
| 689 resources_pool_.push_back(frame_resources); | |
| 690 for (size_t i = 0; i < num_planes; i += PlanesPerCopy(format)) { | |
| 691 PlaneResource& plane_resource = frame_resources->plane_resources[i]; | |
| 692 const size_t width = VideoFrame::Columns(i, format, size.width()); | |
| 693 const size_t height = VideoFrame::Rows(i, format, size.height()); | |
| 694 plane_resource.size = gfx::Size(width, height); | |
| 695 | |
| 696 const gfx::BufferFormat buffer_format = GpuMemoryBufferFormat(format, i); | |
| 697 plane_resource.gpu_memory_buffer = gpu_factories_->AllocateGpuMemoryBuffer( | |
| 698 plane_resource.size, buffer_format, | |
| 699 gfx::BufferUsage::GPU_READ_CPU_READ_WRITE); | |
| 700 | |
| 701 unsigned texture_target = gpu_factories_->ImageTextureTarget(buffer_format); | |
| 702 gles2->GenTextures(1, &plane_resource.texture_id); | |
| 703 gles2->BindTexture(texture_target, plane_resource.texture_id); | |
| 704 gles2->TexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
| 705 gles2->TexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
| 706 gles2->TexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
| 707 gles2->TexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
| 708 gles2->GenMailboxCHROMIUM(plane_resource.mailbox.name); | |
| 709 gles2->ProduceTextureCHROMIUM(texture_target, plane_resource.mailbox.name); | |
| 710 } | |
| 711 return frame_resources; | |
| 712 } | |
| 713 | |
| 714 // static | |
| 715 void GpuMemoryBufferVideoFramePool::PoolImpl::DeleteFrameResources( | |
| 716 GpuVideoAcceleratorFactories* gpu_factories, | |
| 717 FrameResources* frame_resources) { | |
| 718 // TODO(dcastagna): As soon as the context lost is dealt with in media, | |
| 719 // make sure that we won't execute this callback (use a weak pointer to | |
| 720 // the old context). | |
| 721 | |
| 722 scoped_ptr<GpuVideoAcceleratorFactories::ScopedGLContextLock> lock( | |
| 723 gpu_factories->GetGLContextLock()); | |
| 724 if (!lock) | |
| 725 return; | |
| 726 gpu::gles2::GLES2Interface* gles2 = lock->ContextGL(); | |
| 727 | |
| 728 for (PlaneResource& plane_resource : frame_resources->plane_resources) { | |
| 729 if (plane_resource.image_id) | |
| 730 gles2->DestroyImageCHROMIUM(plane_resource.image_id); | |
| 731 if (plane_resource.texture_id) | |
| 732 gles2->DeleteTextures(1, &plane_resource.texture_id); | |
| 733 } | |
| 734 } | |
| 735 | |
| 736 // Called when a VideoFrame is no longer referenced. | |
| 737 // Put back the resources in the pool. | |
| 738 void GpuMemoryBufferVideoFramePool::PoolImpl::MailboxHoldersReleased( | |
| 739 FrameResources* frame_resources, | |
| 740 const gpu::SyncToken& release_sync_token) { | |
| 741 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
| 742 auto it = std::find(resources_pool_.begin(), resources_pool_.end(), | |
| 743 frame_resources); | |
| 744 DCHECK(it != resources_pool_.end()); | |
| 745 // We want the pool to behave in a FIFO way. | |
| 746 // This minimizes the chances of locking the buffer that might be | |
| 747 // still needed for drawing. | |
| 748 std::swap(*it, resources_pool_.back()); | |
| 749 frame_resources->SetIsInUse(false); | |
| 750 } | |
| 751 | |
| 752 GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool() {} | |
| 753 | 591 |
| 754 GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool( | 592 GpuMemoryBufferVideoFramePool::GpuMemoryBufferVideoFramePool( |
| 755 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, | 593 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, |
| 756 const scoped_refptr<base::TaskRunner>& worker_task_runner, | |
| 757 GpuVideoAcceleratorFactories* gpu_factories) | 594 GpuVideoAcceleratorFactories* gpu_factories) |
| 758 : pool_impl_( | 595 : pool_impl_(new PoolImpl(media_task_runner, gpu_factories)) {} |
| 759 new PoolImpl(media_task_runner, worker_task_runner, gpu_factories)) { | |
| 760 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( | |
| 761 pool_impl_.get(), "GpuMemoryBufferVideoFramePool", media_task_runner); | |
| 762 } | |
| 763 | 596 |
| 764 GpuMemoryBufferVideoFramePool::~GpuMemoryBufferVideoFramePool() { | 597 GpuMemoryBufferVideoFramePool::~GpuMemoryBufferVideoFramePool() { |
| 765 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( | 598 } |
| 766 pool_impl_.get()); | 599 |
| 767 } | 600 std::unique_ptr<VideoFrameFuture> GpuMemoryBufferVideoFramePool::CreateFrame( |
| 768 | 601 VideoPixelFormat format, |
| 769 void GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame( | 602 const gfx::Size& coded_size, |
| 770 const scoped_refptr<VideoFrame>& video_frame, | 603 const gfx::Rect& visible_rect, |
| 771 const FrameReadyCB& frame_ready_cb) { | 604 const gfx::Size& natural_size, |
| 772 DCHECK(video_frame); | 605 base::TimeDelta timestamp) { |
| 773 pool_impl_->CreateHardwareFrame(video_frame, frame_ready_cb); | 606 return pool_impl_->CreateFrame(format, coded_size, visible_rect, natural_size, |
| 607 timestamp); |
| 608 } |
| 609 |
| 610 size_t GpuMemoryBufferVideoFramePool::GetPoolSizeForTesting() const { |
| 611 return pool_impl_->GetPoolSizeForTesting(); |
| 612 } |
| 613 |
| 614 // static |
| 615 size_t GpuMemoryBufferVideoFramePool::PlanesPerCopy(VideoPixelFormat format, |
| 616 size_t plane) { |
| 617 return gfx::NumberOfPlanesForBufferFormat( |
| 618 GpuMemoryBufferFormat(format, plane)); |
| 774 } | 619 } |
| 775 | 620 |
| 776 } // namespace media | 621 } // namespace media |
| OLD | NEW |