Chromium Code Reviews| 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> | 7 #include <GLES2/gl2.h> |
| 8 #include <GLES2/gl2ext.h> | 8 #include <GLES2/gl2ext.h> |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| 11 #include <list> | 11 #include <list> |
| 12 #include <utility> | 12 #include <utility> |
| 13 | 13 |
| 14 #include "base/barrier_closure.h" | 14 #include "base/barrier_closure.h" |
| 15 #include "base/bind.h" | 15 #include "base/bind.h" |
| 16 #include "base/containers/stack_container.h" | 16 #include "base/containers/stack_container.h" |
| 17 #include "base/location.h" | 17 #include "base/location.h" |
| 18 #include "base/memory/linked_ptr.h" | 18 #include "base/memory/linked_ptr.h" |
| 19 #include "base/trace_event/trace_event.h" | 19 #include "base/trace_event/trace_event.h" |
| 20 #include "gpu/command_buffer/client/gles2_interface.h" | 20 #include "gpu/command_buffer/client/gles2_interface.h" |
| 21 #include "media/renderers/gpu_video_accelerator_factories.h" | 21 #include "media/renderers/gpu_video_accelerator_factories.h" |
| 22 #include "third_party/libyuv/include/libyuv.h" | |
| 23 #include "ui/gfx/buffer_format_util.h" | |
| 22 | 24 |
| 23 namespace media { | 25 namespace media { |
| 24 | 26 |
| 25 // Implementation of a pool of GpuMemoryBuffers used to back VideoFrames. | 27 // Implementation of a pool of GpuMemoryBuffers used to back VideoFrames. |
| 26 class GpuMemoryBufferVideoFramePool::PoolImpl | 28 class GpuMemoryBufferVideoFramePool::PoolImpl |
| 27 : public base::RefCountedThreadSafe< | 29 : public base::RefCountedThreadSafe< |
| 28 GpuMemoryBufferVideoFramePool::PoolImpl> { | 30 GpuMemoryBufferVideoFramePool::PoolImpl> { |
| 29 public: | 31 public: |
| 30 // |media_task_runner| is the media task runner associated with the | 32 // |media_task_runner| is the media task runner associated with the |
| 31 // GL context provided by |gpu_factories| | 33 // GL context provided by |gpu_factories| |
| 32 // |worker_task_runner| is a task runner used to asynchronously copy | 34 // |worker_task_runner| is a task runner used to asynchronously copy |
| 33 // video frame's planes. | 35 // video frame's planes. |
| 34 // |gpu_factories| is an interface to GPU related operation and can be | 36 // |gpu_factories| is an interface to GPU related operation and can be |
| 35 // null if a GL context is not available. | 37 // null if a GL context is not available. |
| 36 PoolImpl(const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, | 38 PoolImpl(const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, |
| 37 const scoped_refptr<base::TaskRunner>& worker_task_runner, | 39 const scoped_refptr<base::TaskRunner>& worker_task_runner, |
| 38 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories) | 40 const scoped_refptr<GpuVideoAcceleratorFactories>& gpu_factories) |
| 39 : media_task_runner_(media_task_runner), | 41 : media_task_runner_(media_task_runner), |
| 40 worker_task_runner_(worker_task_runner), | 42 worker_task_runner_(worker_task_runner), |
| 41 gpu_factories_(gpu_factories), | 43 gpu_factories_(gpu_factories), |
| 42 texture_target_(gpu_factories ? gpu_factories->ImageTextureTarget() | 44 texture_target_(gpu_factories ? gpu_factories->ImageTextureTarget() |
| 43 : GL_TEXTURE_2D) { | 45 : GL_TEXTURE_2D), |
| 46 output_format_(gpu_factories ? gpu_factories->VideoFramePixelFormat() | |
| 47 : PIXEL_FORMAT_I420) { | |
| 44 DCHECK(media_task_runner_); | 48 DCHECK(media_task_runner_); |
| 45 DCHECK(worker_task_runner_); | 49 DCHECK(worker_task_runner_); |
| 46 } | 50 } |
| 47 | 51 |
| 48 // Takes a software VideoFrame and calls |frame_ready_cb| with a VideoFrame | 52 // Takes a software VideoFrame and calls |frame_ready_cb| with a VideoFrame |
| 49 // backed by native textures if possible. | 53 // backed by native textures if possible. |
| 50 // The data contained in video_frame is copied into the returned frame | 54 // The data contained in video_frame is copied into the returned frame |
| 51 // asynchronously posting tasks to |worker_task_runner_|, while | 55 // asynchronously posting tasks to |worker_task_runner_|, while |
| 52 // |frame_ready_cb| will be called on |media_task_runner_| once all the data | 56 // |frame_ready_cb| will be called on |media_task_runner_| once all the data |
| 53 // has been copied. | 57 // has been copied. |
| 54 void CreateHardwareFrame(const scoped_refptr<VideoFrame>& video_frame, | 58 void CreateHardwareFrame(const scoped_refptr<VideoFrame>& video_frame, |
| 55 const FrameReadyCB& cb); | 59 const FrameReadyCB& cb); |
| 56 | 60 |
| 57 private: | 61 private: |
| 58 friend class base::RefCountedThreadSafe< | 62 friend class base::RefCountedThreadSafe< |
| 59 GpuMemoryBufferVideoFramePool::PoolImpl>; | 63 GpuMemoryBufferVideoFramePool::PoolImpl>; |
| 60 ~PoolImpl(); | 64 ~PoolImpl(); |
| 61 | 65 |
| 62 // Resource to represent a plane. | 66 // Resource to represent a plane. |
| 63 struct PlaneResource { | 67 struct PlaneResource { |
| 64 gfx::Size size; | 68 gfx::Size size; |
| 65 scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer; | 69 scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer; |
| 66 unsigned texture_id = 0u; | 70 unsigned texture_id = 0u; |
| 67 unsigned image_id = 0u; | 71 unsigned image_id = 0u; |
| 68 gpu::Mailbox mailbox; | 72 gpu::Mailbox mailbox; |
| 69 }; | 73 }; |
| 70 | 74 |
| 71 // All the resources needed to compose a frame. | 75 // All the resources needed to compose a frame. |
| 72 struct FrameResources { | 76 struct FrameResources { |
| 73 FrameResources(VideoPixelFormat format, const gfx::Size& size) | 77 FrameResources(const gfx::Size& size) : size(size) {} |
|
xhwang
2015/08/21 20:53:07
explicit?
Daniele Castagna
2015/08/21 21:28:18
Done.
| |
| 74 : format(format), size(size) {} | |
| 75 bool in_use = true; | 78 bool in_use = true; |
| 76 VideoPixelFormat format; | |
| 77 gfx::Size size; | 79 gfx::Size size; |
| 78 PlaneResource plane_resources[VideoFrame::kMaxPlanes]; | 80 PlaneResource plane_resources[VideoFrame::kMaxPlanes]; |
| 79 }; | 81 }; |
| 80 | 82 |
| 81 // Copy |video_frame| data into |frame_resouces| | 83 // Copy |video_frame| data into |frame_resouces| |
| 82 // and calls |done| when done. | 84 // and calls |done| when done. |
| 83 void CopyVideoFrameToGpuMemoryBuffers( | 85 void CopyVideoFrameToGpuMemoryBuffers( |
| 84 const scoped_refptr<VideoFrame>& video_frame, | 86 const scoped_refptr<VideoFrame>& video_frame, |
| 85 FrameResources* frame_resources, | 87 FrameResources* frame_resources, |
| 86 const FrameReadyCB& frame_ready_cb); | 88 const FrameReadyCB& frame_ready_cb); |
| 87 | 89 |
| 88 // Called when all the data has been copied. | 90 // Called when all the data has been copied. |
| 89 void OnCopiesDone(const scoped_refptr<VideoFrame>& video_frame, | 91 void OnCopiesDone(const scoped_refptr<VideoFrame>& video_frame, |
| 90 FrameResources* frame_resources, | 92 FrameResources* frame_resources, |
| 91 const FrameReadyCB& frame_ready_cb); | 93 const FrameReadyCB& frame_ready_cb); |
| 92 | 94 |
| 93 // Prepares GL resources, mailboxes and calls |frame_ready_cb| with the new | 95 // Prepares GL resources, mailboxes and calls |frame_ready_cb| with the new |
| 94 // VideoFrame. | 96 // VideoFrame. |
| 95 // This has to be run on |media_task_runner_| where |frame_ready_cb| will also | 97 // This has to be run on |media_task_runner_| where |frame_ready_cb| will also |
| 96 // be run. | 98 // be run. |
| 97 void BindAndCreateMailboxesHardwareFrameResources( | 99 void BindAndCreateMailboxesHardwareFrameResources( |
| 98 const scoped_refptr<VideoFrame>& video_frame, | 100 const scoped_refptr<VideoFrame>& video_frame, |
| 99 FrameResources* frame_resources, | 101 FrameResources* frame_resources, |
| 100 const FrameReadyCB& frame_ready_cb); | 102 const FrameReadyCB& frame_ready_cb); |
| 101 | 103 |
| 102 // Return true if |resources| can be used to represent a frame for | 104 // Return true if |resources| can be used to represent a frame for |
| 103 // specific |format| and |size|. | 105 // specific |format| and |size|. |
| 104 static bool AreFrameResourcesCompatible(const FrameResources* resources, | 106 static bool AreFrameResourcesCompatible(const FrameResources* resources, |
| 105 const gfx::Size& size, | 107 const gfx::Size& size) { |
| 106 VideoPixelFormat format) { | 108 return size == resources->size; |
| 107 return size == resources->size && format == resources->format; | |
| 108 } | 109 } |
| 109 | 110 |
| 110 // Get the resources needed for a frame out of the pool, or create them if | 111 // Get the resources needed for a frame out of the pool, or create them if |
| 111 // necessary. | 112 // necessary. |
| 112 // This also drops the LRU resources that can't be reuse for this frame. | 113 // This also drops the LRU resources that can't be reuse for this frame. |
| 113 FrameResources* GetOrCreateFrameResources(const gfx::Size& size, | 114 FrameResources* GetOrCreateFrameResources(const gfx::Size& size, |
| 114 VideoPixelFormat format); | 115 VideoPixelFormat format); |
| 115 | 116 |
| 116 // Callback called when a VideoFrame generated with GetFrameResources is no | 117 // Callback called when a VideoFrame generated with GetFrameResources is no |
| 117 // longer referenced. | 118 // longer referenced. |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 134 // Task runner used to asynchronously copy planes. | 135 // Task runner used to asynchronously copy planes. |
| 135 scoped_refptr<base::TaskRunner> worker_task_runner_; | 136 scoped_refptr<base::TaskRunner> worker_task_runner_; |
| 136 | 137 |
| 137 // Interface to GPU related operations. | 138 // Interface to GPU related operations. |
| 138 scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories_; | 139 scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories_; |
| 139 | 140 |
| 140 // Pool of resources. | 141 // Pool of resources. |
| 141 std::list<FrameResources*> resources_pool_; | 142 std::list<FrameResources*> resources_pool_; |
| 142 | 143 |
| 143 const unsigned texture_target_; | 144 const unsigned texture_target_; |
| 145 const VideoPixelFormat output_format_; | |
|
reveman
2015/08/21 21:53:03
why is output_format_ of type VideoPixelFormat? wh
Daniele Castagna
2015/08/21 22:46:04
That is what we tried to address long time ago wit
reveman
2015/08/22 14:04:46
We should have fixed that before landing (or as pa
| |
| 146 | |
| 144 DISALLOW_COPY_AND_ASSIGN(PoolImpl); | 147 DISALLOW_COPY_AND_ASSIGN(PoolImpl); |
| 145 }; | 148 }; |
| 146 | 149 |
| 147 namespace { | 150 namespace { |
| 148 | 151 |
| 149 // VideoFrame copies to GpuMemoryBuffers will be split in |kBytesPerCopyTarget| | 152 // VideoFrame copies to GpuMemoryBuffers will be split in copies where the |
| 150 // bytes copies and run in parallel. | 153 // output size is |kBytesPerCopyTarget| bytes and run in parallel. |
| 151 const size_t kBytesPerCopyTarget = 1024 * 1024; // 1MB | 154 const size_t kBytesPerCopyTarget = 1024 * 1024; // 1MB |
| 152 | 155 |
| 153 void CopyRowsToBuffer(int first_row, | 156 // Return the GpuMemoryBuffer format to use for a specific VideoPixelFormat |
| 154 int rows, | 157 // and plane. |
| 155 int bytes_per_row, | 158 gfx::BufferFormat GpuMemoryBufferFormat(VideoPixelFormat format, size_t plane) { |
| 156 const uint8* source, | 159 switch (format) { |
| 157 int source_stride, | 160 case PIXEL_FORMAT_I420: |
| 158 uint8* output, | 161 DCHECK_LE(plane, 2u); |
| 159 int dest_stride, | 162 return gfx::BufferFormat::R_8; |
| 160 const base::Closure& done) { | 163 case PIXEL_FORMAT_UYVY: |
| 161 TRACE_EVENT2("media", "CopyRowsToBuffer", "bytes_per_row", bytes_per_row, | 164 DCHECK_EQ(0u, plane); |
| 165 return gfx::BufferFormat::UYVY_422; | |
| 166 default: | |
| 167 NOTREACHED(); | |
| 168 return gfx::BufferFormat::BGRA_8888; | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 unsigned ImageInternalFormat(VideoPixelFormat format, size_t plane) { | |
| 173 switch (format) { | |
| 174 case PIXEL_FORMAT_I420: | |
| 175 DCHECK_LE(plane, 2u); | |
| 176 return GL_R8_EXT; | |
| 177 case PIXEL_FORMAT_UYVY: | |
| 178 DCHECK_EQ(0u, plane); | |
| 179 return GL_RGB; | |
| 180 default: | |
| 181 NOTREACHED(); | |
| 182 return 0; | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 void CopyRowsToI420Buffer(int first_row, | |
| 187 int rows, | |
| 188 int bytes_per_row, | |
| 189 const uint8* source, | |
| 190 int source_stride, | |
| 191 uint8* output, | |
| 192 int dest_stride, | |
| 193 const base::Closure& done) { | |
| 194 TRACE_EVENT2("media", "CopyRowsToI420Buffer", "bytes_per_row", bytes_per_row, | |
| 162 "rows", rows); | 195 "rows", rows); |
| 163 DCHECK_NE(dest_stride, 0); | 196 DCHECK_NE(dest_stride, 0); |
| 164 DCHECK_LE(bytes_per_row, std::abs(dest_stride)); | 197 DCHECK_LE(bytes_per_row, std::abs(dest_stride)); |
| 165 DCHECK_LE(bytes_per_row, source_stride); | 198 DCHECK_LE(bytes_per_row, source_stride); |
| 166 for (int row = first_row; row < first_row + rows; ++row) { | 199 for (int row = first_row; row < first_row + rows; ++row) { |
| 167 memcpy(output + dest_stride * row, source + source_stride * row, | 200 memcpy(output + dest_stride * row, source + source_stride * row, |
| 168 bytes_per_row); | 201 bytes_per_row); |
| 169 } | 202 } |
| 170 done.Run(); | 203 done.Run(); |
| 171 } | 204 } |
| 172 | 205 |
| 206 void CopyRowsToUYVYBuffer(int first_row, | |
| 207 int rows, | |
| 208 int width, | |
| 209 const scoped_refptr<VideoFrame>& source_frame, | |
| 210 uint8* output, | |
| 211 int dest_stride, | |
| 212 const base::Closure& done) { | |
| 213 TRACE_EVENT2("media", "CopyRowsToUYVYBuffer", "bytes_per_row", width * 2, | |
| 214 "rows", rows); | |
| 215 DCHECK_NE(dest_stride, 0); | |
| 216 DCHECK_LE(width, std::abs(dest_stride / 2)); | |
| 217 DCHECK_EQ(0, first_row % 2); | |
| 218 libyuv::I420ToUYVY( | |
| 219 source_frame->data(VideoFrame::kYPlane) + | |
| 220 first_row * source_frame->stride(VideoFrame::kYPlane), | |
| 221 source_frame->stride(VideoFrame::kYPlane), | |
| 222 source_frame->data(VideoFrame::kUPlane) + | |
| 223 first_row / 2 * source_frame->stride(VideoFrame::kUPlane), | |
| 224 source_frame->stride(VideoFrame::kUPlane), | |
| 225 source_frame->data(VideoFrame::kVPlane) + | |
| 226 first_row / 2 * source_frame->stride(VideoFrame::kVPlane), | |
| 227 source_frame->stride(VideoFrame::kVPlane), | |
| 228 output + first_row * dest_stride, dest_stride, width, rows); | |
| 229 done.Run(); | |
| 230 } | |
| 231 | |
| 173 } // unnamed namespace | 232 } // unnamed namespace |
| 174 | 233 |
| 175 // Creates a VideoFrame backed by native textures starting from a software | 234 // Creates a VideoFrame backed by native textures starting from a software |
| 176 // VideoFrame. | 235 // VideoFrame. |
| 177 // The data contained in |video_frame| is copied into the VideoFrame passed to | 236 // The data contained in |video_frame| is copied into the VideoFrame passed to |
| 178 // |frame_ready_cb|. | 237 // |frame_ready_cb|. |
| 179 // This has to be called on the thread where |media_task_runner_| is current. | 238 // This has to be called on the thread where |media_task_runner_| is current. |
| 180 void GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame( | 239 void GpuMemoryBufferVideoFramePool::PoolImpl::CreateHardwareFrame( |
| 181 const scoped_refptr<VideoFrame>& video_frame, | 240 const scoped_refptr<VideoFrame>& video_frame, |
| 182 const FrameReadyCB& frame_ready_cb) { | 241 const FrameReadyCB& frame_ready_cb) { |
| 183 DCHECK(media_task_runner_->BelongsToCurrentThread()); | 242 DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| 184 if (!gpu_factories_ || !gpu_factories_->IsTextureRGSupported()) { | 243 if (!gpu_factories_ || (output_format_ == PIXEL_FORMAT_I420 && |
| 244 !gpu_factories_->IsTextureRGSupported())) { | |
| 185 frame_ready_cb.Run(video_frame); | 245 frame_ready_cb.Run(video_frame); |
| 186 return; | 246 return; |
| 187 } | 247 } |
| 188 switch (video_frame->format()) { | 248 switch (video_frame->format()) { |
| 189 // Supported cases. | 249 // Supported cases. |
| 190 case PIXEL_FORMAT_YV12: | 250 case PIXEL_FORMAT_YV12: |
| 191 case PIXEL_FORMAT_I420: | 251 case PIXEL_FORMAT_I420: |
| 192 break; | 252 break; |
| 193 // Unsupported cases. | 253 // Unsupported cases. |
| 194 case PIXEL_FORMAT_YV12A: | 254 case PIXEL_FORMAT_YV12A: |
| 195 case PIXEL_FORMAT_YV16: | 255 case PIXEL_FORMAT_YV16: |
| 196 case PIXEL_FORMAT_YV24: | 256 case PIXEL_FORMAT_YV24: |
| 197 case PIXEL_FORMAT_NV12: | 257 case PIXEL_FORMAT_NV12: |
| 198 case PIXEL_FORMAT_ARGB: | 258 case PIXEL_FORMAT_ARGB: |
| 199 case PIXEL_FORMAT_XRGB: | 259 case PIXEL_FORMAT_XRGB: |
| 200 case PIXEL_FORMAT_UYVY: | 260 case PIXEL_FORMAT_UYVY: |
| 201 case PIXEL_FORMAT_UNKNOWN: | 261 case PIXEL_FORMAT_UNKNOWN: |
| 202 frame_ready_cb.Run(video_frame); | 262 frame_ready_cb.Run(video_frame); |
| 203 return; | 263 return; |
| 204 } | 264 } |
| 205 | 265 |
| 206 VideoPixelFormat format = video_frame->format(); | |
| 207 DCHECK(video_frame->visible_rect().origin().IsOrigin()); | 266 DCHECK(video_frame->visible_rect().origin().IsOrigin()); |
| 208 const gfx::Size size = video_frame->visible_rect().size(); | 267 const gfx::Size size = video_frame->visible_rect().size(); |
| 209 | 268 |
| 210 // Acquire resources. Incompatible ones will be dropped from the pool. | 269 // Acquire resources. Incompatible ones will be dropped from the pool. |
| 211 FrameResources* frame_resources = GetOrCreateFrameResources(size, format); | 270 FrameResources* frame_resources = |
| 271 GetOrCreateFrameResources(size, output_format_); | |
| 212 if (!frame_resources) { | 272 if (!frame_resources) { |
| 213 frame_ready_cb.Run(video_frame); | 273 frame_ready_cb.Run(video_frame); |
| 214 return; | 274 return; |
| 215 } | 275 } |
| 216 | 276 |
| 217 worker_task_runner_->PostTask( | 277 worker_task_runner_->PostTask( |
| 218 FROM_HERE, base::Bind(&PoolImpl::CopyVideoFrameToGpuMemoryBuffers, this, | 278 FROM_HERE, base::Bind(&PoolImpl::CopyVideoFrameToGpuMemoryBuffers, this, |
| 219 video_frame, frame_resources, frame_ready_cb)); | 279 video_frame, frame_resources, frame_ready_cb)); |
| 220 } | 280 } |
| 221 | 281 |
| 222 void GpuMemoryBufferVideoFramePool::PoolImpl::OnCopiesDone( | 282 void GpuMemoryBufferVideoFramePool::PoolImpl::OnCopiesDone( |
| 223 const scoped_refptr<VideoFrame>& video_frame, | 283 const scoped_refptr<VideoFrame>& video_frame, |
| 224 FrameResources* frame_resources, | 284 FrameResources* frame_resources, |
| 225 const FrameReadyCB& frame_ready_cb) { | 285 const FrameReadyCB& frame_ready_cb) { |
| 226 const VideoPixelFormat format = video_frame->format(); | 286 const size_t planes = VideoFrame::NumPlanes(output_format_); |
| 227 const size_t planes = VideoFrame::NumPlanes(format); | |
| 228 for (size_t i = 0; i < planes; ++i) { | 287 for (size_t i = 0; i < planes; ++i) { |
| 229 frame_resources->plane_resources[i].gpu_memory_buffer->Unmap(); | 288 frame_resources->plane_resources[i].gpu_memory_buffer->Unmap(); |
| 230 } | 289 } |
| 231 | 290 |
| 232 media_task_runner_->PostTask( | 291 media_task_runner_->PostTask( |
| 233 FROM_HERE, | 292 FROM_HERE, |
| 234 base::Bind(&PoolImpl::BindAndCreateMailboxesHardwareFrameResources, this, | 293 base::Bind(&PoolImpl::BindAndCreateMailboxesHardwareFrameResources, this, |
| 235 video_frame, frame_resources, frame_ready_cb)); | 294 video_frame, frame_resources, frame_ready_cb)); |
| 236 } | 295 } |
| 237 | 296 |
| 238 // Copies |video_frame| into |frame_resources| asynchronously, posting n tasks | 297 // Copies |video_frame| into |frame_resources| asynchronously, posting n tasks |
| 239 // that will be synchronized by a barrier. | 298 // that will be synchronized by a barrier. |
| 240 // After the barrier is passed OnCopiesDone will be called. | 299 // After the barrier is passed OnCopiesDone will be called. |
| 241 void GpuMemoryBufferVideoFramePool::PoolImpl::CopyVideoFrameToGpuMemoryBuffers( | 300 void GpuMemoryBufferVideoFramePool::PoolImpl::CopyVideoFrameToGpuMemoryBuffers( |
| 242 const scoped_refptr<VideoFrame>& video_frame, | 301 const scoped_refptr<VideoFrame>& video_frame, |
| 243 FrameResources* frame_resources, | 302 FrameResources* frame_resources, |
| 244 const FrameReadyCB& frame_ready_cb) { | 303 const FrameReadyCB& frame_ready_cb) { |
| 245 const VideoPixelFormat format = video_frame->format(); | 304 // Compute the number of tasks to post and create the barrier. |
| 246 const size_t planes = VideoFrame::NumPlanes(format); | 305 const size_t dest_planes = VideoFrame::NumPlanes(output_format_); |
| 247 gfx::Size size = video_frame->visible_rect().size(); | 306 gfx::Size size = video_frame->visible_rect().size(); |
| 248 size_t copies = 0; | 307 size_t copies = 0; |
| 249 for (size_t i = 0; i < planes; ++i) { | 308 for (size_t i = 0; i < dest_planes; ++i) { |
| 250 int rows = VideoFrame::Rows(i, format, size.height()); | 309 int rows = VideoFrame::Rows(i, output_format_, size.height()); |
| 251 int bytes_per_row = VideoFrame::RowBytes(i, format, size.width()); | 310 int bytes_per_row = VideoFrame::RowBytes(i, output_format_, size.width()); |
| 311 // Copy a even number of lines, and at least one. | |
| 252 int rows_per_copy = | 312 int rows_per_copy = |
| 253 std::max<size_t>(kBytesPerCopyTarget / bytes_per_row, 1); | 313 std::max<size_t>((kBytesPerCopyTarget / bytes_per_row) & ~1, 1); |
| 254 copies += rows / rows_per_copy; | 314 copies += rows / rows_per_copy; |
| 255 if (rows % rows_per_copy) | 315 if (rows % rows_per_copy) |
| 256 ++copies; | 316 ++copies; |
| 257 } | 317 } |
| 258 | |
| 259 base::Closure copies_done = | 318 base::Closure copies_done = |
| 260 base::Bind(&PoolImpl::OnCopiesDone, this, video_frame, frame_resources, | 319 base::Bind(&PoolImpl::OnCopiesDone, this, video_frame, frame_resources, |
| 261 frame_ready_cb); | 320 frame_ready_cb); |
| 262 base::Closure barrier = base::BarrierClosure(copies, copies_done); | 321 base::Closure barrier = base::BarrierClosure(copies, copies_done); |
| 322 // Post all the async tasks. | |
| 323 for (size_t i = 0; i < dest_planes; ++i) { | |
| 324 int rows = VideoFrame::Rows(i, output_format_, size.height()); | |
| 325 int bytes_per_row = VideoFrame::RowBytes(i, output_format_, size.width()); | |
| 326 int rows_per_copy = | |
| 327 std::max<size_t>((kBytesPerCopyTarget / bytes_per_row) & ~1, 1); | |
| 263 | 328 |
| 264 for (size_t i = 0; i < planes; ++i) { | 329 void* data = nullptr; |
| 265 int rows = VideoFrame::Rows(i, format, size.height()); | 330 CHECK_EQ(1u, gfx::NumberOfPlanesForBufferFormat( |
| 266 int bytes_per_row = VideoFrame::RowBytes(i, format, size.width()); | 331 GpuMemoryBufferFormat(output_format_, i))); |
| 267 int rows_per_copy = | 332 CHECK(frame_resources->plane_resources[i].gpu_memory_buffer->Map(&data)); |
|
Avi (use Gerrit)
2015/08/21 20:57:46
Why CHECK and not DCHECK?
Daniele Castagna
2015/08/21 21:28:18
I remember I had a discussion with reveman@ about
reveman
2015/08/22 14:04:46
This can typically only fail when OOM.
Use a DCHE
| |
| 268 std::max<size_t>(kBytesPerCopyTarget / bytes_per_row, 1); | 333 uint8* mapped_buffer = static_cast<uint8*>(data); |
| 269 | 334 |
| 270 PlaneResource& plane_resource = frame_resources->plane_resources[i]; | |
| 271 void* data = nullptr; | |
| 272 CHECK(plane_resource.gpu_memory_buffer->Map(&data)); | |
| 273 uint8* mapped_buffer = static_cast<uint8*>(data); | |
| 274 int dest_stride = 0; | 335 int dest_stride = 0; |
| 275 plane_resource.gpu_memory_buffer->GetStride(&dest_stride); | 336 frame_resources->plane_resources[i].gpu_memory_buffer->GetStride( |
| 337 &dest_stride); | |
| 276 | 338 |
| 277 for (int row = 0; row < rows; row += rows_per_copy) { | 339 for (int row = 0; row < rows; row += rows_per_copy) { |
| 278 worker_task_runner_->PostTask( | 340 switch (output_format_) { |
| 279 FROM_HERE, | 341 case PIXEL_FORMAT_I420: |
| 280 base::Bind(&CopyRowsToBuffer, row, | 342 worker_task_runner_->PostTask( |
| 281 std::min(rows_per_copy, rows - row), bytes_per_row, | 343 FROM_HERE, |
| 282 video_frame->data(i), video_frame->stride(i), | 344 base::Bind(&CopyRowsToI420Buffer, row, |
| 283 mapped_buffer, dest_stride, barrier)); | 345 std::min(rows_per_copy, rows - row), bytes_per_row, |
| 346 video_frame->data(i), video_frame->stride(i), | |
| 347 mapped_buffer, dest_stride, barrier)); | |
| 348 break; | |
| 349 case PIXEL_FORMAT_UYVY: | |
| 350 worker_task_runner_->PostTask( | |
| 351 FROM_HERE, | |
| 352 base::Bind(&CopyRowsToUYVYBuffer, row, | |
| 353 std::min(rows_per_copy, rows - row), size.width(), | |
| 354 video_frame, mapped_buffer, dest_stride, barrier)); | |
| 355 break; | |
| 356 default: | |
| 357 NOTREACHED(); | |
| 358 } | |
| 284 } | 359 } |
| 285 } | 360 } |
| 286 } | 361 } |
| 287 | 362 |
| 288 void GpuMemoryBufferVideoFramePool::PoolImpl:: | 363 void GpuMemoryBufferVideoFramePool::PoolImpl:: |
| 289 BindAndCreateMailboxesHardwareFrameResources( | 364 BindAndCreateMailboxesHardwareFrameResources( |
| 290 const scoped_refptr<VideoFrame>& video_frame, | 365 const scoped_refptr<VideoFrame>& video_frame, |
| 291 FrameResources* frame_resources, | 366 FrameResources* frame_resources, |
| 292 const FrameReadyCB& frame_ready_cb) { | 367 const FrameReadyCB& frame_ready_cb) { |
| 293 gpu::gles2::GLES2Interface* gles2 = gpu_factories_->GetGLES2Interface(); | 368 gpu::gles2::GLES2Interface* gles2 = gpu_factories_->GetGLES2Interface(); |
| 294 if (!gles2) { | 369 if (!gles2) { |
| 295 frame_ready_cb.Run(video_frame); | 370 frame_ready_cb.Run(video_frame); |
| 296 return; | 371 return; |
| 297 } | 372 } |
| 298 | 373 |
| 299 const VideoPixelFormat format = video_frame->format(); | 374 const size_t planes = VideoFrame::NumPlanes(output_format_); |
| 300 const size_t planes = VideoFrame::NumPlanes(format); | |
| 301 const gfx::Size size = video_frame->visible_rect().size(); | 375 const gfx::Size size = video_frame->visible_rect().size(); |
| 302 gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes]; | 376 gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes]; |
| 303 // Set up the planes creating the mailboxes needed to refer to the textures. | 377 // Set up the planes creating the mailboxes needed to refer to the textures. |
| 304 for (size_t i = 0; i < planes; ++i) { | 378 for (size_t i = 0; i < planes; ++i) { |
| 305 PlaneResource& plane_resource = frame_resources->plane_resources[i]; | 379 PlaneResource& plane_resource = frame_resources->plane_resources[i]; |
| 306 // Bind the texture and create or rebind the image. | 380 // Bind the texture and create or rebind the image. |
| 307 gles2->BindTexture(texture_target_, plane_resource.texture_id); | 381 gles2->BindTexture(texture_target_, plane_resource.texture_id); |
| 308 | 382 |
| 309 if (plane_resource.gpu_memory_buffer && !plane_resource.image_id) { | 383 if (plane_resource.gpu_memory_buffer && !plane_resource.image_id) { |
| 310 const size_t width = VideoFrame::Columns(i, format, size.width()); | 384 const size_t width = VideoFrame::Columns(i, output_format_, size.width()); |
| 311 const size_t height = VideoFrame::Rows(i, format, size.height()); | 385 const size_t height = VideoFrame::Rows(i, output_format_, size.height()); |
| 312 plane_resource.image_id = gles2->CreateImageCHROMIUM( | 386 plane_resource.image_id = gles2->CreateImageCHROMIUM( |
| 313 plane_resource.gpu_memory_buffer->AsClientBuffer(), width, height, | 387 plane_resource.gpu_memory_buffer->AsClientBuffer(), width, height, |
| 314 GL_R8_EXT); | 388 ImageInternalFormat(output_format_, i)); |
| 315 } else { | 389 } else { |
| 316 gles2->ReleaseTexImage2DCHROMIUM(texture_target_, | 390 gles2->ReleaseTexImage2DCHROMIUM(texture_target_, |
| 317 plane_resource.image_id); | 391 plane_resource.image_id); |
| 318 } | 392 } |
| 319 gles2->BindTexImage2DCHROMIUM(texture_target_, plane_resource.image_id); | 393 gles2->BindTexImage2DCHROMIUM(texture_target_, plane_resource.image_id); |
| 320 mailbox_holders[i] = | 394 mailbox_holders[i] = |
| 321 gpu::MailboxHolder(plane_resource.mailbox, texture_target_, 0); | 395 gpu::MailboxHolder(plane_resource.mailbox, texture_target_, 0); |
| 322 } | 396 } |
| 323 | 397 |
| 324 // Insert a sync_point, this is needed to make sure that the textures the | 398 // Insert a sync_point, this is needed to make sure that the textures the |
| 325 // mailboxes refer to will be used only after all the previous commands posted | 399 // mailboxes refer to will be used only after all the previous commands posted |
| 326 // in the command buffer have been processed. | 400 // in the command buffer have been processed. |
| 327 unsigned sync_point = gles2->InsertSyncPointCHROMIUM(); | 401 unsigned sync_point = gles2->InsertSyncPointCHROMIUM(); |
| 328 for (size_t i = 0; i < planes; ++i) { | 402 for (size_t i = 0; i < planes; ++i) { |
| 329 mailbox_holders[i].sync_point = sync_point; | 403 mailbox_holders[i].sync_point = sync_point; |
| 330 } | 404 } |
| 331 | 405 |
| 406 scoped_refptr<VideoFrame> frame; | |
| 332 // Create the VideoFrame backed by native textures. | 407 // Create the VideoFrame backed by native textures. |
| 333 scoped_refptr<VideoFrame> frame = VideoFrame::WrapYUV420NativeTextures( | 408 switch (output_format_) { |
| 334 mailbox_holders[VideoFrame::kYPlane], | 409 case PIXEL_FORMAT_I420: |
| 335 mailbox_holders[VideoFrame::kUPlane], | 410 frame = VideoFrame::WrapYUV420NativeTextures( |
| 336 mailbox_holders[VideoFrame::kVPlane], | 411 mailbox_holders[VideoFrame::kYPlane], |
| 337 base::Bind(&PoolImpl::MailboxHoldersReleased, this, frame_resources), | 412 mailbox_holders[VideoFrame::kUPlane], |
| 338 size, video_frame->visible_rect(), video_frame->natural_size(), | 413 mailbox_holders[VideoFrame::kVPlane], |
| 339 video_frame->timestamp()); | 414 base::Bind(&PoolImpl::MailboxHoldersReleased, this, frame_resources), |
| 415 size, video_frame->visible_rect(), video_frame->natural_size(), | |
| 416 video_frame->timestamp()); | |
| 340 if (video_frame->metadata()->IsTrue(VideoFrameMetadata::ALLOW_OVERLAY)) | 417 if (video_frame->metadata()->IsTrue(VideoFrameMetadata::ALLOW_OVERLAY)) |
| 341 frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true); | 418 frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true); |
| 419 break; | |
|
xhwang
2015/08/21 20:53:07
indentation is off?
Daniele Castagna
2015/08/21 21:28:18
Yeah, andresantoso previously commented on this to
| |
| 420 case PIXEL_FORMAT_UYVY: | |
| 421 frame = VideoFrame::WrapNativeTexture( | |
| 422 PIXEL_FORMAT_UYVY, mailbox_holders[VideoFrame::kYPlane], | |
| 423 base::Bind(&PoolImpl::MailboxHoldersReleased, this, frame_resources), | |
| 424 size, video_frame->visible_rect(), video_frame->natural_size(), | |
| 425 video_frame->timestamp()); | |
| 426 frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true); | |
| 427 break; | |
| 428 default: | |
| 429 NOTREACHED(); | |
| 430 } | |
| 342 frame_ready_cb.Run(frame); | 431 frame_ready_cb.Run(frame); |
| 343 } | 432 } |
| 344 | 433 |
| 345 // Destroy all the resources posting one task per FrameResources | 434 // Destroy all the resources posting one task per FrameResources |
| 346 // to the |media_task_runner_|. | 435 // to the |media_task_runner_|. |
| 347 GpuMemoryBufferVideoFramePool::PoolImpl::~PoolImpl() { | 436 GpuMemoryBufferVideoFramePool::PoolImpl::~PoolImpl() { |
| 348 // Delete all the resources on the media thread. | 437 // Delete all the resources on the media thread. |
| 349 while (!resources_pool_.empty()) { | 438 while (!resources_pool_.empty()) { |
| 350 FrameResources* frame_resources = resources_pool_.front(); | 439 FrameResources* frame_resources = resources_pool_.front(); |
| 351 resources_pool_.pop_front(); | 440 resources_pool_.pop_front(); |
| 352 media_task_runner_->PostTask( | 441 media_task_runner_->PostTask( |
| 353 FROM_HERE, base::Bind(&PoolImpl::DeleteFrameResources, gpu_factories_, | 442 FROM_HERE, base::Bind(&PoolImpl::DeleteFrameResources, gpu_factories_, |
| 354 base::Owned(frame_resources))); | 443 base::Owned(frame_resources))); |
| 355 } | 444 } |
| 356 } | 445 } |
| 357 | 446 |
| 358 // Tries to find the resources in the pool or create them. | 447 // Tries to find the resources in the pool or create them. |
| 359 // Incompatible resources will be dropped. | 448 // Incompatible resources will be dropped. |
| 360 GpuMemoryBufferVideoFramePool::PoolImpl::FrameResources* | 449 GpuMemoryBufferVideoFramePool::PoolImpl::FrameResources* |
| 361 GpuMemoryBufferVideoFramePool::PoolImpl::GetOrCreateFrameResources( | 450 GpuMemoryBufferVideoFramePool::PoolImpl::GetOrCreateFrameResources( |
| 362 const gfx::Size& size, | 451 const gfx::Size& size, |
| 363 VideoPixelFormat format) { | 452 VideoPixelFormat format) { |
| 364 auto it = resources_pool_.begin(); | 453 auto it = resources_pool_.begin(); |
| 365 while (it != resources_pool_.end()) { | 454 while (it != resources_pool_.end()) { |
| 366 FrameResources* frame_resources = *it; | 455 FrameResources* frame_resources = *it; |
| 367 if (!frame_resources->in_use) { | 456 if (!frame_resources->in_use) { |
| 368 if (AreFrameResourcesCompatible(frame_resources, size, format)) { | 457 if (AreFrameResourcesCompatible(frame_resources, size)) { |
| 369 frame_resources->in_use = true; | 458 frame_resources->in_use = true; |
| 370 return frame_resources; | 459 return frame_resources; |
| 371 } else { | 460 } else { |
| 372 resources_pool_.erase(it++); | 461 resources_pool_.erase(it++); |
| 373 DeleteFrameResources(gpu_factories_, frame_resources); | 462 DeleteFrameResources(gpu_factories_, frame_resources); |
| 374 delete frame_resources; | 463 delete frame_resources; |
| 375 } | 464 } |
| 376 } else { | 465 } else { |
| 377 it++; | 466 it++; |
| 378 } | 467 } |
| 379 } | 468 } |
| 380 | 469 |
| 381 // Create the resources. | 470 // Create the resources. |
| 382 gpu::gles2::GLES2Interface* gles2 = gpu_factories_->GetGLES2Interface(); | 471 gpu::gles2::GLES2Interface* gles2 = gpu_factories_->GetGLES2Interface(); |
| 383 if (!gles2) | 472 if (!gles2) |
| 384 return nullptr; | 473 return nullptr; |
| 385 gles2->ActiveTexture(GL_TEXTURE0); | 474 gles2->ActiveTexture(GL_TEXTURE0); |
| 386 size_t planes = VideoFrame::NumPlanes(format); | 475 size_t planes = VideoFrame::NumPlanes(format); |
| 387 FrameResources* frame_resources = new FrameResources(format, size); | 476 FrameResources* frame_resources = new FrameResources(size); |
| 388 resources_pool_.push_back(frame_resources); | 477 resources_pool_.push_back(frame_resources); |
| 389 for (size_t i = 0; i < planes; ++i) { | 478 for (size_t i = 0; i < planes; ++i) { |
| 390 PlaneResource& plane_resource = frame_resources->plane_resources[i]; | 479 PlaneResource& plane_resource = frame_resources->plane_resources[i]; |
| 391 const size_t width = VideoFrame::Columns(i, format, size.width()); | 480 const size_t width = VideoFrame::Columns(i, format, size.width()); |
| 392 const size_t height = VideoFrame::Rows(i, format, size.height()); | 481 const size_t height = VideoFrame::Rows(i, format, size.height()); |
| 393 const gfx::Size plane_size(width, height); | 482 const gfx::Size plane_size(width, height); |
| 483 | |
| 394 plane_resource.gpu_memory_buffer = gpu_factories_->AllocateGpuMemoryBuffer( | 484 plane_resource.gpu_memory_buffer = gpu_factories_->AllocateGpuMemoryBuffer( |
| 395 plane_size, gfx::BufferFormat::R_8, gfx::BufferUsage::MAP); | 485 plane_size, GpuMemoryBufferFormat(format, i), gfx::BufferUsage::MAP); |
| 396 | 486 |
| 397 gles2->GenTextures(1, &plane_resource.texture_id); | 487 gles2->GenTextures(1, &plane_resource.texture_id); |
| 398 gles2->BindTexture(texture_target_, plane_resource.texture_id); | 488 gles2->BindTexture(texture_target_, plane_resource.texture_id); |
| 399 gles2->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | 489 gles2->TexParameteri(texture_target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 400 gles2->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 490 gles2->TexParameteri(texture_target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 401 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 491 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 402 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 492 gles2->TexParameteri(texture_target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 403 gles2->GenMailboxCHROMIUM(plane_resource.mailbox.name); | 493 gles2->GenMailboxCHROMIUM(plane_resource.mailbox.name); |
| 404 gles2->ProduceTextureCHROMIUM(texture_target_, plane_resource.mailbox.name); | 494 gles2->ProduceTextureCHROMIUM(texture_target_, plane_resource.mailbox.name); |
| 405 } | 495 } |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 460 } | 550 } |
| 461 | 551 |
| 462 void GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame( | 552 void GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame( |
| 463 const scoped_refptr<VideoFrame>& video_frame, | 553 const scoped_refptr<VideoFrame>& video_frame, |
| 464 const FrameReadyCB& frame_ready_cb) { | 554 const FrameReadyCB& frame_ready_cb) { |
| 465 DCHECK(video_frame); | 555 DCHECK(video_frame); |
| 466 pool_impl_->CreateHardwareFrame(video_frame, frame_ready_cb); | 556 pool_impl_->CreateHardwareFrame(video_frame, frame_ready_cb); |
| 467 } | 557 } |
| 468 | 558 |
| 469 } // namespace media | 559 } // namespace media |
| OLD | NEW |