Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/filters/vpx_video_decoder.h" | 5 #include "media/filters/vpx_video_decoder.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <vector> | |
| 9 | 10 |
| 10 #include "base/bind.h" | 11 #include "base/bind.h" |
| 11 #include "base/callback_helpers.h" | 12 #include "base/callback_helpers.h" |
| 12 #include "base/command_line.h" | 13 #include "base/command_line.h" |
| 13 #include "base/location.h" | 14 #include "base/location.h" |
| 14 #include "base/logging.h" | 15 #include "base/logging.h" |
| 15 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
| 16 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
| 17 #include "base/sys_byteorder.h" | 18 #include "base/sys_byteorder.h" |
| 18 #include "media/base/bind_to_current_loop.h" | 19 #include "media/base/bind_to_current_loop.h" |
| 19 #include "media/base/decoder_buffer.h" | 20 #include "media/base/decoder_buffer.h" |
| 20 #include "media/base/demuxer_stream.h" | 21 #include "media/base/demuxer_stream.h" |
| 22 #include "media/base/limits.h" | |
| 21 #include "media/base/media_switches.h" | 23 #include "media/base/media_switches.h" |
| 22 #include "media/base/pipeline.h" | 24 #include "media/base/pipeline.h" |
| 23 #include "media/base/video_decoder_config.h" | 25 #include "media/base/video_decoder_config.h" |
| 24 #include "media/base/video_frame.h" | 26 #include "media/base/video_frame.h" |
| 25 #include "media/base/video_util.h" | 27 #include "media/base/video_util.h" |
| 26 | 28 |
| 27 // Include libvpx header files. | 29 // Include libvpx header files. |
| 28 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide | 30 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide |
| 29 // backwards compatibility for legacy applications using the library. | 31 // backwards compatibility for legacy applications using the library. |
| 30 #define VPX_CODEC_DISABLE_COMPAT 1 | 32 #define VPX_CODEC_DISABLE_COMPAT 1 |
| 31 extern "C" { | 33 extern "C" { |
| 32 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h" | 34 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h" |
| 35 #include "third_party/libvpx/source/libvpx/vpx/vpx_external_frame_buffer.h" | |
| 33 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h" | 36 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h" |
| 34 } | 37 } |
| 35 | 38 |
| 36 namespace media { | 39 namespace media { |
| 37 | 40 |
| 38 // Always try to use three threads for video decoding. There is little reason | 41 // Always try to use three threads for video decoding. There is little reason |
| 39 // not to since current day CPUs tend to be multi-core and we measured | 42 // not to since current day CPUs tend to be multi-core and we measured |
| 40 // performance benefits on older machines such as P4s with hyperthreading. | 43 // performance benefits on older machines such as P4s with hyperthreading. |
| 41 static const int kDecodeThreads = 2; | 44 static const int kDecodeThreads = 2; |
| 42 static const int kMaxDecodeThreads = 16; | 45 static const int kMaxDecodeThreads = 16; |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 60 } | 63 } |
| 61 | 64 |
| 62 return decode_threads; | 65 return decode_threads; |
| 63 } | 66 } |
| 64 | 67 |
| 65 decode_threads = std::max(decode_threads, 0); | 68 decode_threads = std::max(decode_threads, 0); |
| 66 decode_threads = std::min(decode_threads, kMaxDecodeThreads); | 69 decode_threads = std::min(decode_threads, kMaxDecodeThreads); |
| 67 return decode_threads; | 70 return decode_threads; |
| 68 } | 71 } |
| 69 | 72 |
| 73 // Maximum number of frame buffers that can be used (by both chromium and libvpx | |
| 74 // combined) for VP9 Decoding. | |
| 75 // TODO(vigneshv): Investigate if this can be relaxed to a higher number. | |
| 76 static const int kVP9MaxFrameBuffers = VP9_MAXIMUM_REF_BUFFERS + | |
| 77 VPX_MAXIMUM_WORK_BUFFERS + | |
| 78 limits::kMaxVideoFrames; | |
| 79 | |
| 80 // Reference counted frame buffers used for VP9 decoding. Reference counting is | |
| 81 // done manually because both chromium and libvpx has to release this before a | |
| 82 // buffer can be re-used. | |
| 83 struct VP9FrameBuffer { | |
|
acolwell GONE FROM CHROMIUM
2014/02/04 00:48:35
nit: Make this decl private inside the MemoryPool
vignesh
2014/02/04 01:38:10
Done.
| |
| 84 uint8* data; | |
| 85 uint64 size; | |
| 86 uint32 ref_cnt; | |
| 87 }; | |
| 88 | |
| 89 class VpxVideoDecoder::MemoryPool | |
| 90 : public base::RefCountedThreadSafe<VpxVideoDecoder::MemoryPool> { | |
| 91 public: | |
| 92 MemoryPool(VpxVideoDecoder* decoder); | |
| 93 | |
| 94 // Callback that will be called by libvpx when it needs a frame buffer. | |
| 95 // Parameters: | |
| 96 // |user_priv| Private data passed to libvpx (pointer to memory pool). | |
| 97 // |min_size| Minimum size needed by libvpx to decompress the next frame. | |
| 98 // |fb| Pointer to the frame buffer to update. | |
| 99 // Returns 0 on success. Returns < 0 on failure. | |
| 100 static int32 GetVP9FrameBuffer(void* user_priv, size_t min_size, | |
| 101 vpx_codec_frame_buffer* fb); | |
| 102 | |
| 103 // Callback that will be called by libvpx when the frame buffer is no more | |
|
acolwell GONE FROM CHROMIUM
2014/02/04 00:48:35
nit: s/more/longer/
vignesh
2014/02/04 01:38:10
Done.
| |
| 104 // being used by libvpx. Parameters: | |
| 105 // |user_priv| Private data passed to libvpx (pointer to memory pool). | |
| 106 // |fb| Pointer to the frame buffer that's being released. | |
| 107 static int32 ReleaseVP9FrameBuffer(void *user_priv, | |
| 108 vpx_codec_frame_buffer *fb); | |
| 109 | |
| 110 // Generates a "no_longer_needed" closure that holds a reference | |
| 111 // to this pool. | |
| 112 base::Closure frame_callback(VP9FrameBuffer* frame_buffer); | |
| 113 | |
| 114 // Sets |decoder_| to NULL. | |
| 115 void ClearDecoder(); | |
| 116 | |
| 117 private: | |
| 118 friend class base::RefCountedThreadSafe<VpxVideoDecoder::MemoryPool>; | |
| 119 ~MemoryPool(); | |
| 120 | |
| 121 // Method that gets called when a VideoFrame that references this pool gets | |
| 122 // destroyed. | |
| 123 void OnVideoFrameDestroyed(VP9FrameBuffer* frame_buffer); | |
| 124 | |
| 125 // Pointer to the VpxVideoDecoder object that has this object. | |
| 126 VpxVideoDecoder* decoder_; | |
| 127 | |
| 128 // Frame buffers to be used by libvpx for VP9 Decoding. | |
| 129 std::vector<VP9FrameBuffer*> frame_buffers_; | |
| 130 | |
| 131 DISALLOW_COPY_AND_ASSIGN(MemoryPool); | |
| 132 }; | |
| 133 | |
| 134 VpxVideoDecoder::MemoryPool::MemoryPool(VpxVideoDecoder* decoder) | |
| 135 : decoder_(decoder) { | |
| 136 } | |
| 137 | |
| 138 VpxVideoDecoder::MemoryPool::~MemoryPool() { | |
| 139 for (size_t i = 0; i < frame_buffers_.size(); ++i) { | |
| 140 delete[] frame_buffers_[i]->data; | |
| 141 delete frame_buffers_[i]; | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 int32 VpxVideoDecoder::MemoryPool::GetVP9FrameBuffer( | |
| 146 void* user_priv, size_t min_size, vpx_codec_frame_buffer* fb) { | |
| 147 if (user_priv == NULL || fb == NULL) | |
|
acolwell GONE FROM CHROMIUM
2014/02/04 00:48:35
nit: These look like they should be DCHECKs since
vignesh
2014/02/04 01:38:10
Done.
| |
| 148 return -1; | |
| 149 | |
| 150 VpxVideoDecoder::MemoryPool* memory_pool = | |
| 151 static_cast<VpxVideoDecoder::MemoryPool*>(user_priv); | |
| 152 | |
|
acolwell GONE FROM CHROMIUM
2014/02/04 00:48:35
Put the rest of this method in a private method an
vignesh
2014/02/04 01:38:10
Done.
| |
| 153 // Check if a free frame buffer exists. | |
| 154 int i = 0; | |
| 155 for (i = 0; i < memory_pool->frame_buffers_.size(); ++i) { | |
|
acolwell GONE FROM CHROMIUM
2014/02/04 00:48:35
nit: s/i = 0// since you initialize it on the line
vignesh
2014/02/04 01:38:10
Done.
| |
| 156 if (memory_pool->frame_buffers_[i]->ref_cnt == 0) | |
| 157 break; | |
| 158 } | |
| 159 | |
| 160 if (i == memory_pool->frame_buffers_.size()) { | |
| 161 // Maximum number of frame buffers reached. | |
| 162 if (i == kVP9MaxFrameBuffers) | |
| 163 return -1; | |
| 164 | |
| 165 // Create a new frame buffer. | |
| 166 memory_pool->frame_buffers_.push_back(new VP9FrameBuffer()); | |
| 167 } | |
| 168 | |
| 169 // Reallocate the frame buffer if necessary. | |
| 170 if (memory_pool->frame_buffers_[i]->size < min_size) { | |
| 171 delete[] memory_pool->frame_buffers_[i]->data; | |
|
acolwell GONE FROM CHROMIUM
2014/02/04 00:48:35
Just use std::vector<uint8> for data so you can re
vignesh
2014/02/04 01:38:10
Done.
| |
| 172 memory_pool->frame_buffers_[i]->data = new uint8[min_size]; | |
| 173 if (!memory_pool->frame_buffers_[i]->data) | |
| 174 return -1; | |
| 175 memory_pool->frame_buffers_[i]->size = min_size; | |
| 176 } | |
| 177 | |
| 178 fb->data = memory_pool->frame_buffers_[i]->data; | |
| 179 fb->size = memory_pool->frame_buffers_[i]->size; | |
| 180 ++memory_pool->frame_buffers_[i]->ref_cnt; | |
| 181 | |
| 182 // Set the frame buffer's private data to point at the external frame buffer. | |
| 183 fb->frame_priv = static_cast<void*>(memory_pool->frame_buffers_[i]); | |
| 184 return 0; | |
| 185 } | |
| 186 | |
| 187 int32 VpxVideoDecoder::MemoryPool::ReleaseVP9FrameBuffer( | |
| 188 void *user_priv, vpx_codec_frame_buffer *fb) { | |
| 189 VP9FrameBuffer* frame_buffer = static_cast<VP9FrameBuffer*>(fb->frame_priv); | |
| 190 --frame_buffer->ref_cnt; | |
|
acolwell GONE FROM CHROMIUM
2014/02/04 00:48:35
Is this method and GetVP9FrameBuffer() ALWAYS call
vignesh
2014/02/04 01:38:10
yes, both the methods are always called on the sam
| |
| 191 return 0; | |
| 192 } | |
| 193 | |
| 194 base::Closure VpxVideoDecoder::MemoryPool::frame_callback( | |
|
acolwell GONE FROM CHROMIUM
2014/02/04 00:48:35
nit: This isn't a trivial accessor so it should ha
vignesh
2014/02/04 01:38:10
Done.
| |
| 195 VP9FrameBuffer* frame_buffer) { | |
| 196 ++frame_buffer->ref_cnt; | |
| 197 return BindToCurrentLoop( | |
| 198 base::Bind(&MemoryPool::OnVideoFrameDestroyed, this, | |
| 199 frame_buffer)); | |
| 200 } | |
| 201 | |
| 202 void VpxVideoDecoder::MemoryPool::OnVideoFrameDestroyed( | |
| 203 VP9FrameBuffer* frame_buffer) { | |
| 204 --frame_buffer->ref_cnt; | |
| 205 } | |
| 206 | |
| 207 void VpxVideoDecoder::MemoryPool::ClearDecoder() { | |
| 208 decoder_ = NULL; | |
| 209 } | |
| 210 | |
| 70 VpxVideoDecoder::VpxVideoDecoder( | 211 VpxVideoDecoder::VpxVideoDecoder( |
| 71 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) | 212 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) |
| 72 : task_runner_(task_runner), | 213 : task_runner_(task_runner), |
| 73 weak_factory_(this), | 214 weak_factory_(this), |
| 74 state_(kUninitialized), | 215 state_(kUninitialized), |
| 75 vpx_codec_(NULL), | 216 vpx_codec_(NULL), |
| 76 vpx_codec_alpha_(NULL) { | 217 vpx_codec_alpha_(NULL), |
| 218 memory_pool_(NULL) { | |
|
acolwell GONE FROM CHROMIUM
2014/02/04 00:48:35
nit: You don't need this for a scoped_refptr. It i
vignesh
2014/02/04 01:38:10
Done.
| |
| 77 } | 219 } |
| 78 | 220 |
| 79 VpxVideoDecoder::~VpxVideoDecoder() { | 221 VpxVideoDecoder::~VpxVideoDecoder() { |
| 80 DCHECK_EQ(kUninitialized, state_); | 222 DCHECK_EQ(kUninitialized, state_); |
| 81 CloseDecoder(); | 223 CloseDecoder(); |
| 82 } | 224 } |
| 83 | 225 |
| 84 void VpxVideoDecoder::Initialize(const VideoDecoderConfig& config, | 226 void VpxVideoDecoder::Initialize(const VideoDecoderConfig& config, |
| 85 const PipelineStatusCB& status_cb) { | 227 const PipelineStatusCB& status_cb) { |
| 86 DCHECK(task_runner_->BelongsToCurrentThread()); | 228 DCHECK(task_runner_->BelongsToCurrentThread()); |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 135 } | 277 } |
| 136 if (!can_handle) | 278 if (!can_handle) |
| 137 return false; | 279 return false; |
| 138 | 280 |
| 139 CloseDecoder(); | 281 CloseDecoder(); |
| 140 | 282 |
| 141 vpx_codec_ = InitializeVpxContext(vpx_codec_, config); | 283 vpx_codec_ = InitializeVpxContext(vpx_codec_, config); |
| 142 if (!vpx_codec_) | 284 if (!vpx_codec_) |
| 143 return false; | 285 return false; |
| 144 | 286 |
| 287 // We use our own buffers for VP9 so that there is no need to copy data after | |
| 288 // decoding. | |
| 289 if (config.codec() == kCodecVP9) { | |
| 290 memory_pool_ = new MemoryPool(this); | |
| 291 if (vpx_codec_set_external_frame_buffer_functions( | |
| 292 vpx_codec_, | |
| 293 &MemoryPool::GetVP9FrameBuffer, | |
| 294 &MemoryPool::ReleaseVP9FrameBuffer, | |
| 295 memory_pool_)) { | |
| 296 LOG(ERROR) << "Failed to configure external buffers."; | |
| 297 return false; | |
| 298 } | |
| 299 } | |
| 300 | |
| 145 if (config.format() == VideoFrame::YV12A) { | 301 if (config.format() == VideoFrame::YV12A) { |
| 146 vpx_codec_alpha_ = InitializeVpxContext(vpx_codec_alpha_, config); | 302 vpx_codec_alpha_ = InitializeVpxContext(vpx_codec_alpha_, config); |
| 147 if (!vpx_codec_alpha_) | 303 if (!vpx_codec_alpha_) |
| 148 return false; | 304 return false; |
| 149 } | 305 } |
| 150 | 306 |
| 151 return true; | 307 return true; |
| 152 } | 308 } |
| 153 | 309 |
| 154 void VpxVideoDecoder::CloseDecoder() { | 310 void VpxVideoDecoder::CloseDecoder() { |
| 155 if (vpx_codec_) { | 311 if (vpx_codec_) { |
| 156 vpx_codec_destroy(vpx_codec_); | 312 vpx_codec_destroy(vpx_codec_); |
| 157 delete vpx_codec_; | 313 delete vpx_codec_; |
| 158 vpx_codec_ = NULL; | 314 vpx_codec_ = NULL; |
| 315 if (memory_pool_) { | |
| 316 memory_pool_->ClearDecoder(); | |
| 317 memory_pool_ = NULL; | |
| 318 } | |
| 159 } | 319 } |
| 160 if (vpx_codec_alpha_) { | 320 if (vpx_codec_alpha_) { |
| 161 vpx_codec_destroy(vpx_codec_alpha_); | 321 vpx_codec_destroy(vpx_codec_alpha_); |
| 162 delete vpx_codec_alpha_; | 322 delete vpx_codec_alpha_; |
| 163 vpx_codec_alpha_ = NULL; | 323 vpx_codec_alpha_ = NULL; |
| 164 } | 324 } |
| 165 } | 325 } |
| 166 | 326 |
| 167 void VpxVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, | 327 void VpxVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, |
| 168 const DecodeCB& decode_cb) { | 328 const DecodeCB& decode_cb) { |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 335 | 495 |
| 336 void VpxVideoDecoder::CopyVpxImageTo(const vpx_image* vpx_image, | 496 void VpxVideoDecoder::CopyVpxImageTo(const vpx_image* vpx_image, |
| 337 const struct vpx_image* vpx_image_alpha, | 497 const struct vpx_image* vpx_image_alpha, |
| 338 scoped_refptr<VideoFrame>* video_frame) { | 498 scoped_refptr<VideoFrame>* video_frame) { |
| 339 CHECK(vpx_image); | 499 CHECK(vpx_image); |
| 340 CHECK(vpx_image->fmt == VPX_IMG_FMT_I420 || | 500 CHECK(vpx_image->fmt == VPX_IMG_FMT_I420 || |
| 341 vpx_image->fmt == VPX_IMG_FMT_YV12); | 501 vpx_image->fmt == VPX_IMG_FMT_YV12); |
| 342 | 502 |
| 343 gfx::Size size(vpx_image->d_w, vpx_image->d_h); | 503 gfx::Size size(vpx_image->d_w, vpx_image->d_h); |
| 344 | 504 |
| 505 if (!vpx_codec_alpha_ && memory_pool_) { | |
| 506 *video_frame = VideoFrame::WrapExternalYuvData( | |
| 507 VideoFrame::YV12, | |
| 508 size, gfx::Rect(size), config_.natural_size(), | |
| 509 vpx_image->stride[VPX_PLANE_Y], | |
| 510 vpx_image->stride[VPX_PLANE_U], | |
| 511 vpx_image->stride[VPX_PLANE_V], | |
| 512 vpx_image->planes[VPX_PLANE_Y], | |
| 513 vpx_image->planes[VPX_PLANE_U], | |
| 514 vpx_image->planes[VPX_PLANE_V], | |
| 515 kNoTimestamp(), | |
| 516 memory_pool_->frame_callback( | |
|
acolwell GONE FROM CHROMIUM
2014/02/04 00:48:35
nit: Change the signature of this method so you do
vignesh
2014/02/04 01:38:10
Done.
| |
| 517 static_cast<VP9FrameBuffer*>( | |
| 518 vpx_image->ext_fb_priv))); | |
| 519 return; | |
| 520 } | |
| 521 | |
| 345 *video_frame = frame_pool_.CreateFrame( | 522 *video_frame = frame_pool_.CreateFrame( |
| 346 vpx_codec_alpha_ ? VideoFrame::YV12A : VideoFrame::YV12, | 523 vpx_codec_alpha_ ? VideoFrame::YV12A : VideoFrame::YV12, |
| 347 size, | 524 size, |
| 348 gfx::Rect(size), | 525 gfx::Rect(size), |
| 349 config_.natural_size(), | 526 config_.natural_size(), |
| 350 kNoTimestamp()); | 527 kNoTimestamp()); |
| 351 | 528 |
| 352 CopyYPlane(vpx_image->planes[VPX_PLANE_Y], | 529 CopyYPlane(vpx_image->planes[VPX_PLANE_Y], |
| 353 vpx_image->stride[VPX_PLANE_Y], | 530 vpx_image->stride[VPX_PLANE_Y], |
| 354 vpx_image->d_h, | 531 vpx_image->d_h, |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 368 vpx_image->stride[VPX_PLANE_Y], vpx_image->d_h, video_frame->get()); | 545 vpx_image->stride[VPX_PLANE_Y], vpx_image->d_h, video_frame->get()); |
| 369 return; | 546 return; |
| 370 } | 547 } |
| 371 CopyAPlane(vpx_image_alpha->planes[VPX_PLANE_Y], | 548 CopyAPlane(vpx_image_alpha->planes[VPX_PLANE_Y], |
| 372 vpx_image->stride[VPX_PLANE_Y], | 549 vpx_image->stride[VPX_PLANE_Y], |
| 373 vpx_image->d_h, | 550 vpx_image->d_h, |
| 374 video_frame->get()); | 551 video_frame->get()); |
| 375 } | 552 } |
| 376 | 553 |
| 377 } // namespace media | 554 } // namespace media |
| OLD | NEW |