• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "vp9_decoder.h"
6 
7 #include <memory>
8 
9 #include "base/bind.h"
10 #include "base/logging.h"
11 
12 namespace media {
13 
VP9Accelerator()14 VP9Decoder::VP9Accelerator::VP9Accelerator() {}
15 
~VP9Accelerator()16 VP9Decoder::VP9Accelerator::~VP9Accelerator() {}
17 
VP9Decoder(VP9Accelerator * accelerator)18 VP9Decoder::VP9Decoder(VP9Accelerator* accelerator)
19     : state_(kNeedStreamMetadata),
20       accelerator_(accelerator),
21       parser_(accelerator->IsFrameContextRequired()) {
22   ref_frames_.resize(kVp9NumRefFrames);
23 }
24 
~VP9Decoder()25 VP9Decoder::~VP9Decoder() {}
26 
SetStream(const uint8_t * ptr,size_t size)27 void VP9Decoder::SetStream(const uint8_t* ptr, size_t size) {
28   DCHECK(ptr);
29   DCHECK(size);
30 
31   DVLOG(4) << "New input stream at: " << (void*)ptr << " size: " << size;
32   parser_.SetStream(ptr, size);
33 }
34 
Flush()35 bool VP9Decoder::Flush() {
36   DVLOG(2) << "Decoder flush";
37   Reset();
38   return true;
39 }
40 
Reset()41 void VP9Decoder::Reset() {
42   curr_frame_hdr_ = nullptr;
43   for (auto& ref_frame : ref_frames_)
44     ref_frame = nullptr;
45 
46   parser_.Reset();
47 
48   if (state_ == kDecoding)
49     state_ = kAfterReset;
50 }
51 
Decode()52 VP9Decoder::DecodeResult VP9Decoder::Decode() {
53   while (1) {
54     // Read a new frame header if one is not awaiting decoding already.
55     if (!curr_frame_hdr_) {
56       std::unique_ptr<Vp9FrameHeader> hdr(new Vp9FrameHeader());
57       Vp9Parser::Result res = parser_.ParseNextFrame(hdr.get());
58       switch (res) {
59         case Vp9Parser::kOk:
60           curr_frame_hdr_ = std::move(hdr);
61           break;
62 
63         case Vp9Parser::kEOStream:
64           return kRanOutOfStreamData;
65 
66         case Vp9Parser::kInvalidStream:
67           DVLOG(1) << "Error parsing stream";
68           SetError();
69           return kDecodeError;
70 
71         case Vp9Parser::kAwaitingRefresh:
72           DVLOG(4) << "Awaiting context update";
73           return kNeedContextUpdate;
74       }
75     }
76 
77     if (state_ != kDecoding) {
78       // Not kDecoding, so we need a resume point (a keyframe), as we are after
79       // reset or at the beginning of the stream. Drop anything that is not
80       // a keyframe in such case, and continue looking for a keyframe.
81       if (curr_frame_hdr_->IsKeyframe()) {
82         state_ = kDecoding;
83       } else {
84         curr_frame_hdr_.reset();
85         continue;
86       }
87     }
88 
89     if (curr_frame_hdr_->show_existing_frame) {
90       // This frame header only instructs us to display one of the
91       // previously-decoded frames, but has no frame data otherwise. Display
92       // and continue decoding subsequent frames.
93       size_t frame_to_show = curr_frame_hdr_->frame_to_show_map_idx;
94       if (frame_to_show >= ref_frames_.size() || !ref_frames_[frame_to_show]) {
95         DVLOG(1) << "Request to show an invalid frame";
96         SetError();
97         return kDecodeError;
98       }
99 
100       if (!accelerator_->OutputPicture(ref_frames_[frame_to_show])) {
101         SetError();
102         return kDecodeError;
103       }
104 
105       curr_frame_hdr_.reset();
106       continue;
107     }
108 
109     Size new_pic_size(curr_frame_hdr_->frame_width,
110                            curr_frame_hdr_->frame_height);
111     DCHECK(!new_pic_size.IsEmpty());
112 
113     if (new_pic_size != pic_size_) {
114       DVLOG(1) << "New resolution: " << new_pic_size.ToString();
115 
116       if (!curr_frame_hdr_->IsKeyframe()) {
117         // TODO(posciak): This is doable, but requires a few modifications to
118         // VDA implementations to allow multiple picture buffer sets in flight.
119         DVLOG(1) << "Resolution change currently supported for keyframes only";
120         SetError();
121         return kDecodeError;
122       }
123 
124       // TODO(posciak): This requires us to be on a keyframe (see above) and is
125       // required, because VDA clients expect all surfaces to be returned before
126       // they can cycle surface sets after receiving kAllocateNewSurfaces.
127       // This is only an implementation detail of VDAs and can be improved.
128       for (auto& ref_frame : ref_frames_)
129         ref_frame = nullptr;
130 
131       pic_size_ = new_pic_size;
132       return kAllocateNewSurfaces;
133     }
134 
135     scoped_refptr<VP9Picture> pic = accelerator_->CreateVP9Picture();
136     if (!pic)
137       return kRanOutOfSurfaces;
138 
139     pic->frame_hdr.reset(curr_frame_hdr_.release());
140 
141     if (!DecodeAndOutputPicture(pic)) {
142       SetError();
143       return kDecodeError;
144     }
145   }
146 }
147 
RefreshReferenceFrames(const scoped_refptr<VP9Picture> & pic)148 void VP9Decoder::RefreshReferenceFrames(const scoped_refptr<VP9Picture>& pic) {
149   for (size_t i = 0; i < kVp9NumRefFrames; ++i) {
150     DCHECK(!pic->frame_hdr->IsKeyframe() || pic->frame_hdr->RefreshFlag(i));
151     if (pic->frame_hdr->RefreshFlag(i))
152       ref_frames_[i] = pic;
153   }
154 }
155 
UpdateFrameContext(const scoped_refptr<VP9Picture> & pic,const base::Callback<void (const Vp9FrameContext &)> & context_refresh_cb)156 void VP9Decoder::UpdateFrameContext(
157     const scoped_refptr<VP9Picture>& pic,
158     const base::Callback<void(const Vp9FrameContext&)>& context_refresh_cb) {
159   DCHECK(!context_refresh_cb.is_null());
160   Vp9FrameContext frame_ctx;
161   memset(&frame_ctx, 0, sizeof(frame_ctx));
162 
163   if (!accelerator_->GetFrameContext(pic, &frame_ctx)) {
164     SetError();
165     return;
166   }
167 
168   context_refresh_cb.Run(frame_ctx);
169 }
170 
DecodeAndOutputPicture(scoped_refptr<VP9Picture> pic)171 bool VP9Decoder::DecodeAndOutputPicture(scoped_refptr<VP9Picture> pic) {
172   DCHECK(!pic_size_.IsEmpty());
173   DCHECK(pic->frame_hdr);
174 
175   base::Closure done_cb;
176   const auto& context_refresh_cb =
177       parser_.GetContextRefreshCb(pic->frame_hdr->frame_context_idx);
178   if (!context_refresh_cb.is_null())
179     done_cb = base::Bind(&VP9Decoder::UpdateFrameContext,
180                          base::Unretained(this), pic, context_refresh_cb);
181 
182   const Vp9Parser::Context& context = parser_.context();
183   if (!accelerator_->SubmitDecode(pic, context.segmentation(),
184                                   context.loop_filter(), ref_frames_, done_cb))
185     return false;
186 
187   if (pic->frame_hdr->show_frame) {
188     if (!accelerator_->OutputPicture(pic))
189       return false;
190   }
191 
192   RefreshReferenceFrames(pic);
193   return true;
194 }
195 
SetError()196 void VP9Decoder::SetError() {
197   Reset();
198   state_ = kError;
199 }
200 
GetPicSize() const201 Size VP9Decoder::GetPicSize() const {
202   return pic_size_;
203 }
204 
GetRequiredNumOfPictures() const205 size_t VP9Decoder::GetRequiredNumOfPictures() const {
206   // kMaxVideoFrames to keep higher level media pipeline populated, +2 for the
207   // pictures being parsed and decoded currently.
208   // TODO(johnylin): see if we could get rid of kMaxVideoFrames.
209   const size_t kMaxVideoFrames = 4;
210   return kMaxVideoFrames + kVp9NumRefFrames + 2;
211 }
212 
213 }  // namespace media
214