blob: f57d5adefe79fbc1b681cb866d4416d63bc67fa5 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/vp8_decoder.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "media/base/limits.h"
namespace media {
namespace {
constexpr size_t kVP8NumFramesActive = 4;
}
VP8Decoder::VP8Accelerator::VP8Accelerator() {}
VP8Decoder::VP8Accelerator::~VP8Accelerator() {}
VP8Decoder::VP8Decoder(std::unique_ptr<VP8Accelerator> accelerator)
: state_(kNeedStreamMetadata),
curr_frame_start_(nullptr),
frame_size_(0),
accelerator_(std::move(accelerator)) {
DCHECK(accelerator_);
}
VP8Decoder::~VP8Decoder() = default;
bool VP8Decoder::Flush() {
DVLOG(2) << "Decoder flush";
Reset();
return true;
}
void VP8Decoder::SetStream(int32_t id, const DecoderBuffer& decoder_buffer) {
const uint8_t* ptr = decoder_buffer.data();
const size_t size = decoder_buffer.data_size();
const DecryptConfig* decrypt_config = decoder_buffer.decrypt_config();
DCHECK(ptr);
DCHECK(size);
if (decrypt_config) {
NOTIMPLEMENTED();
state_ = kError;
return;
}
DVLOG(4) << "New input stream id: " << id << " at: " << (void*)ptr
<< " size: " << size;
stream_id_ = id;
curr_frame_start_ = ptr;
frame_size_ = size;
}
void VP8Decoder::Reset() {
curr_frame_hdr_ = nullptr;
curr_frame_start_ = nullptr;
frame_size_ = 0;
ref_frames_.Clear();
if (state_ == kDecoding)
state_ = kAfterReset;
}
VP8Decoder::DecodeResult VP8Decoder::Decode() {
if (!curr_frame_start_ || frame_size_ == 0)
return kRanOutOfStreamData;
if (!curr_frame_hdr_) {
curr_frame_hdr_.reset(new Vp8FrameHeader());
if (!parser_.ParseFrame(curr_frame_start_, frame_size_,
curr_frame_hdr_.get())) {
DVLOG(1) << "Error during decode";
state_ = kError;
return kDecodeError;
}
}
// The |stream_id_|s are expected to be monotonically increasing, and we've
// lost (at least) a frame if this condition doesn't uphold.
const bool have_skipped_frame = last_decoded_stream_id_ + 1 != stream_id_ &&
last_decoded_stream_id_ != kInvalidId;
if (curr_frame_hdr_->IsKeyframe()) {
const gfx::Size new_picture_size(curr_frame_hdr_->width,
curr_frame_hdr_->height);
if (new_picture_size.IsEmpty())
return kDecodeError;
if (new_picture_size != pic_size_) {
DVLOG(2) << "New resolution: " << new_picture_size.ToString();
pic_size_ = new_picture_size;
ref_frames_.Clear();
last_decoded_stream_id_ = stream_id_;
size_change_failure_counter_ = 0;
return kConfigChange;
}
state_ = kDecoding;
} else if (state_ != kDecoding || have_skipped_frame) {
// Only trust the next frame. Otherwise, new keyframe might be missed, so
// |pic_size_| might be stale.
// TODO(dshwang): if rtc decoder can know the size of inter frame, change
// this condition to check if new keyframe is missed.
// https://crbug.com/832545
DVLOG(4) << "Drop the frame because the size maybe stale.";
if (have_skipped_frame &&
++size_change_failure_counter_ > kVPxMaxNumOfSizeChangeFailures) {
state_ = kError;
return kDecodeError;
}
// Need a resume point.
curr_frame_hdr_ = nullptr;
return kRanOutOfStreamData;
}
scoped_refptr<VP8Picture> pic = accelerator_->CreateVP8Picture();
if (!pic)
return kRanOutOfSurfaces;
if (!DecodeAndOutputCurrentFrame(std::move(pic))) {
state_ = kError;
return kDecodeError;
}
last_decoded_stream_id_ = stream_id_;
size_change_failure_counter_ = 0;
return kRanOutOfStreamData;
}
bool VP8Decoder::DecodeAndOutputCurrentFrame(scoped_refptr<VP8Picture> pic) {
DCHECK(pic);
DCHECK(!pic_size_.IsEmpty());
DCHECK(curr_frame_hdr_);
pic->set_visible_rect(gfx::Rect(pic_size_));
pic->set_bitstream_id(stream_id_);
if (curr_frame_hdr_->IsKeyframe()) {
horizontal_scale_ = curr_frame_hdr_->horizontal_scale;
vertical_scale_ = curr_frame_hdr_->vertical_scale;
} else {
// Populate fields from decoder state instead.
curr_frame_hdr_->width = pic_size_.width();
curr_frame_hdr_->height = pic_size_.height();
curr_frame_hdr_->horizontal_scale = horizontal_scale_;
curr_frame_hdr_->vertical_scale = vertical_scale_;
}
const bool show_frame = curr_frame_hdr_->show_frame;
pic->frame_hdr = std::move(curr_frame_hdr_);
if (!accelerator_->SubmitDecode(pic, ref_frames_))
return false;
if (show_frame && !accelerator_->OutputPicture(pic))
return false;
ref_frames_.Refresh(pic);
curr_frame_start_ = nullptr;
frame_size_ = 0;
return true;
}
gfx::Size VP8Decoder::GetPicSize() const {
return pic_size_;
}
gfx::Rect VP8Decoder::GetVisibleRect() const {
return gfx::Rect(pic_size_);
}
VideoCodecProfile VP8Decoder::GetProfile() const {
return VP8PROFILE_ANY;
}
uint8_t VP8Decoder::GetBitDepth() const {
return 8u;
}
size_t VP8Decoder::GetRequiredNumOfPictures() const {
constexpr size_t kPicsInPipeline = limits::kMaxVideoFrames + 1;
return kVP8NumFramesActive + kPicsInPipeline;
}
size_t VP8Decoder::GetNumReferenceFrames() const {
// Maximum number of reference frames.
return kVP8NumFramesActive;
}
} // namespace media