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 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/callback_helpers.h" | 11 #include "base/callback_helpers.h" |
| 12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 13 #include "base/location.h" | 13 #include "base/location.h" |
| 14 #include "base/logging.h" | 14 #include "base/logging.h" |
| 15 #include "base/message_loop/message_loop_proxy.h" | 15 #include "base/message_loop/message_loop_proxy.h" |
| 16 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
| 17 #include "base/sys_byteorder.h" | 17 #include "base/sys_byteorder.h" |
| 18 #include "media/base/bind_to_loop.h" | 18 #include "media/base/bind_to_loop.h" |
| 19 #include "media/base/decoder_buffer.h" | 19 #include "media/base/decoder_buffer.h" |
| 20 #include "media/base/demuxer_stream.h" | 20 #include "media/base/demuxer_stream.h" |
| 21 #include "media/base/limits.h" | |
| 21 #include "media/base/media_switches.h" | 22 #include "media/base/media_switches.h" |
| 22 #include "media/base/pipeline.h" | 23 #include "media/base/pipeline.h" |
| 23 #include "media/base/video_decoder_config.h" | 24 #include "media/base/video_decoder_config.h" |
| 24 #include "media/base/video_frame.h" | 25 #include "media/base/video_frame.h" |
| 25 #include "media/base/video_util.h" | 26 #include "media/base/video_util.h" |
| 26 | 27 |
| 27 // Include libvpx header files. | 28 // Include libvpx header files. |
| 28 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide | 29 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide |
| 29 // backwards compatibility for legacy applications using the library. | 30 // backwards compatibility for legacy applications using the library. |
| 30 #define VPX_CODEC_DISABLE_COMPAT 1 | 31 #define VPX_CODEC_DISABLE_COMPAT 1 |
| 31 extern "C" { | 32 extern "C" { |
| 32 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h" | 33 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h" |
| 34 #include "third_party/libvpx/source/libvpx/vpx/vpx_external_frame_buffer.h" | |
| 33 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h" | 35 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h" |
| 34 } | 36 } |
| 35 | 37 |
| 36 namespace media { | 38 namespace media { |
| 37 | 39 |
| 38 // Always try to use three threads for video decoding. There is little reason | 40 // 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 | 41 // 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. | 42 // performance benefits on older machines such as P4s with hyperthreading. |
| 41 static const int kDecodeThreads = 2; | 43 static const int kDecodeThreads = 2; |
| 42 static const int kMaxDecodeThreads = 16; | 44 static const int kMaxDecodeThreads = 16; |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 60 } | 62 } |
| 61 | 63 |
| 62 return decode_threads; | 64 return decode_threads; |
| 63 } | 65 } |
| 64 | 66 |
| 65 decode_threads = std::max(decode_threads, 0); | 67 decode_threads = std::max(decode_threads, 0); |
| 66 decode_threads = std::min(decode_threads, kMaxDecodeThreads); | 68 decode_threads = std::min(decode_threads, kMaxDecodeThreads); |
| 67 return decode_threads; | 69 return decode_threads; |
| 68 } | 70 } |
| 69 | 71 |
| 72 // Maximum number of frame buffers allowed to be used by libvpx for VP9 | |
| 73 // decoding. | |
| 74 static const int kVP9MaxFrameBuffers = VP9_MAXIMUM_REF_BUFFERS + | |
| 75 VPX_MAXIMUM_WORK_BUFFERS + | |
| 76 limits::kMaxVideoFrames; | |
| 77 | |
| 78 class VpxVideoDecoder::MemoryPool | |
| 79 : public base::RefCountedThreadSafe<VpxVideoDecoder::MemoryPool> { | |
| 80 public: | |
| 81 MemoryPool(VpxVideoDecoder* decoder); | |
| 82 vpx_codec_frame_buffer* frame_buffers() { return frame_buffers_; } | |
| 83 size_t frame_buffers_size() const { return arraysize(frame_buffers_); } | |
| 84 | |
| 85 // Callback that will be called by libvpx if the frame buffer size needs to | |
| 86 // increase. Parameters: | |
| 87 // user_priv Data passed into libvpx (we pass NULL). | |
| 88 // new_size Minimum size needed by libvpx to decompress the next frame. | |
| 89 // fb Pointer to the frame buffer to update. | |
| 90 // Returns VPX_CODEC_OK on success. Returns < 0 on failure. | |
| 91 static int32 ReallocVP9FrameBuffer(void* user_priv, size_t new_size, | |
| 92 vpx_codec_frame_buffer* fb); | |
| 93 | |
| 94 // Generates a "no_longer_needed" closure that holds a reference | |
| 95 // to this pool. | |
| 96 base::Closure frame_callback(); | |
| 97 | |
| 98 // Checks if it is safe to call libvpx. Since libvpx uses LRU to reuse frame | |
| 99 // buffers, it is not safe to call libvpx if the "oldest" frame according to | |
| 100 // libvpx is still in use by chromium. | |
| 101 bool IsSafeToCallDecoder(); | |
| 102 | |
| 103 private: | |
| 104 friend class base::RefCountedThreadSafe<VpxVideoDecoder::MemoryPool>; | |
| 105 ~MemoryPool(); | |
| 106 | |
| 107 // Method that gets called when a VideoFrame that references this pool gets | |
| 108 // destroyed. | |
| 109 void OnVideoFrameDestroyed(uint64 frame_id); | |
| 110 | |
| 111 // Pointer to the VpxVideoDecoder object that has this object. | |
| 112 VpxVideoDecoder* decoder_; | |
| 113 | |
| 114 // Incrementing counter of frames passed into libvpx. | |
| 115 uint64 next_frame_id_; | |
| 116 | |
| 117 // List of frame ids currently in use by chromium. | |
| 118 std::set<uint64> frame_ids_in_use_; | |
| 119 | |
| 120 vpx_codec_frame_buffer frame_buffers_[kVP9MaxFrameBuffers]; | |
| 121 DISALLOW_COPY_AND_ASSIGN(MemoryPool); | |
| 122 }; | |
| 123 | |
| 124 VpxVideoDecoder::MemoryPool::MemoryPool(VpxVideoDecoder* decoder) | |
| 125 : decoder_(decoder), | |
| 126 next_frame_id_(0) { | |
| 127 memset(frame_buffers_, 0, | |
| 128 sizeof(vpx_codec_frame_buffer) * arraysize(frame_buffers_)); | |
|
acolwell GONE FROM CHROMIUM
2013/12/20 17:26:44
nit: I believe you should be able to just use size
vignesh
2013/12/20 19:59:40
I tried that before i used this, it did not work.
| |
| 129 } | |
| 130 | |
| 131 VpxVideoDecoder::MemoryPool::~MemoryPool() { | |
| 132 for (size_t i = 0; i < arraysize(frame_buffers_); ++i) | |
| 133 delete[] frame_buffers_[i].data; | |
| 134 } | |
| 135 | |
| 136 int32 VpxVideoDecoder::MemoryPool::ReallocVP9FrameBuffer( | |
| 137 void* user_priv, size_t new_size, vpx_codec_frame_buffer* fb) { | |
| 138 if (!fb) | |
| 139 return -1; | |
| 140 if (fb->data) | |
| 141 delete[] fb->data; | |
| 142 fb->data = new uint8[new_size]; | |
| 143 if (!fb->data) { | |
| 144 fb->size = 0; | |
| 145 return -1; | |
| 146 } | |
| 147 fb->size = new_size; | |
| 148 return VPX_CODEC_OK; | |
| 149 } | |
| 150 | |
| 151 base::Closure VpxVideoDecoder::MemoryPool::frame_callback() { | |
| 152 frame_ids_in_use_.insert(next_frame_id_); | |
| 153 return BindToCurrentLoop( | |
| 154 base::Bind(&MemoryPool::OnVideoFrameDestroyed, this, | |
| 155 next_frame_id_++)); | |
| 156 } | |
| 157 | |
| 158 void VpxVideoDecoder::MemoryPool::OnVideoFrameDestroyed(uint64 frame_id) { | |
| 159 frame_ids_in_use_.erase(frame_id); | |
| 160 if (decoder_->pending_buffer_ && IsSafeToCallDecoder()) { | |
|
acolwell GONE FROM CHROMIUM
2013/12/20 17:26:44
You'll need a decoder_ null check and a way for th
vignesh
2013/12/20 19:59:40
nice catch. Done.
| |
| 161 decoder_->DecodeBuffer(decoder_->pending_buffer_); | |
| 162 decoder_->pending_buffer_ = NULL; | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 bool VpxVideoDecoder::MemoryPool::IsSafeToCallDecoder() { | |
| 167 return next_frame_id_ < limits::kMaxVideoFrames || | |
| 168 frame_ids_in_use_.find(next_frame_id_ - limits::kMaxVideoFrames) == | |
| 169 frame_ids_in_use_.end(); | |
| 170 } | |
| 171 | |
| 70 VpxVideoDecoder::VpxVideoDecoder( | 172 VpxVideoDecoder::VpxVideoDecoder( |
| 71 const scoped_refptr<base::MessageLoopProxy>& message_loop) | 173 const scoped_refptr<base::MessageLoopProxy>& message_loop) |
| 72 : message_loop_(message_loop), | 174 : message_loop_(message_loop), |
| 73 weak_factory_(this), | 175 weak_factory_(this), |
| 74 state_(kUninitialized), | 176 state_(kUninitialized), |
| 75 vpx_codec_(NULL), | 177 vpx_codec_(NULL), |
| 76 vpx_codec_alpha_(NULL) { | 178 vpx_codec_alpha_(NULL), |
| 179 memory_pool_(NULL), | |
| 180 pending_buffer_(NULL) { | |
| 77 } | 181 } |
| 78 | 182 |
| 79 VpxVideoDecoder::~VpxVideoDecoder() { | 183 VpxVideoDecoder::~VpxVideoDecoder() { |
| 80 DCHECK_EQ(kUninitialized, state_); | 184 DCHECK_EQ(kUninitialized, state_); |
| 81 CloseDecoder(); | 185 CloseDecoder(); |
| 82 } | 186 } |
| 83 | 187 |
| 84 void VpxVideoDecoder::Initialize(const VideoDecoderConfig& config, | 188 void VpxVideoDecoder::Initialize(const VideoDecoderConfig& config, |
| 85 const PipelineStatusCB& status_cb) { | 189 const PipelineStatusCB& status_cb) { |
| 86 DCHECK(message_loop_->BelongsToCurrentThread()); | 190 DCHECK(message_loop_->BelongsToCurrentThread()); |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 135 } | 239 } |
| 136 if (!can_handle) | 240 if (!can_handle) |
| 137 return false; | 241 return false; |
| 138 | 242 |
| 139 CloseDecoder(); | 243 CloseDecoder(); |
| 140 | 244 |
| 141 vpx_codec_ = InitializeVpxContext(vpx_codec_, config); | 245 vpx_codec_ = InitializeVpxContext(vpx_codec_, config); |
| 142 if (!vpx_codec_) | 246 if (!vpx_codec_) |
| 143 return false; | 247 return false; |
| 144 | 248 |
| 249 // We use our own buffers for VP9 so that there is no need to copy data after | |
| 250 // decoding. | |
| 251 if (config.codec() == kCodecVP9) { | |
| 252 memory_pool_ = new MemoryPool(this); | |
| 253 if (vpx_codec_set_frame_buffers( | |
| 254 vpx_codec_, | |
| 255 memory_pool_->frame_buffers(), | |
| 256 memory_pool_->frame_buffers_size(), | |
| 257 &MemoryPool::ReallocVP9FrameBuffer, | |
| 258 NULL)) { | |
| 259 LOG(ERROR) << "Failed to configure external buffers."; | |
| 260 return false; | |
| 261 } | |
| 262 if (vpx_codec_control(vpx_codec_, VP9D_SET_FRAME_BUFFER_LRU_CACHE, 1)) { | |
| 263 LOG(ERROR) << "Failed to set frame buffer lru cache."; | |
| 264 return false; | |
| 265 } | |
| 266 } | |
| 267 | |
| 145 if (config.format() == VideoFrame::YV12A) { | 268 if (config.format() == VideoFrame::YV12A) { |
| 146 vpx_codec_alpha_ = InitializeVpxContext(vpx_codec_alpha_, config); | 269 vpx_codec_alpha_ = InitializeVpxContext(vpx_codec_alpha_, config); |
| 147 if (!vpx_codec_alpha_) | 270 if (!vpx_codec_alpha_) |
| 148 return false; | 271 return false; |
| 149 } | 272 } |
| 150 | 273 |
| 151 return true; | 274 return true; |
| 152 } | 275 } |
| 153 | 276 |
| 154 void VpxVideoDecoder::CloseDecoder() { | 277 void VpxVideoDecoder::CloseDecoder() { |
| 155 if (vpx_codec_) { | 278 if (vpx_codec_) { |
| 156 vpx_codec_destroy(vpx_codec_); | 279 vpx_codec_destroy(vpx_codec_); |
| 157 delete vpx_codec_; | 280 delete vpx_codec_; |
| 158 vpx_codec_ = NULL; | 281 vpx_codec_ = NULL; |
| 282 memory_pool_ = NULL; | |
|
acolwell GONE FROM CHROMIUM
2013/12/20 17:26:44
I think right before this line is where you need t
vignesh
2013/12/20 19:59:40
Done.
| |
| 283 pending_buffer_ = NULL; | |
| 159 } | 284 } |
| 160 if (vpx_codec_alpha_) { | 285 if (vpx_codec_alpha_) { |
| 161 vpx_codec_destroy(vpx_codec_alpha_); | 286 vpx_codec_destroy(vpx_codec_alpha_); |
| 162 delete vpx_codec_alpha_; | 287 delete vpx_codec_alpha_; |
| 163 vpx_codec_alpha_ = NULL; | 288 vpx_codec_alpha_ = NULL; |
| 164 } | 289 } |
| 165 } | 290 } |
| 166 | 291 |
| 167 void VpxVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, | 292 void VpxVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, |
| 168 const DecodeCB& decode_cb) { | 293 const DecodeCB& decode_cb) { |
| 169 DCHECK(message_loop_->BelongsToCurrentThread()); | 294 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 170 DCHECK(!decode_cb.is_null()); | 295 DCHECK(!decode_cb.is_null()); |
| 171 CHECK_NE(state_, kUninitialized); | 296 CHECK_NE(state_, kUninitialized); |
| 172 CHECK(decode_cb_.is_null()) << "Overlapping decodes are not supported."; | 297 CHECK(decode_cb_.is_null()) << "Overlapping decodes are not supported."; |
| 173 | 298 |
| 174 decode_cb_ = BindToCurrentLoop(decode_cb); | 299 decode_cb_ = BindToCurrentLoop(decode_cb); |
| 175 | 300 |
| 176 if (state_ == kError) { | 301 if (state_ == kError) { |
| 177 base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL); | 302 base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL); |
| 178 return; | 303 return; |
| 179 } | 304 } |
| 180 | 305 |
| 181 // Return empty frames if decoding has finished. | 306 // Return empty frames if decoding has finished. |
| 182 if (state_ == kDecodeFinished) { | 307 if (state_ == kDecodeFinished) { |
| 183 base::ResetAndReturn(&decode_cb_).Run(kOk, VideoFrame::CreateEOSFrame()); | 308 base::ResetAndReturn(&decode_cb_).Run(kOk, VideoFrame::CreateEOSFrame()); |
| 184 return; | 309 return; |
| 185 } | 310 } |
| 186 | 311 |
| 312 // Check if it is safe to call libvpx. | |
| 313 if (memory_pool_ && !memory_pool_->IsSafeToCallDecoder()) { | |
| 314 pending_buffer_ = buffer; | |
| 315 return; | |
| 316 } | |
| 317 | |
| 187 DecodeBuffer(buffer); | 318 DecodeBuffer(buffer); |
| 188 } | 319 } |
| 189 | 320 |
| 190 void VpxVideoDecoder::Reset(const base::Closure& closure) { | 321 void VpxVideoDecoder::Reset(const base::Closure& closure) { |
| 191 DCHECK(message_loop_->BelongsToCurrentThread()); | 322 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 192 DCHECK(reset_cb_.is_null()); | 323 DCHECK(reset_cb_.is_null()); |
| 193 reset_cb_ = BindToCurrentLoop(closure); | 324 reset_cb_ = BindToCurrentLoop(closure); |
| 194 | 325 |
| 195 // Defer the reset if a decode is pending. | 326 // Defer the reset if a decode is pending. |
| 196 if (!decode_cb_.is_null()) | 327 if (!decode_cb_.is_null()) |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 335 | 466 |
| 336 void VpxVideoDecoder::CopyVpxImageTo(const vpx_image* vpx_image, | 467 void VpxVideoDecoder::CopyVpxImageTo(const vpx_image* vpx_image, |
| 337 const struct vpx_image* vpx_image_alpha, | 468 const struct vpx_image* vpx_image_alpha, |
| 338 scoped_refptr<VideoFrame>* video_frame) { | 469 scoped_refptr<VideoFrame>* video_frame) { |
| 339 CHECK(vpx_image); | 470 CHECK(vpx_image); |
| 340 CHECK(vpx_image->fmt == VPX_IMG_FMT_I420 || | 471 CHECK(vpx_image->fmt == VPX_IMG_FMT_I420 || |
| 341 vpx_image->fmt == VPX_IMG_FMT_YV12); | 472 vpx_image->fmt == VPX_IMG_FMT_YV12); |
| 342 | 473 |
| 343 gfx::Size size(vpx_image->d_w, vpx_image->d_h); | 474 gfx::Size size(vpx_image->d_w, vpx_image->d_h); |
| 344 | 475 |
| 476 if (!vpx_codec_alpha_ && memory_pool_) { | |
| 477 *video_frame = VideoFrame::WrapExternalYuvData( | |
| 478 VideoFrame::YV12, | |
| 479 size, gfx::Rect(size), config_.natural_size(), | |
| 480 vpx_image->stride[VPX_PLANE_Y], | |
| 481 vpx_image->stride[VPX_PLANE_U], | |
| 482 vpx_image->stride[VPX_PLANE_V], | |
| 483 vpx_image->planes[VPX_PLANE_Y], | |
| 484 vpx_image->planes[VPX_PLANE_U], | |
| 485 vpx_image->planes[VPX_PLANE_V], | |
| 486 kNoTimestamp(), | |
| 487 memory_pool_->frame_callback()); | |
| 488 return; | |
| 489 } | |
| 490 | |
| 345 *video_frame = frame_pool_.CreateFrame( | 491 *video_frame = frame_pool_.CreateFrame( |
| 346 vpx_codec_alpha_ ? VideoFrame::YV12A : VideoFrame::YV12, | 492 vpx_codec_alpha_ ? VideoFrame::YV12A : VideoFrame::YV12, |
| 347 size, | 493 size, |
| 348 gfx::Rect(size), | 494 gfx::Rect(size), |
| 349 config_.natural_size(), | 495 config_.natural_size(), |
| 350 kNoTimestamp()); | 496 kNoTimestamp()); |
| 351 | 497 |
| 352 CopyYPlane(vpx_image->planes[VPX_PLANE_Y], | 498 CopyYPlane(vpx_image->planes[VPX_PLANE_Y], |
| 353 vpx_image->stride[VPX_PLANE_Y], | 499 vpx_image->stride[VPX_PLANE_Y], |
| 354 vpx_image->d_h, | 500 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()); | 514 vpx_image->stride[VPX_PLANE_Y], vpx_image->d_h, video_frame->get()); |
| 369 return; | 515 return; |
| 370 } | 516 } |
| 371 CopyAPlane(vpx_image_alpha->planes[VPX_PLANE_Y], | 517 CopyAPlane(vpx_image_alpha->planes[VPX_PLANE_Y], |
| 372 vpx_image->stride[VPX_PLANE_Y], | 518 vpx_image->stride[VPX_PLANE_Y], |
| 373 vpx_image->d_h, | 519 vpx_image->d_h, |
| 374 video_frame->get()); | 520 video_frame->get()); |
| 375 } | 521 } |
| 376 | 522 |
| 377 } // namespace media | 523 } // namespace media |
| OLD | NEW |