dpb: port to GstVaapiMiniObject.
[vaapi:gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapidecoder_dpb.c
1 /*
2  *  gstvaapidecoder_dpb.c - Decoded Picture Buffer
3  *
4  *  Copyright (C) 2012 Intel Corporation
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public License
8  *  as published by the Free Software Foundation; either version 2.1
9  *  of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free
18  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301 USA
20  */
21
22 #include "sysdeps.h"
23 #include "gstvaapidecoder_dpb.h"
24
25 #define DEBUG 1
26 #include "gstvaapidebug.h"
27
28 #define GST_VAAPI_DPB_CLASS(klass) \
29     ((GstVaapiDpbClass *)(klass))
30
31 #define GST_VAAPI_DPB_GET_CLASS(obj) \
32     GST_VAAPI_DPB_CLASS(gst_vaapi_mini_object_get_class( \
33                             GST_VAAPI_MINI_OBJECT(obj)))
34
35 /**
36  * GstVaapiDpb:
37  *
38  * A decoded picture buffer (DPB) object.
39  */
40 struct _GstVaapiDpb {
41     /*< private >*/
42     GstVaapiMiniObject   parent_instance;
43
44     /*< protected >*/
45     GstVaapiPicture   **pictures;
46     guint               num_pictures;
47     guint               max_pictures;
48 };
49
50 /**
51  * GstVaapiDpbClass:
52  *
53  * The #GstVaapiDpb base class.
54  */
55 struct _GstVaapiDpbClass {
56     /*< private >*/
57     GstVaapiMiniObjectClass parent_class;
58
59     /*< protected >*/
60     void      (*flush)          (GstVaapiDpb *dpb);
61     gboolean  (*add)            (GstVaapiDpb *dpb, GstVaapiPicture *picture);
62     void      (*get_neighbours) (GstVaapiDpb *dpb, GstVaapiPicture *picture,
63         GstVaapiPicture **prev_picture_ptr, GstVaapiPicture **next_picture_ptr);
64 };
65
66 static const GstVaapiMiniObjectClass *
67 gst_vaapi_dpb_class(void);
68
69 static const GstVaapiMiniObjectClass *
70 gst_vaapi_dpb2_class(void);
71
72 /* ------------------------------------------------------------------------- */
73 /* --- Common Decoded Picture Buffer utilities                           --- */
74 /* ------------------------------------------------------------------------- */
75
76 static GstVaapiDpb *
77 dpb_new(guint max_pictures)
78 {
79     const GstVaapiMiniObjectClass *klass;
80     GstVaapiDpb *dpb;
81
82     g_return_val_if_fail(max_pictures > 0, NULL);
83
84     klass = max_pictures == 2 ? gst_vaapi_dpb2_class() : gst_vaapi_dpb_class();
85
86     dpb = (GstVaapiDpb *)gst_vaapi_mini_object_new(klass);
87     if (!dpb)
88         return NULL;
89
90     dpb->num_pictures = 0;
91     dpb->max_pictures = max_pictures;
92
93     dpb->pictures = g_new0(GstVaapiPicture *, max_pictures);
94     if (!dpb->pictures)
95         goto error;
96     return dpb;
97
98 error:
99     gst_vaapi_dpb_unref(dpb);
100     return NULL;
101 }
102
103 static gint
104 dpb_get_oldest(GstVaapiDpb *dpb, gboolean output)
105 {
106     gint i, lowest_pts_index;
107
108     for (i = 0; i < dpb->num_pictures; i++) {
109         if ((GST_VAAPI_PICTURE_IS_OUTPUT(dpb->pictures[i]) ^ output) == 0)
110             break;
111     }
112     if (i == dpb->num_pictures)
113         return -1;
114
115     lowest_pts_index = i++;
116     for (; i < dpb->num_pictures; i++) {
117         GstVaapiPicture * const picture = dpb->pictures[i];
118         if ((GST_VAAPI_PICTURE_IS_OUTPUT(picture) ^ output) != 0)
119             continue;
120         if (picture->poc < dpb->pictures[lowest_pts_index]->poc)
121             lowest_pts_index = i;
122     }
123     return lowest_pts_index;
124 }
125
126 static void
127 dpb_remove_index(GstVaapiDpb *dpb, guint index)
128 {
129     GstVaapiPicture ** const pictures = dpb->pictures;
130     guint num_pictures = --dpb->num_pictures;
131
132     if (index != num_pictures)
133         gst_vaapi_picture_replace(&pictures[index], pictures[num_pictures]);
134     gst_vaapi_picture_replace(&pictures[num_pictures], NULL);
135 }
136
137 static inline gboolean
138 dpb_output(GstVaapiDpb *dpb, GstVaapiPicture *picture)
139 {
140     return gst_vaapi_picture_output(picture);
141 }
142
143 static gboolean
144 dpb_bump(GstVaapiDpb *dpb)
145 {
146     gint index;
147     gboolean success;
148
149     index = dpb_get_oldest(dpb, FALSE);
150     if (index < 0)
151         return FALSE;
152
153     success = dpb_output(dpb, dpb->pictures[index]);
154     if (!GST_VAAPI_PICTURE_IS_REFERENCE(dpb->pictures[index]))
155         dpb_remove_index(dpb, index);
156     return success;
157 }
158
159 static void
160 dpb_clear(GstVaapiDpb *dpb)
161 {
162     guint i;
163
164     for (i = 0; i < dpb->num_pictures; i++)
165         gst_vaapi_picture_replace(&dpb->pictures[i], NULL);
166     dpb->num_pictures = 0;
167 }
168
169 /* ------------------------------------------------------------------------- */
170 /* --- Base Decoded Picture Buffer                                       --- */
171 /* ------------------------------------------------------------------------- */
172
173 static void
174 gst_vaapi_dpb_base_flush(GstVaapiDpb *dpb)
175 {
176     while (dpb_bump(dpb))
177         ;
178     dpb_clear(dpb);
179 }
180
181 static gboolean
182 gst_vaapi_dpb_base_add(GstVaapiDpb *dpb, GstVaapiPicture *picture)
183 {
184     guint i;
185
186     // Remove all unused pictures
187     i = 0;
188     while (i < dpb->num_pictures) {
189         GstVaapiPicture * const picture = dpb->pictures[i];
190         if (GST_VAAPI_PICTURE_IS_OUTPUT(picture) &&
191             !GST_VAAPI_PICTURE_IS_REFERENCE(picture))
192             dpb_remove_index(dpb, i);
193         else
194             i++;
195     }
196
197     // Store reference decoded picture into the DPB
198     if (GST_VAAPI_PICTURE_IS_REFERENCE(picture)) {
199         while (dpb->num_pictures == dpb->max_pictures) {
200             if (!dpb_bump(dpb))
201                 return FALSE;
202         }
203     }
204
205     // Store non-reference decoded picture into the DPB
206     else {
207         if (GST_VAAPI_PICTURE_IS_SKIPPED(picture))
208             return TRUE;
209         while (dpb->num_pictures == dpb->max_pictures) {
210             for (i = 0; i < dpb->num_pictures; i++) {
211                 if (!GST_VAAPI_PICTURE_IS_OUTPUT(picture) &&
212                     dpb->pictures[i]->poc < picture->poc)
213                     break;
214             }
215             if (i == dpb->num_pictures)
216                 return dpb_output(dpb, picture);
217             if (!dpb_bump(dpb))
218                 return FALSE;
219         }
220     }
221     gst_vaapi_picture_replace(&dpb->pictures[dpb->num_pictures++], picture);
222     return TRUE;
223 }
224
225 void
226 gst_vaapi_dpb_base_get_neighbours(
227     GstVaapiDpb        *dpb,
228     GstVaapiPicture    *picture,
229     GstVaapiPicture   **prev_picture_ptr,
230     GstVaapiPicture   **next_picture_ptr
231 )
232 {
233     GstVaapiPicture *prev_picture = NULL;
234     GstVaapiPicture *next_picture = NULL;
235     guint i;
236
237     /* Find the first picture with POC > specified picture POC */
238     for (i = 0; i < dpb->num_pictures; i++) {
239         GstVaapiPicture * const ref_picture = dpb->pictures[i];
240         if (ref_picture->poc == picture->poc) {
241             if (i > 0)
242                 prev_picture = dpb->pictures[i - 1];
243             if (i + 1 < dpb->num_pictures)
244                 next_picture = dpb->pictures[i + 1];
245             break;
246         }
247         else if (ref_picture->poc > picture->poc) {
248             next_picture = ref_picture;
249             if (i > 0)
250                 prev_picture = dpb->pictures[i - 1];
251             break;
252         }
253     }
254
255     g_assert(next_picture ? next_picture->poc > picture->poc : TRUE);
256     g_assert(prev_picture ? prev_picture->poc < picture->poc : TRUE);
257
258     if (prev_picture_ptr)
259         *prev_picture_ptr = prev_picture;
260     if (next_picture_ptr)
261         *next_picture_ptr = next_picture;
262 }
263
264 static void
265 gst_vaapi_dpb_finalize(GstVaapiDpb *dpb)
266 {
267     if (dpb->pictures) {
268         dpb_clear(dpb);
269         g_free(dpb->pictures);
270     }
271 }
272
273 static const GstVaapiMiniObjectClass *
274 gst_vaapi_dpb_class(void)
275 {
276     static const GstVaapiDpbClass GstVaapiDpbClass = {
277         { sizeof(GstVaapiDpb),
278           (GDestroyNotify)gst_vaapi_dpb_finalize },
279
280         gst_vaapi_dpb_base_flush,
281         gst_vaapi_dpb_base_add,
282         gst_vaapi_dpb_base_get_neighbours
283     };
284     return &GstVaapiDpbClass.parent_class;
285 }
286
287 GstVaapiDpb *
288 gst_vaapi_dpb_new(guint max_pictures)
289 {
290     return dpb_new(max_pictures);
291 }
292
293 void
294 gst_vaapi_dpb_flush(GstVaapiDpb *dpb)
295 {
296     const GstVaapiDpbClass *klass;
297
298     g_return_if_fail(GST_VAAPI_IS_DPB(dpb));
299
300     klass = GST_VAAPI_DPB_GET_CLASS(dpb);
301     if (G_UNLIKELY(!klass || !klass->add))
302         return;
303     klass->flush(dpb);
304 }
305
306 gboolean
307 gst_vaapi_dpb_add(GstVaapiDpb *dpb, GstVaapiPicture *picture)
308 {
309     const GstVaapiDpbClass *klass;
310
311     g_return_val_if_fail(GST_VAAPI_IS_DPB(dpb), FALSE);
312     g_return_val_if_fail(GST_VAAPI_IS_PICTURE(picture), FALSE);
313
314     klass = GST_VAAPI_DPB_GET_CLASS(dpb);
315     if (G_UNLIKELY(!klass || !klass->add))
316         return FALSE;
317     return klass->add(dpb, picture);
318 }
319
320 guint
321 gst_vaapi_dpb_size(GstVaapiDpb *dpb)
322 {
323     g_return_val_if_fail(GST_VAAPI_IS_DPB(dpb), 0);
324
325     return dpb->num_pictures;
326 }
327
328 void
329 gst_vaapi_dpb_get_neighbours(
330     GstVaapiDpb        *dpb,
331     GstVaapiPicture    *picture,
332     GstVaapiPicture   **prev_picture_ptr,
333     GstVaapiPicture   **next_picture_ptr
334 )
335 {
336     const GstVaapiDpbClass *klass;
337
338     g_return_if_fail(GST_VAAPI_IS_DPB(dpb));
339     g_return_if_fail(GST_VAAPI_IS_PICTURE(picture));
340
341     klass = GST_VAAPI_DPB_GET_CLASS(dpb);
342     if (G_UNLIKELY(!klass || !klass->get_neighbours))
343         return;
344     klass->get_neighbours(dpb, picture, prev_picture_ptr, next_picture_ptr);
345 }
346
347 /* ------------------------------------------------------------------------- */
348 /* --- Decoded Picture Buffer (optimized for 2 reference pictures)       --- */
349 /* ------------------------------------------------------------------------- */
350
351 static void
352 gst_vaapi_dpb2_get_neighbours(
353     GstVaapiDpb        *dpb,
354     GstVaapiPicture    *picture,
355     GstVaapiPicture   **prev_picture_ptr,
356     GstVaapiPicture   **next_picture_ptr
357 );
358
359 static gboolean
360 gst_vaapi_dpb2_add(GstVaapiDpb *dpb, GstVaapiPicture *picture)
361 {
362     GstVaapiPicture *ref_picture;
363     gint index = -1;
364
365     g_return_val_if_fail(GST_VAAPI_IS_DPB(dpb), FALSE);
366     g_return_val_if_fail(dpb->max_pictures == 2, FALSE);
367
368     /*
369      * Purpose: only store reference decoded pictures into the DPB
370      *
371      * This means:
372      * - non-reference decoded pictures are output immediately
373      * - ... thus causing older reference pictures to be output, if not already
374      * - the oldest reference picture is replaced with the new reference picture
375      */
376     if (G_LIKELY(dpb->num_pictures == 2)) {
377         index = (dpb->pictures[0]->poc > dpb->pictures[1]->poc);
378         ref_picture = dpb->pictures[index];
379         if (!GST_VAAPI_PICTURE_IS_OUTPUT(ref_picture)) {
380             if (!dpb_output(dpb, ref_picture))
381                 return FALSE;
382         }
383     }
384
385     if (!GST_VAAPI_PICTURE_IS_REFERENCE(picture))
386         return dpb_output(dpb, picture);
387
388     if (index < 0)
389         index = dpb->num_pictures++;
390     gst_vaapi_picture_replace(&dpb->pictures[index], picture);
391     return TRUE;
392 }
393
394 static const GstVaapiMiniObjectClass *
395 gst_vaapi_dpb2_class(void)
396 {
397     static const GstVaapiDpbClass GstVaapiDpb2Class = {
398         { sizeof(GstVaapiDpb),
399           (GDestroyNotify)gst_vaapi_dpb_finalize },
400
401         gst_vaapi_dpb_base_flush,
402         gst_vaapi_dpb2_add,
403         gst_vaapi_dpb2_get_neighbours
404     };
405     return &GstVaapiDpb2Class.parent_class;
406 }
407
408 void
409 gst_vaapi_dpb2_get_neighbours(
410     GstVaapiDpb        *dpb,
411     GstVaapiPicture    *picture,
412     GstVaapiPicture   **prev_picture_ptr,
413     GstVaapiPicture   **next_picture_ptr
414 )
415 {
416     GstVaapiPicture *ref_picture, *ref_pictures[2];
417     GstVaapiPicture **picture_ptr;
418     guint i, index;
419
420     g_return_if_fail(GST_VAAPI_IS_DPB(dpb));
421     g_return_if_fail(dpb->max_pictures == 2);
422     g_return_if_fail(GST_VAAPI_IS_PICTURE(picture));
423
424     ref_pictures[0] = NULL;
425     ref_pictures[1] = NULL;
426     for (i = 0; i < dpb->num_pictures; i++) {
427         ref_picture = dpb->pictures[i];
428         index       = ref_picture->poc > picture->poc;
429         picture_ptr = &ref_pictures[index];
430         if (!*picture_ptr || ((*picture_ptr)->poc > ref_picture->poc) == index)
431             *picture_ptr = ref_picture;
432     }
433
434     if (prev_picture_ptr)
435         *prev_picture_ptr = ref_pictures[0];
436     if (next_picture_ptr)
437         *next_picture_ptr = ref_pictures[1];
438 }