• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 /*
21  * Copyright 2015 The Chromium Authors. All rights reserved.
22  *
23  * Redistribution and use in source and binary forms, with or without
24  * modification, are permitted provided that the following conditions are
25  * met:
26  *
27  *    * Redistributions of source code must retain the above copyright
28  * notice, this list of conditions and the following disclaimer.
29  *    * Redistributions in binary form must reproduce the above
30  * copyright notice, this list of conditions and the following disclaimer
31  * in the documentation and/or other materials provided with the
32  * distribution.
33  *    * Neither the name of Google Inc. nor the names of its
34  * contributors may be used to endorse or promote products derived from
35  * this software without specific prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
38  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
39  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
40  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
41  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
44  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
45  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49 /**
50  * SECTION:gstvp9decoder
51  * @title: Gstvp9Decoder
52  * @short_description: Base class to implement stateless VP9 decoders
53  * @sources:
54  * - gstvp9picture.h
55  */
56 
57 #ifdef HAVE_CONFIG_H
58 #include <config.h>
59 #endif
60 
61 #include <gst/base/base.h>
62 #include "gstvp9decoder.h"
63 
64 GST_DEBUG_CATEGORY (gst_vp9_decoder_debug);
65 #define GST_CAT_DEFAULT gst_vp9_decoder_debug
66 
67 struct _GstVp9DecoderPrivate
68 {
69   gint frame_width;
70   gint frame_height;
71   gint render_width;
72   gint render_height;
73   GstVP9Profile profile;
74 
75   gboolean had_sequence;
76 
77   GstVp9StatefulParser *parser;
78   GstVp9Dpb *dpb;
79 
80   gboolean support_non_kf_change;
81 
82   gboolean wait_keyframe;
83   /* controls how many frames to delay when calling output_picture() */
84   guint preferred_output_delay;
85   GstQueueArray *output_queue;
86   gboolean is_live;
87 };
88 
89 typedef struct
90 {
91   GstVideoCodecFrame *frame;
92   GstVp9Picture *picture;
93   GstVp9Decoder *self;
94 } GstVp9DecoderOutputFrame;
95 
96 #define parent_class gst_vp9_decoder_parent_class
97 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstVp9Decoder, gst_vp9_decoder,
98     GST_TYPE_VIDEO_DECODER,
99     G_ADD_PRIVATE (GstVp9Decoder);
100     GST_DEBUG_CATEGORY_INIT (gst_vp9_decoder_debug, "vp9decoder", 0,
101         "VP9 Video Decoder"));
102 
103 static gboolean gst_vp9_decoder_start (GstVideoDecoder * decoder);
104 static gboolean gst_vp9_decoder_stop (GstVideoDecoder * decoder);
105 static gboolean gst_vp9_decoder_set_format (GstVideoDecoder * decoder,
106     GstVideoCodecState * state);
107 static GstFlowReturn gst_vp9_decoder_finish (GstVideoDecoder * decoder);
108 static gboolean gst_vp9_decoder_flush (GstVideoDecoder * decoder);
109 static GstFlowReturn gst_vp9_decoder_drain (GstVideoDecoder * decoder);
110 static GstFlowReturn gst_vp9_decoder_handle_frame (GstVideoDecoder * decoder,
111     GstVideoCodecFrame * frame);
112 
113 static void
114 gst_vp9_decoder_clear_output_frame (GstVp9DecoderOutputFrame * output_frame);
115 static void gst_vp9_decoder_drain_output_queue (GstVp9Decoder * self,
116     guint num, GstFlowReturn * ret);
117 static GstFlowReturn gst_vp9_decoder_drain_internal (GstVp9Decoder * self,
118     gboolean wait_keyframe);
119 
120 static void
gst_vp9_decoder_class_init(GstVp9DecoderClass * klass)121 gst_vp9_decoder_class_init (GstVp9DecoderClass * klass)
122 {
123   GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
124 
125   decoder_class->start = GST_DEBUG_FUNCPTR (gst_vp9_decoder_start);
126   decoder_class->stop = GST_DEBUG_FUNCPTR (gst_vp9_decoder_stop);
127   decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_vp9_decoder_set_format);
128   decoder_class->finish = GST_DEBUG_FUNCPTR (gst_vp9_decoder_finish);
129   decoder_class->flush = GST_DEBUG_FUNCPTR (gst_vp9_decoder_flush);
130   decoder_class->drain = GST_DEBUG_FUNCPTR (gst_vp9_decoder_drain);
131   decoder_class->handle_frame =
132       GST_DEBUG_FUNCPTR (gst_vp9_decoder_handle_frame);
133 }
134 
135 static void
gst_vp9_decoder_init(GstVp9Decoder * self)136 gst_vp9_decoder_init (GstVp9Decoder * self)
137 {
138   gst_video_decoder_set_packetized (GST_VIDEO_DECODER (self), TRUE);
139 
140   self->priv = gst_vp9_decoder_get_instance_private (self);
141 
142   /* Assume subclass can support non-keyframe format change by default */
143   self->priv->support_non_kf_change = TRUE;
144 }
145 
146 static gboolean
gst_vp9_decoder_start(GstVideoDecoder * decoder)147 gst_vp9_decoder_start (GstVideoDecoder * decoder)
148 {
149   GstVp9Decoder *self = GST_VP9_DECODER (decoder);
150   GstVp9DecoderPrivate *priv = self->priv;
151 
152   priv->parser = gst_vp9_stateful_parser_new ();
153   priv->dpb = gst_vp9_dpb_new ();
154   priv->wait_keyframe = TRUE;
155   priv->profile = GST_VP9_PROFILE_UNDEFINED;
156   priv->frame_width = 0;
157   priv->frame_height = 0;
158   priv->render_width = 0;
159   priv->render_height = 0;
160 
161   priv->output_queue =
162       gst_queue_array_new_for_struct (sizeof (GstVp9DecoderOutputFrame), 1);
163   gst_queue_array_set_clear_func (priv->output_queue,
164       (GDestroyNotify) gst_vp9_decoder_clear_output_frame);
165 
166   return TRUE;
167 }
168 
169 static gboolean
gst_vp9_decoder_stop(GstVideoDecoder * decoder)170 gst_vp9_decoder_stop (GstVideoDecoder * decoder)
171 {
172   GstVp9Decoder *self = GST_VP9_DECODER (decoder);
173   GstVp9DecoderPrivate *priv = self->priv;
174 
175   g_clear_pointer (&self->input_state, gst_video_codec_state_unref);
176   g_clear_pointer (&priv->parser, gst_vp9_stateful_parser_free);
177   g_clear_pointer (&priv->dpb, gst_vp9_dpb_free);
178   gst_queue_array_free (priv->output_queue);
179 
180   return TRUE;
181 }
182 
183 static gboolean
gst_vp9_decoder_is_format_change(GstVp9Decoder * self,const GstVp9FrameHeader * frame_hdr)184 gst_vp9_decoder_is_format_change (GstVp9Decoder * self,
185     const GstVp9FrameHeader * frame_hdr)
186 {
187   GstVp9DecoderPrivate *priv = self->priv;
188 
189   if (priv->frame_width != frame_hdr->width
190       || priv->frame_height != frame_hdr->height) {
191     GST_INFO_OBJECT (self, "frame resolution changed %dx%d", frame_hdr->width,
192         frame_hdr->height);
193     return TRUE;
194   }
195 
196   if (priv->render_width != frame_hdr->render_width
197       || priv->render_height != frame_hdr->render_height) {
198     GST_INFO_OBJECT (self, "render resolution changed %dx%d",
199         frame_hdr->render_width, frame_hdr->render_height);
200     return TRUE;
201   }
202 
203   if (priv->profile != frame_hdr->profile) {
204     GST_INFO_OBJECT (self, "profile changed %d", frame_hdr->profile);
205     return TRUE;
206   }
207 
208   return FALSE;
209 }
210 
211 static GstFlowReturn
gst_vp9_decoder_check_codec_change(GstVp9Decoder * self,const GstVp9FrameHeader * frame_hdr)212 gst_vp9_decoder_check_codec_change (GstVp9Decoder * self,
213     const GstVp9FrameHeader * frame_hdr)
214 {
215   GstVp9DecoderPrivate *priv = self->priv;
216   GstVp9DecoderClass *klass = GST_VP9_DECODER_GET_CLASS (self);
217   GstFlowReturn ret = GST_FLOW_OK;
218 
219   if (priv->had_sequence && !gst_vp9_decoder_is_format_change (self, frame_hdr)) {
220     return GST_FLOW_OK;
221   }
222 
223   priv->frame_width = frame_hdr->width;
224   priv->frame_height = frame_hdr->height;
225   priv->render_width = frame_hdr->render_width;
226   priv->render_height = frame_hdr->render_height;
227   priv->profile = frame_hdr->profile;
228 
229   /* Drain before new sequence */
230   ret = gst_vp9_decoder_drain_internal (self, FALSE);
231   if (ret != GST_FLOW_OK) {
232     GST_WARNING_OBJECT (self, "Failed to drain pending frames, returned %s",
233         gst_flow_get_name (ret));
234     return ret;
235   }
236 
237   priv->had_sequence = TRUE;
238 
239   if (klass->get_preferred_output_delay) {
240     priv->preferred_output_delay =
241         klass->get_preferred_output_delay (self, priv->is_live);
242   } else {
243     priv->preferred_output_delay = 0;
244   }
245 
246   if (klass->new_sequence)
247     ret = klass->new_sequence (self, frame_hdr);
248 
249   if (ret != GST_FLOW_OK)
250     priv->had_sequence = FALSE;
251 
252   return ret;
253 }
254 
255 static gboolean
gst_vp9_decoder_set_format(GstVideoDecoder * decoder,GstVideoCodecState * state)256 gst_vp9_decoder_set_format (GstVideoDecoder * decoder,
257     GstVideoCodecState * state)
258 {
259   GstVp9Decoder *self = GST_VP9_DECODER (decoder);
260   GstVp9DecoderPrivate *priv = self->priv;
261   GstQuery *query;
262 
263   GST_DEBUG_OBJECT (decoder, "Set format");
264 
265   if (self->input_state)
266     gst_video_codec_state_unref (self->input_state);
267 
268   self->input_state = gst_video_codec_state_ref (state);
269 
270   query = gst_query_new_latency ();
271   if (gst_pad_peer_query (GST_VIDEO_DECODER_SINK_PAD (self), query))
272     gst_query_parse_latency (query, &priv->is_live, NULL, NULL);
273   gst_query_unref (query);
274 
275   return TRUE;
276 }
277 
278 static void
gst_vp9_decoder_reset(GstVp9Decoder * self)279 gst_vp9_decoder_reset (GstVp9Decoder * self)
280 {
281   GstVp9DecoderPrivate *priv = self->priv;
282 
283   if (priv->dpb)
284     gst_vp9_dpb_clear (priv->dpb);
285 
286   priv->wait_keyframe = TRUE;
287   gst_queue_array_clear (priv->output_queue);
288 }
289 
290 static GstFlowReturn
gst_vp9_decoder_drain_internal(GstVp9Decoder * self,gboolean wait_keyframe)291 gst_vp9_decoder_drain_internal (GstVp9Decoder * self, gboolean wait_keyframe)
292 {
293   GstFlowReturn ret = GST_FLOW_OK;
294   GstVp9DecoderPrivate *priv = self->priv;
295 
296   gst_vp9_decoder_drain_output_queue (self, 0, &ret);
297   if (priv->dpb)
298     gst_vp9_dpb_clear (priv->dpb);
299 
300   priv->wait_keyframe = wait_keyframe;
301 
302   return ret;
303 }
304 
305 static GstFlowReturn
gst_vp9_decoder_finish(GstVideoDecoder * decoder)306 gst_vp9_decoder_finish (GstVideoDecoder * decoder)
307 {
308   GST_DEBUG_OBJECT (decoder, "finish");
309 
310   return gst_vp9_decoder_drain_internal (GST_VP9_DECODER (decoder), TRUE);
311 }
312 
313 static gboolean
gst_vp9_decoder_flush(GstVideoDecoder * decoder)314 gst_vp9_decoder_flush (GstVideoDecoder * decoder)
315 {
316   GST_DEBUG_OBJECT (decoder, "flush");
317 
318   gst_vp9_decoder_reset (GST_VP9_DECODER (decoder));
319 
320   return TRUE;
321 }
322 
323 static GstFlowReturn
gst_vp9_decoder_drain(GstVideoDecoder * decoder)324 gst_vp9_decoder_drain (GstVideoDecoder * decoder)
325 {
326   GST_DEBUG_OBJECT (decoder, "drain");
327 
328   return gst_vp9_decoder_drain_internal (GST_VP9_DECODER (decoder), TRUE);
329 }
330 
331 static void
gst_vp9_decoder_clear_output_frame(GstVp9DecoderOutputFrame * output_frame)332 gst_vp9_decoder_clear_output_frame (GstVp9DecoderOutputFrame * output_frame)
333 {
334   if (!output_frame)
335     return;
336 
337   if (output_frame->frame) {
338     gst_video_decoder_release_frame (GST_VIDEO_DECODER (output_frame->self),
339         output_frame->frame);
340     output_frame->frame = NULL;
341   }
342 
343   gst_vp9_picture_clear (&output_frame->picture);
344 }
345 
346 static GstFlowReturn
gst_vp9_decoder_handle_frame(GstVideoDecoder * decoder,GstVideoCodecFrame * frame)347 gst_vp9_decoder_handle_frame (GstVideoDecoder * decoder,
348     GstVideoCodecFrame * frame)
349 {
350   GstVp9Decoder *self = GST_VP9_DECODER (decoder);
351   GstVp9DecoderClass *klass = GST_VP9_DECODER_GET_CLASS (self);
352   GstVp9DecoderPrivate *priv = self->priv;
353   GstBuffer *in_buf = frame->input_buffer;
354   GstVp9FrameHeader frame_hdr;
355   GstVp9Picture *picture = NULL;
356   GstVp9ParserResult pres;
357   GstMapInfo map;
358   GstFlowReturn ret = GST_FLOW_OK;
359   gboolean intra_only = FALSE;
360   gboolean check_codec_change = FALSE;
361   GstVp9DecoderOutputFrame output_frame;
362 
363   GST_LOG_OBJECT (self, "handle frame %" GST_PTR_FORMAT, in_buf);
364 
365   if (!gst_buffer_map (in_buf, &map, GST_MAP_READ)) {
366     GST_ERROR_OBJECT (self, "Cannot map input buffer");
367     ret = GST_FLOW_ERROR;
368     goto error;
369   }
370 
371   pres =
372       gst_vp9_stateful_parser_parse_uncompressed_frame_header (priv->parser,
373       &frame_hdr, map.data, map.size);
374 
375   if (pres != GST_VP9_PARSER_OK) {
376     GST_ERROR_OBJECT (self, "Failed to parsing frame header");
377     ret = GST_FLOW_ERROR;
378     goto unmap_and_error;
379   }
380 
381   if (self->parse_compressed_headers && !frame_hdr.show_existing_frame) {
382     pres =
383         gst_vp9_stateful_parser_parse_compressed_frame_header (priv->parser,
384         &frame_hdr, map.data + frame_hdr.frame_header_length_in_bytes,
385         map.size);
386 
387     if (pres != GST_VP9_PARSER_OK) {
388       GST_ERROR_OBJECT (self, "Failed to parse the compressed frame header");
389       goto unmap_and_error;
390     }
391   }
392 
393   if (frame_hdr.show_existing_frame) {
394     /* This is a non-intra, dummy frame */
395     intra_only = FALSE;
396   } else if (frame_hdr.frame_type == GST_VP9_KEY_FRAME || frame_hdr.intra_only) {
397     intra_only = TRUE;
398   }
399 
400   if (intra_only) {
401     if (frame_hdr.frame_type == GST_VP9_KEY_FRAME) {
402       /* Always check codec change per keyframe */
403       check_codec_change = TRUE;
404     } else if (priv->wait_keyframe) {
405       /* Or, if we are waiting for leading keyframe, but this is intra-only,
406        * try decoding this frame, it's allowed as per spec */
407       check_codec_change = TRUE;
408     }
409   }
410 
411   if (priv->wait_keyframe && !intra_only) {
412     GST_DEBUG_OBJECT (self, "Drop frame before initial keyframe");
413     gst_buffer_unmap (in_buf, &map);
414 
415     gst_video_decoder_release_frame (decoder, frame);;
416 
417     return GST_FLOW_OK;
418   }
419 
420   if (check_codec_change) {
421     ret = gst_vp9_decoder_check_codec_change (self, &frame_hdr);
422     if (ret != GST_FLOW_OK) {
423       GST_WARNING_OBJECT (self, "Subclass cannot handle codec change");
424       goto unmap_and_error;
425     }
426   } else if (!frame_hdr.show_existing_frame && !priv->support_non_kf_change &&
427       gst_vp9_decoder_is_format_change (self, &frame_hdr)) {
428     GST_DEBUG_OBJECT (self, "Drop frame on non-keyframe format change");
429 
430     gst_buffer_unmap (in_buf, &map);
431     gst_video_decoder_release_frame (decoder, frame);
432 
433     /* Drains frames if any and waits for keyframe again */
434     return gst_vp9_decoder_drain_internal (self, TRUE);
435   }
436 
437   if (!priv->had_sequence) {
438     GST_WARNING_OBJECT (self, "No handled frame header, drop frame");
439     goto unmap_and_error;
440   }
441 
442   priv->wait_keyframe = FALSE;
443 
444   if (frame_hdr.show_existing_frame) {
445     GstVp9Picture *pic_to_dup;
446 
447     if (frame_hdr.frame_to_show_map_idx >= GST_VP9_REF_FRAMES ||
448         !priv->dpb->pic_list[frame_hdr.frame_to_show_map_idx]) {
449       GST_ERROR_OBJECT (self, "Invalid frame_to_show_map_idx %d",
450           frame_hdr.frame_to_show_map_idx);
451       goto unmap_and_error;
452     }
453 
454     /* If not implemented by subclass, we can just drop this picture
455      * since this frame header indicates the frame index to be duplicated
456      * and also this frame header doesn't affect reference management */
457     if (!klass->duplicate_picture) {
458       gst_buffer_unmap (in_buf, &map);
459       GST_VIDEO_CODEC_FRAME_SET_DECODE_ONLY (frame);
460 
461       gst_video_decoder_finish_frame (GST_VIDEO_DECODER (self), frame);
462     }
463 
464     pic_to_dup = priv->dpb->pic_list[frame_hdr.frame_to_show_map_idx];
465     picture = klass->duplicate_picture (self, frame, pic_to_dup);
466 
467     if (!picture) {
468       GST_ERROR_OBJECT (self, "subclass didn't provide duplicated picture");
469       goto unmap_and_error;
470     }
471   } else {
472     picture = gst_vp9_picture_new ();
473     picture->frame_hdr = frame_hdr;
474     picture->system_frame_number = frame->system_frame_number;
475     picture->data = map.data;
476     picture->size = map.size;
477 
478     if (klass->new_picture) {
479       ret = klass->new_picture (self, frame, picture);
480       if (ret != GST_FLOW_OK) {
481         GST_WARNING_OBJECT (self, "subclass failed to handle new picture");
482         goto unmap_and_error;
483       }
484     }
485 
486     if (klass->start_picture) {
487       ret = klass->start_picture (self, picture);
488       if (ret != GST_FLOW_OK) {
489         GST_WARNING_OBJECT (self, "subclass failed to handle start picture");
490         goto unmap_and_error;
491       }
492     }
493 
494     if (klass->decode_picture) {
495       ret = klass->decode_picture (self, picture, priv->dpb);
496       if (ret != GST_FLOW_OK) {
497         GST_WARNING_OBJECT (self, "subclass failed to decode current picture");
498         goto unmap_and_error;
499       }
500     }
501 
502     if (klass->end_picture) {
503       ret = klass->end_picture (self, picture);
504       if (ret != GST_FLOW_OK) {
505         GST_WARNING_OBJECT (self, "subclass failed to handle end picture");
506         goto unmap_and_error;
507       }
508     }
509 
510     /* Just pass our picture to dpb object.
511      * Even if this picture does not need to be added to dpb
512      * (i.e., not a reference frame), gst_vp9_dpb_add() will take care of
513      * the case as well */
514     gst_vp9_dpb_add (priv->dpb, gst_vp9_picture_ref (picture));
515   }
516 
517   gst_buffer_unmap (in_buf, &map);
518 
519   if (!frame_hdr.show_frame && !frame_hdr.show_existing_frame) {
520     GST_LOG_OBJECT (self, "Decode only picture %p", picture);
521     GST_VIDEO_CODEC_FRAME_SET_DECODE_ONLY (frame);
522 
523     gst_vp9_picture_unref (picture);
524 
525     ret = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (self), frame);
526   } else {
527     output_frame.frame = frame;
528     output_frame.picture = picture;
529     output_frame.self = self;
530     gst_queue_array_push_tail_struct (priv->output_queue, &output_frame);
531   }
532 
533   gst_vp9_decoder_drain_output_queue (self, priv->preferred_output_delay, &ret);
534 
535   if (ret == GST_FLOW_ERROR) {
536     GST_VIDEO_DECODER_ERROR (self, 1, STREAM, DECODE,
537         ("Failed to decode data"), (NULL), ret);
538     return ret;
539   }
540 
541   return ret;
542 
543 unmap_and_error:
544   {
545     gst_buffer_unmap (in_buf, &map);
546     goto error;
547   }
548 
549 error:
550   {
551     if (picture)
552       gst_vp9_picture_unref (picture);
553 
554     if (ret == GST_FLOW_ERROR) {
555       GST_VIDEO_DECODER_ERROR (self, 1, STREAM, DECODE,
556           ("Failed to decode data"), (NULL), ret);
557     }
558 
559     gst_video_decoder_drop_frame (decoder, frame);
560 
561     return ret;
562   }
563 }
564 
565 static void
gst_vp9_decoder_drain_output_queue(GstVp9Decoder * self,guint num,GstFlowReturn * ret)566 gst_vp9_decoder_drain_output_queue (GstVp9Decoder * self, guint num,
567     GstFlowReturn * ret)
568 {
569   GstVp9DecoderPrivate *priv = self->priv;
570   GstVp9DecoderClass *klass = GST_VP9_DECODER_GET_CLASS (self);
571 
572   g_assert (klass->output_picture);
573 
574   while (gst_queue_array_get_length (priv->output_queue) > num) {
575     GstVp9DecoderOutputFrame *output_frame = (GstVp9DecoderOutputFrame *)
576         gst_queue_array_pop_head_struct (priv->output_queue);
577     /* Output queued frames whatever the return value is, in order to empty
578      * the queue */
579     GstFlowReturn flow_ret = klass->output_picture (self,
580         output_frame->frame, output_frame->picture);
581 
582     /* Then, update @ret with new flow return value only if @ret was
583      * GST_FLOW_OK. This is to avoid pattern such that
584      * ```c
585      * GstFlowReturn my_return = GST_FLOW_OK;
586      * do something
587      *
588      * if (my_return == GST_FLOW_OK) {
589      *   my_return = gst_vp9_decoder_drain_output_queue ();
590      * } else {
591      *   // Ignore flow return of this method, but current `my_return` error code
592      *   gst_vp9_decoder_drain_output_queue ();
593      * }
594      *
595      * return my_return;
596      * ```
597      */
598     if (*ret == GST_FLOW_OK)
599       *ret = flow_ret;
600   }
601 }
602 
603 /**
604  * gst_vp9_decoder_set_non_keyframe_format_change_support:
605  * @decoder: a #GstVp9Decoder
606  * @support: whether subclass can support non-keyframe format change
607  *
608  * Called to set non-keyframe format change awareness
609  *
610  * Since: 1.20
611  */
612 void
gst_vp9_decoder_set_non_keyframe_format_change_support(GstVp9Decoder * decoder,gboolean support)613 gst_vp9_decoder_set_non_keyframe_format_change_support (GstVp9Decoder * decoder,
614     gboolean support)
615 {
616   g_return_if_fail (GST_IS_VP9_DECODER (decoder));
617 
618   decoder->priv->support_non_kf_change = support;
619 }
620