Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / common / gpu / media / vp9_decoder.cc
blob7c25832cdc12c89cd6895f6c56744cc620c3d0da
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
3 // found in the LICENSE file.
5 #include "base/logging.h"
6 #include "content/common/gpu/media/vp9_decoder.h"
7 #include "media/base/limits.h"
9 namespace content {
11 VP9Decoder::VP9Accelerator::VP9Accelerator() {}
13 VP9Decoder::VP9Accelerator::~VP9Accelerator() {}
15 VP9Decoder::VP9Decoder(VP9Accelerator* accelerator)
16 : state_(kNeedStreamMetadata), accelerator_(accelerator) {
17 DCHECK(accelerator_);
18 ref_frames_.resize(media::kVp9NumRefFrames);
21 VP9Decoder::~VP9Decoder() {}
23 void VP9Decoder::SetStream(const uint8_t* ptr, size_t size) {
24 DCHECK(ptr);
25 DCHECK(size);
27 DVLOG(4) << "New input stream at: " << (void*)ptr << " size: " << size;
28 parser_.SetStream(ptr, size);
31 bool VP9Decoder::Flush() {
32 DVLOG(2) << "Decoder flush";
33 Reset();
34 return true;
37 void VP9Decoder::Reset() {
38 curr_frame_hdr_ = nullptr;
39 for (auto& ref_frame : ref_frames_)
40 ref_frame = nullptr;
42 parser_.Reset();
44 if (state_ == kDecoding)
45 state_ = kAfterReset;
48 VP9Decoder::DecodeResult VP9Decoder::Decode() {
49 while (1) {
50 // Read a new frame header if one is not awaiting decoding already.
51 if (!curr_frame_hdr_) {
52 scoped_ptr<media::Vp9FrameHeader> hdr(new media::Vp9FrameHeader());
53 media::Vp9Parser::Result res = parser_.ParseNextFrame(hdr.get());
54 switch (res) {
55 case media::Vp9Parser::kOk:
56 curr_frame_hdr_.reset(hdr.release());
57 break;
59 case media::Vp9Parser::kEOStream:
60 return kRanOutOfStreamData;
62 case media::Vp9Parser::kInvalidStream:
63 DVLOG(1) << "Error parsing stream";
64 SetError();
65 return kDecodeError;
69 if (state_ != kDecoding) {
70 // Not kDecoding, so we need a resume point (a keyframe), as we are after
71 // reset or at the beginning of the stream. Drop anything that is not
72 // a keyframe in such case, and continue looking for a keyframe.
73 if (curr_frame_hdr_->IsKeyframe()) {
74 state_ = kDecoding;
75 } else {
76 curr_frame_hdr_.reset();
77 continue;
81 if (curr_frame_hdr_->show_existing_frame) {
82 // This frame header only instructs us to display one of the
83 // previously-decoded frames, but has no frame data otherwise. Display
84 // and continue decoding subsequent frames.
85 size_t frame_to_show = curr_frame_hdr_->frame_to_show;
86 if (frame_to_show >= ref_frames_.size() || !ref_frames_[frame_to_show]) {
87 DVLOG(1) << "Request to show an invalid frame";
88 SetError();
89 return kDecodeError;
92 if (!accelerator_->OutputPicture(ref_frames_[frame_to_show])) {
93 SetError();
94 return kDecodeError;
97 curr_frame_hdr_.reset();
98 continue;
101 gfx::Size new_pic_size(curr_frame_hdr_->width, curr_frame_hdr_->height);
102 DCHECK(!new_pic_size.IsEmpty());
104 if (new_pic_size != pic_size_) {
105 DVLOG(1) << "New resolution: " << new_pic_size.ToString();
107 if (!curr_frame_hdr_->IsKeyframe()) {
108 // TODO(posciak): This is doable, but requires a few modifications to
109 // VDA implementations to allow multiple picture buffer sets in flight.
110 DVLOG(1) << "Resolution change currently supported for keyframes only";
111 SetError();
112 return kDecodeError;
115 // TODO(posciak): This requires us to be on a keyframe (see above) and is
116 // required, because VDA clients expect all surfaces to be returned before
117 // they can cycle surface sets after receiving kAllocateNewSurfaces.
118 // This is only an implementation detail of VDAs and can be improved.
119 for (auto& ref_frame : ref_frames_)
120 ref_frame = nullptr;
122 pic_size_ = new_pic_size;
123 return kAllocateNewSurfaces;
126 scoped_refptr<VP9Picture> pic = accelerator_->CreateVP9Picture();
127 if (!pic)
128 return kRanOutOfSurfaces;
130 pic->frame_hdr.reset(curr_frame_hdr_.release());
132 if (!DecodeAndOutputPicture(pic)) {
133 SetError();
134 return kDecodeError;
139 void VP9Decoder::RefreshReferenceFrames(const scoped_refptr<VP9Picture>& pic) {
140 for (size_t i = 0; i < media::kVp9NumRefFrames; ++i) {
141 DCHECK_IMPLIES(pic->frame_hdr->IsKeyframe(),
142 pic->frame_hdr->RefreshFlag(i));
143 if (pic->frame_hdr->RefreshFlag(i))
144 ref_frames_[i] = pic;
148 bool VP9Decoder::DecodeAndOutputPicture(scoped_refptr<VP9Picture> pic) {
149 DCHECK(!pic_size_.IsEmpty());
150 DCHECK(pic->frame_hdr);
152 if (!accelerator_->SubmitDecode(pic, parser_.GetSegmentation(),
153 parser_.GetLoopFilter(), ref_frames_))
154 return false;
156 if (pic->frame_hdr->show_frame) {
157 if (!accelerator_->OutputPicture(pic))
158 return false;
161 RefreshReferenceFrames(pic);
162 return true;
165 void VP9Decoder::SetError() {
166 Reset();
167 state_ = kError;
170 gfx::Size VP9Decoder::GetPicSize() const {
171 return pic_size_;
174 size_t VP9Decoder::GetRequiredNumOfPictures() const {
175 // kMaxVideoFrames to keep higher level media pipeline populated, +2 for the
176 // pictures being parsed and decoded currently.
177 return media::limits::kMaxVideoFrames + media::kVp9NumRefFrames + 2;
180 } // namespace content