| 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 "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback_helpers.h" | 8 #include "base/callback_helpers.h" |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/location.h" | 10 #include "base/location.h" |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 54 decode_threads = std::max(decode_threads, 0); | 54 decode_threads = std::max(decode_threads, 0); |
| 55 decode_threads = std::min(decode_threads, kMaxDecodeThreads); | 55 decode_threads = std::min(decode_threads, kMaxDecodeThreads); |
| 56 return decode_threads; | 56 return decode_threads; |
| 57 } | 57 } |
| 58 | 58 |
| 59 VpxVideoDecoder::VpxVideoDecoder( | 59 VpxVideoDecoder::VpxVideoDecoder( |
| 60 const scoped_refptr<base::MessageLoopProxy>& message_loop) | 60 const scoped_refptr<base::MessageLoopProxy>& message_loop) |
| 61 : message_loop_(message_loop), | 61 : message_loop_(message_loop), |
| 62 weak_factory_(this), | 62 weak_factory_(this), |
| 63 state_(kUninitialized), | 63 state_(kUninitialized), |
| 64 demuxer_stream_(NULL), | |
| 65 vpx_codec_(NULL), | 64 vpx_codec_(NULL), |
| 66 vpx_codec_alpha_(NULL) { | 65 vpx_codec_alpha_(NULL) { |
| 67 } | 66 } |
| 68 | 67 |
| 69 VpxVideoDecoder::~VpxVideoDecoder() { | 68 VpxVideoDecoder::~VpxVideoDecoder() { |
| 70 DCHECK_EQ(kUninitialized, state_); | 69 DCHECK_EQ(kUninitialized, state_); |
| 71 CloseDecoder(); | 70 CloseDecoder(); |
| 72 } | 71 } |
| 73 | 72 |
| 74 void VpxVideoDecoder::Initialize( | 73 void VpxVideoDecoder::Initialize(const VideoDecoderConfig& config, |
| 75 DemuxerStream* stream, | 74 const PipelineStatusCB& status_cb, |
| 76 const PipelineStatusCB& status_cb, | 75 const StatisticsCB& statistics_cb) { |
| 77 const StatisticsCB& statistics_cb) { | |
| 78 DCHECK(message_loop_->BelongsToCurrentThread()); | 76 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 79 DCHECK(stream); | 77 DCHECK(config.IsValidConfig()); |
| 78 DCHECK(!config.is_encrypted()); |
| 80 DCHECK(read_cb_.is_null()); | 79 DCHECK(read_cb_.is_null()); |
| 81 DCHECK(reset_cb_.is_null()); | 80 DCHECK(reset_cb_.is_null()); |
| 82 | 81 |
| 83 weak_this_ = weak_factory_.GetWeakPtr(); | 82 weak_this_ = weak_factory_.GetWeakPtr(); |
| 84 | 83 |
| 85 demuxer_stream_ = stream; | 84 if (!ConfigureDecoder(config)) { |
| 86 statistics_cb_ = statistics_cb; | |
| 87 | |
| 88 if (!ConfigureDecoder()) { | |
| 89 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | 85 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
| 90 return; | 86 return; |
| 91 } | 87 } |
| 92 | 88 |
| 93 // Success! | 89 // Success! |
| 90 config_ = config; |
| 91 statistics_cb_ = statistics_cb; |
| 94 state_ = kNormal; | 92 state_ = kNormal; |
| 95 status_cb.Run(PIPELINE_OK); | 93 status_cb.Run(PIPELINE_OK); |
| 96 } | 94 } |
| 97 | 95 |
| 98 static vpx_codec_ctx* InitializeVpxContext(vpx_codec_ctx* context, | 96 static vpx_codec_ctx* InitializeVpxContext(vpx_codec_ctx* context, |
| 99 const VideoDecoderConfig& config) { | 97 const VideoDecoderConfig& config) { |
| 100 context = new vpx_codec_ctx(); | 98 context = new vpx_codec_ctx(); |
| 101 vpx_codec_dec_cfg_t vpx_config = {0}; | 99 vpx_codec_dec_cfg_t vpx_config = {0}; |
| 102 vpx_config.w = config.coded_size().width(); | 100 vpx_config.w = config.coded_size().width(); |
| 103 vpx_config.h = config.coded_size().height(); | 101 vpx_config.h = config.coded_size().height(); |
| 104 vpx_config.threads = GetThreadCount(); | 102 vpx_config.threads = GetThreadCount(); |
| 105 | 103 |
| 106 vpx_codec_err_t status = vpx_codec_dec_init(context, | 104 vpx_codec_err_t status = vpx_codec_dec_init(context, |
| 107 config.codec() == kCodecVP9 ? | 105 config.codec() == kCodecVP9 ? |
| 108 vpx_codec_vp9_dx() : | 106 vpx_codec_vp9_dx() : |
| 109 vpx_codec_vp8_dx(), | 107 vpx_codec_vp8_dx(), |
| 110 &vpx_config, | 108 &vpx_config, |
| 111 0); | 109 0); |
| 112 if (status != VPX_CODEC_OK) { | 110 if (status != VPX_CODEC_OK) { |
| 113 LOG(ERROR) << "vpx_codec_dec_init failed, status=" << status; | 111 LOG(ERROR) << "vpx_codec_dec_init failed, status=" << status; |
| 114 delete context; | 112 delete context; |
| 115 return NULL; | 113 return NULL; |
| 116 } | 114 } |
| 117 return context; | 115 return context; |
| 118 } | 116 } |
| 119 | 117 |
| 120 bool VpxVideoDecoder::ConfigureDecoder() { | 118 bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config) { |
| 121 const VideoDecoderConfig& config = demuxer_stream_->video_decoder_config(); | |
| 122 DCHECK(config.IsValidConfig()); | |
| 123 | |
| 124 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | 119 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| 125 bool can_handle = false; | 120 bool can_handle = false; |
| 126 if (cmd_line->HasSwitch(switches::kEnableVp9Playback) && | 121 if (cmd_line->HasSwitch(switches::kEnableVp9Playback) && |
| 127 config.codec() == kCodecVP9) { | 122 config.codec() == kCodecVP9) { |
| 128 can_handle = true; | 123 can_handle = true; |
| 129 } | 124 } |
| 130 if (cmd_line->HasSwitch(switches::kEnableVp8AlphaPlayback) && | 125 if (cmd_line->HasSwitch(switches::kEnableVp8AlphaPlayback) && |
| 131 config.codec() == kCodecVP8 && config.format() == VideoFrame::YV12A) { | 126 config.codec() == kCodecVP8 && config.format() == VideoFrame::YV12A) { |
| 132 can_handle = true; | 127 can_handle = true; |
| 133 } | 128 } |
| (...skipping 21 matching lines...) Expand all Loading... |
| 155 delete vpx_codec_; | 150 delete vpx_codec_; |
| 156 vpx_codec_ = NULL; | 151 vpx_codec_ = NULL; |
| 157 } | 152 } |
| 158 if (vpx_codec_alpha_) { | 153 if (vpx_codec_alpha_) { |
| 159 vpx_codec_destroy(vpx_codec_alpha_); | 154 vpx_codec_destroy(vpx_codec_alpha_); |
| 160 delete vpx_codec_alpha_; | 155 delete vpx_codec_alpha_; |
| 161 vpx_codec_alpha_ = NULL; | 156 vpx_codec_alpha_ = NULL; |
| 162 } | 157 } |
| 163 } | 158 } |
| 164 | 159 |
| 165 void VpxVideoDecoder::Read(const ReadCB& read_cb) { | 160 void VpxVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, |
| 161 const ReadCB& read_cb) { |
| 166 DCHECK(message_loop_->BelongsToCurrentThread()); | 162 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 167 DCHECK(!read_cb.is_null()); | 163 DCHECK(!read_cb.is_null()); |
| 168 CHECK_NE(state_, kUninitialized); | 164 CHECK_NE(state_, kUninitialized); |
| 169 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | 165 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; |
| 166 |
| 170 read_cb_ = BindToCurrentLoop(read_cb); | 167 read_cb_ = BindToCurrentLoop(read_cb); |
| 171 | 168 |
| 172 if (state_ == kError) { | 169 if (state_ == kError) { |
| 173 read_cb.Run(kDecodeError, NULL); | 170 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); |
| 174 return; | 171 return; |
| 175 } | 172 } |
| 176 | 173 |
| 177 // Return empty frames if decoding has finished. | 174 // Return empty frames if decoding has finished. |
| 178 if (state_ == kDecodeFinished) { | 175 if (state_ == kDecodeFinished) { |
| 179 read_cb.Run(kOk, VideoFrame::CreateEmptyFrame()); | 176 base::ResetAndReturn(&read_cb_).Run(kOk, VideoFrame::CreateEmptyFrame()); |
| 180 return; | 177 return; |
| 181 } | 178 } |
| 182 | 179 |
| 183 ReadFromDemuxerStream(); | 180 DecodeBuffer(buffer); |
| 184 } | 181 } |
| 185 | 182 |
| 186 void VpxVideoDecoder::Reset(const base::Closure& closure) { | 183 void VpxVideoDecoder::Reset(const base::Closure& closure) { |
| 187 DCHECK(message_loop_->BelongsToCurrentThread()); | 184 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 188 DCHECK(reset_cb_.is_null()); | 185 DCHECK(reset_cb_.is_null()); |
| 189 reset_cb_ = BindToCurrentLoop(closure); | 186 reset_cb_ = BindToCurrentLoop(closure); |
| 190 | 187 |
| 191 // Defer the reset if a read is pending. | 188 // Defer the reset if a read is pending. |
| 192 if (!read_cb_.is_null()) | 189 if (!read_cb_.is_null()) |
| 193 return; | 190 return; |
| 194 | 191 |
| 195 DoReset(); | 192 DoReset(); |
| 196 } | 193 } |
| 197 | 194 |
| 198 void VpxVideoDecoder::Stop(const base::Closure& closure) { | 195 void VpxVideoDecoder::Stop(const base::Closure& closure) { |
| 199 DCHECK(message_loop_->BelongsToCurrentThread()); | 196 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 200 | 197 |
| 201 if (state_ == kUninitialized) { | 198 if (state_ == kUninitialized) { |
| 202 closure.Run(); | 199 closure.Run(); |
| 203 return; | 200 return; |
| 204 } | 201 } |
| 205 | 202 |
| 206 if (!read_cb_.is_null()) | 203 if (!read_cb_.is_null()) |
| 207 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | 204 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
| 208 | 205 |
| 209 state_ = kUninitialized; | 206 state_ = kUninitialized; |
| 210 closure.Run(); | 207 closure.Run(); |
| 211 } | 208 } |
| 212 | 209 |
| 213 void VpxVideoDecoder::ReadFromDemuxerStream() { | 210 void VpxVideoDecoder::DecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer) { |
| 214 DCHECK_NE(state_, kUninitialized); | |
| 215 DCHECK_NE(state_, kDecodeFinished); | |
| 216 DCHECK_NE(state_, kError); | |
| 217 DCHECK(!read_cb_.is_null()); | |
| 218 | |
| 219 demuxer_stream_->Read(base::Bind( | |
| 220 &VpxVideoDecoder::DoDecryptOrDecodeBuffer, weak_this_)); | |
| 221 } | |
| 222 | |
| 223 void VpxVideoDecoder::DoDecryptOrDecodeBuffer( | |
| 224 DemuxerStream::Status status, | |
| 225 const scoped_refptr<DecoderBuffer>& buffer) { | |
| 226 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 227 DCHECK_NE(state_, kDecodeFinished); | |
| 228 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; | |
| 229 | |
| 230 if (state_ == kUninitialized) | |
| 231 return; | |
| 232 | |
| 233 DCHECK(!read_cb_.is_null()); | |
| 234 | |
| 235 if (!reset_cb_.is_null()) { | |
| 236 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
| 237 DoReset(); | |
| 238 return; | |
| 239 } | |
| 240 | |
| 241 if (status == DemuxerStream::kAborted) { | |
| 242 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | |
| 243 return; | |
| 244 } | |
| 245 | |
| 246 // VideoFrameStream ensures no kConfigChanged is passed to VideoDecoders. | |
| 247 DCHECK_EQ(status, DemuxerStream::kOk) << status; | |
| 248 DecodeBuffer(buffer); | |
| 249 } | |
| 250 | |
| 251 void VpxVideoDecoder::DecodeBuffer( | |
| 252 const scoped_refptr<DecoderBuffer>& buffer) { | |
| 253 DCHECK(message_loop_->BelongsToCurrentThread()); | 211 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 254 DCHECK_NE(state_, kUninitialized); | 212 DCHECK_NE(state_, kUninitialized); |
| 255 DCHECK_NE(state_, kDecodeFinished); | 213 DCHECK_NE(state_, kDecodeFinished); |
| 256 DCHECK_NE(state_, kError); | 214 DCHECK_NE(state_, kError); |
| 257 DCHECK(reset_cb_.is_null()); | 215 DCHECK(reset_cb_.is_null()); |
| 258 DCHECK(!read_cb_.is_null()); | 216 DCHECK(!read_cb_.is_null()); |
| 259 DCHECK(buffer); | |
| 260 | 217 |
| 261 // Transition to kDecodeFinished on the first end of stream buffer. | 218 // Transition to kDecodeFinished on the first end of stream buffer. |
| 262 if (state_ == kNormal && buffer->IsEndOfStream()) { | 219 if (state_ == kNormal && buffer->IsEndOfStream()) { |
| 263 state_ = kDecodeFinished; | 220 state_ = kDecodeFinished; |
| 264 base::ResetAndReturn(&read_cb_).Run(kOk, VideoFrame::CreateEmptyFrame()); | 221 base::ResetAndReturn(&read_cb_).Run(kOk, VideoFrame::CreateEmptyFrame()); |
| 265 return; | 222 return; |
| 266 } | 223 } |
| 267 | 224 |
| 268 scoped_refptr<VideoFrame> video_frame; | 225 scoped_refptr<VideoFrame> video_frame; |
| 269 if (!Decode(buffer, &video_frame)) { | 226 if (!VpxDecode(buffer, &video_frame)) { |
| 270 state_ = kError; | 227 state_ = kError; |
| 271 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | 228 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); |
| 272 return; | 229 return; |
| 273 } | 230 } |
| 274 | 231 |
| 275 // Any successful decode counts! | 232 // Any successful decode counts! |
| 276 if (buffer->GetDataSize() && buffer->GetSideDataSize()) { | 233 if (buffer->GetDataSize() && buffer->GetSideDataSize()) { |
| 277 PipelineStatistics statistics; | 234 PipelineStatistics statistics; |
| 278 statistics.video_bytes_decoded = buffer->GetDataSize(); | 235 statistics.video_bytes_decoded = buffer->GetDataSize(); |
| 279 statistics_cb_.Run(statistics); | 236 statistics_cb_.Run(statistics); |
| 280 } | 237 } |
| 281 | 238 |
| 282 // If we didn't get a frame we need more data. | |
| 283 if (!video_frame) { | 239 if (!video_frame) { |
| 284 ReadFromDemuxerStream(); | 240 base::ResetAndReturn(&read_cb_).Run(kNotEnoughData, NULL); |
| 285 return; | 241 return; |
| 286 } | 242 } |
| 287 | 243 |
| 288 base::ResetAndReturn(&read_cb_).Run(kOk, video_frame); | 244 base::ResetAndReturn(&read_cb_).Run(kOk, video_frame); |
| 289 } | 245 } |
| 290 | 246 |
| 291 bool VpxVideoDecoder::Decode( | 247 bool VpxVideoDecoder::VpxDecode(const scoped_refptr<DecoderBuffer>& buffer, |
| 292 const scoped_refptr<DecoderBuffer>& buffer, | 248 scoped_refptr<VideoFrame>* video_frame) { |
| 293 scoped_refptr<VideoFrame>* video_frame) { | |
| 294 DCHECK(video_frame); | 249 DCHECK(video_frame); |
| 295 DCHECK(!buffer->IsEndOfStream()); | 250 DCHECK(!buffer->IsEndOfStream()); |
| 296 | 251 |
| 297 // Pass |buffer| to libvpx. | 252 // Pass |buffer| to libvpx. |
| 298 int64 timestamp = buffer->GetTimestamp().InMicroseconds(); | 253 int64 timestamp = buffer->GetTimestamp().InMicroseconds(); |
| 299 void* user_priv = reinterpret_cast<void*>(×tamp); | 254 void* user_priv = reinterpret_cast<void*>(×tamp); |
| 300 vpx_codec_err_t status = vpx_codec_decode(vpx_codec_, | 255 vpx_codec_err_t status = vpx_codec_decode(vpx_codec_, |
| 301 buffer->GetData(), | 256 buffer->GetData(), |
| 302 buffer->GetDataSize(), | 257 buffer->GetDataSize(), |
| 303 user_priv, | 258 user_priv, |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 363 } | 318 } |
| 364 | 319 |
| 365 void VpxVideoDecoder::DoReset() { | 320 void VpxVideoDecoder::DoReset() { |
| 366 DCHECK(read_cb_.is_null()); | 321 DCHECK(read_cb_.is_null()); |
| 367 | 322 |
| 368 state_ = kNormal; | 323 state_ = kNormal; |
| 369 reset_cb_.Run(); | 324 reset_cb_.Run(); |
| 370 reset_cb_.Reset(); | 325 reset_cb_.Reset(); |
| 371 } | 326 } |
| 372 | 327 |
| 373 void VpxVideoDecoder::CopyVpxImageTo( | 328 void VpxVideoDecoder::CopyVpxImageTo(const vpx_image* vpx_image, |
| 374 const vpx_image* vpx_image, | 329 const struct vpx_image* vpx_image_alpha, |
| 375 const struct vpx_image* vpx_image_alpha, | 330 scoped_refptr<VideoFrame>* video_frame) { |
| 376 scoped_refptr<VideoFrame>* video_frame) { | |
| 377 CHECK(vpx_image); | 331 CHECK(vpx_image); |
| 378 CHECK_EQ(vpx_image->d_w % 2, 0U); | 332 CHECK_EQ(vpx_image->d_w % 2, 0U); |
| 379 CHECK_EQ(vpx_image->d_h % 2, 0U); | 333 CHECK_EQ(vpx_image->d_h % 2, 0U); |
| 380 CHECK(vpx_image->fmt == VPX_IMG_FMT_I420 || | 334 CHECK(vpx_image->fmt == VPX_IMG_FMT_I420 || |
| 381 vpx_image->fmt == VPX_IMG_FMT_YV12); | 335 vpx_image->fmt == VPX_IMG_FMT_YV12); |
| 382 | 336 |
| 383 gfx::Size size(vpx_image->d_w, vpx_image->d_h); | 337 gfx::Size size(vpx_image->d_w, vpx_image->d_h); |
| 384 gfx::Size natural_size = | |
| 385 demuxer_stream_->video_decoder_config().natural_size(); | |
| 386 | 338 |
| 387 *video_frame = VideoFrame::CreateFrame(vpx_codec_alpha_ ? | 339 *video_frame = VideoFrame::CreateFrame( |
| 388 VideoFrame::YV12A : | 340 vpx_codec_alpha_ ? VideoFrame::YV12A : VideoFrame::YV12, |
| 389 VideoFrame::YV12, | 341 size, |
| 390 size, | 342 gfx::Rect(size), |
| 391 gfx::Rect(size), | 343 config_.natural_size(), |
| 392 natural_size, | 344 kNoTimestamp()); |
| 393 kNoTimestamp()); | |
| 394 | 345 |
| 395 CopyYPlane(vpx_image->planes[VPX_PLANE_Y], | 346 CopyYPlane(vpx_image->planes[VPX_PLANE_Y], |
| 396 vpx_image->stride[VPX_PLANE_Y], | 347 vpx_image->stride[VPX_PLANE_Y], |
| 397 vpx_image->d_h, | 348 vpx_image->d_h, |
| 398 *video_frame); | 349 *video_frame); |
| 399 CopyUPlane(vpx_image->planes[VPX_PLANE_U], | 350 CopyUPlane(vpx_image->planes[VPX_PLANE_U], |
| 400 vpx_image->stride[VPX_PLANE_U], | 351 vpx_image->stride[VPX_PLANE_U], |
| 401 vpx_image->d_h / 2, | 352 vpx_image->d_h / 2, |
| 402 *video_frame); | 353 *video_frame); |
| 403 CopyVPlane(vpx_image->planes[VPX_PLANE_V], | 354 CopyVPlane(vpx_image->planes[VPX_PLANE_V], |
| 404 vpx_image->stride[VPX_PLANE_V], | 355 vpx_image->stride[VPX_PLANE_V], |
| 405 vpx_image->d_h / 2, | 356 vpx_image->d_h / 2, |
| 406 *video_frame); | 357 *video_frame); |
| 358 |
| 407 if (!vpx_codec_alpha_) | 359 if (!vpx_codec_alpha_) |
| 408 return; | 360 return; |
| 361 |
| 409 if (!vpx_image_alpha) { | 362 if (!vpx_image_alpha) { |
| 410 MakeOpaqueAPlane(vpx_image->stride[VPX_PLANE_Y], vpx_image->d_h, | 363 MakeOpaqueAPlane( |
| 411 *video_frame); | 364 vpx_image->stride[VPX_PLANE_Y], vpx_image->d_h, *video_frame); |
| 412 return; | 365 return; |
| 413 } | 366 } |
| 367 |
| 414 CopyAPlane(vpx_image_alpha->planes[VPX_PLANE_Y], | 368 CopyAPlane(vpx_image_alpha->planes[VPX_PLANE_Y], |
| 415 vpx_image->stride[VPX_PLANE_Y], | 369 vpx_image->stride[VPX_PLANE_Y], |
| 416 vpx_image->d_h, | 370 vpx_image->d_h, |
| 417 *video_frame); | 371 *video_frame); |
| 418 } | 372 } |
| 419 | 373 |
| 420 } // namespace media | 374 } // namespace media |
| OLD | NEW |