Fix seek issue
[profile/ivi/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapidecoder.c
1 /*
2  *  gstvaapidecoder.c - VA decoder abstraction
3  *
4  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5  *  Copyright (C) 2011-2012 Intel Corporation
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public License
9  *  as published by the Free Software Foundation; either version 2.1
10  *  of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301 USA
21  */
22
23 /**
24  * SECTION:gstvaapidecoder
25  * @short_description: VA decoder abstraction
26  */
27
28 #include "sysdeps.h"
29 #include "gstvaapicompat.h"
30 #include "gstvaapidecoder.h"
31 #include "gstvaapidecoder_priv.h"
32 #include "gstvaapiutils.h"
33 #include "gstvaapi_priv.h"
34
35 #define DEBUG 1
36 #include "gstvaapidebug.h"
37
38 G_DEFINE_TYPE(GstVaapiDecoder, gst_vaapi_decoder, G_TYPE_OBJECT)
39
40 enum {
41     PROP_0,
42
43     PROP_DISPLAY,
44     PROP_CAPS,
45
46     N_PROPERTIES
47 };
48
49 static GParamSpec *g_properties[N_PROPERTIES] = { NULL, };
50
51 static void
52 destroy_buffer(GstBuffer *buffer)
53 {
54     gst_buffer_unref(buffer);
55 }
56
57 static gboolean
58 push_buffer(GstVaapiDecoder *decoder, GstBuffer *buffer)
59 {
60     GstVaapiDecoderPrivate * const priv = decoder->priv;
61
62     if (!buffer) {
63         buffer = gst_buffer_new();
64         if (!buffer)
65             return FALSE;
66         GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_FLAG_EOS);
67     }
68
69     GST_DEBUG("queue encoded data buffer %p (%d bytes)",
70               buffer, GST_BUFFER_SIZE(buffer));
71
72     g_queue_push_tail(priv->buffers, buffer);
73     return TRUE;
74 }
75
76 static GstBuffer *
77 pop_buffer(GstVaapiDecoder *decoder)
78 {
79     GstVaapiDecoderPrivate * const priv = decoder->priv;
80     GstBuffer *buffer;
81
82     buffer = g_queue_pop_head(priv->buffers);
83     if (!buffer)
84         return NULL;
85
86     GST_DEBUG("dequeue buffer %p for decoding (%d bytes)",
87               buffer, GST_BUFFER_SIZE(buffer));
88
89     return buffer;
90 }
91
92 static GstVaapiDecoderStatus
93 decode_step(GstVaapiDecoder *decoder, gboolean *try_again)
94 {
95     GstVaapiDecoderStatus status;
96     GstBuffer *buffer;
97
98     /* Decoding will fail if there is no surface left */
99     status = gst_vaapi_decoder_check_status(decoder);
100     if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
101         return status;
102
103     do {
104         if (*try_again){
105             buffer = gst_buffer_new();
106             if (buffer){
107                 gst_buffer_set_data(buffer, NULL, 0);
108             }
109             *try_again = FALSE;
110         }else{
111             buffer = pop_buffer(decoder);
112         }
113         if (!buffer)
114             return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
115
116         status = GST_VAAPI_DECODER_GET_CLASS(decoder)->decode(decoder, buffer);
117         GST_DEBUG("decode frame (status = %d)", status);
118         if (status != GST_VAAPI_DECODER_STATUS_SUCCESS && GST_BUFFER_IS_EOS(buffer))
119             status = GST_VAAPI_DECODER_STATUS_END_OF_STREAM;
120         gst_buffer_unref(buffer);
121     } while (status == GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA);
122     return status;
123 }
124
125 static inline void
126 push_surface(GstVaapiDecoder *decoder, GstVaapiSurfaceProxy *proxy)
127 {
128     GstVaapiDecoderPrivate * const priv = decoder->priv;
129     GstClockTime duration;
130
131     GST_DEBUG("queue decoded surface %" GST_VAAPI_ID_FORMAT,
132               GST_VAAPI_ID_ARGS(gst_vaapi_surface_proxy_get_surface_id(proxy)));
133
134     if (priv->fps_n && priv->fps_d) {
135         /* Actual field duration is computed in vaapipostproc */
136         duration = gst_util_uint64_scale(GST_SECOND, priv->fps_d, priv->fps_n);
137         gst_vaapi_surface_proxy_set_duration(proxy, duration);
138     }
139     g_queue_push_tail(priv->surfaces, proxy);
140 }
141
142 static inline GstVaapiSurfaceProxy *
143 pop_surface(GstVaapiDecoder *decoder)
144 {
145     GstVaapiDecoderPrivate * const priv = decoder->priv;
146
147     return g_queue_pop_head(priv->surfaces);
148 }
149
150 static inline void
151 set_codec_data(GstVaapiDecoder *decoder, GstBuffer *codec_data)
152 {
153     GstVaapiDecoderPrivate * const priv = decoder->priv;
154
155     if (priv->codec_data) {
156         gst_buffer_unref(priv->codec_data);
157         priv->codec_data = NULL;
158     }
159
160     if (codec_data)
161         priv->codec_data = gst_buffer_ref(codec_data);
162 }
163
164 static void
165 set_caps(GstVaapiDecoder *decoder, GstCaps *caps)
166 {
167     GstVaapiDecoderPrivate * const priv = decoder->priv;
168     GstStructure * const structure = gst_caps_get_structure(caps, 0);
169     GstVaapiProfile profile;
170     const GValue *v_codec_data;
171     gint v1, v2;
172     gboolean b;
173
174     profile = gst_vaapi_profile_from_caps(caps);
175     if (!profile)
176         return;
177
178     priv->caps = gst_caps_copy(caps);
179
180     priv->codec = gst_vaapi_profile_get_codec(profile);
181     if (!priv->codec)
182         return;
183
184     if (gst_structure_get_int(structure, "width", &v1))
185         priv->width = v1;
186     if (gst_structure_get_int(structure, "height", &v2))
187         priv->height = v2;
188
189     if (gst_structure_get_fraction(structure, "framerate", &v1, &v2)) {
190         priv->fps_n = v1;
191         priv->fps_d = v2;
192     }
193
194     if (gst_structure_get_fraction(structure, "pixel-aspect-ratio", &v1, &v2)) {
195         priv->par_n = v1;
196         priv->par_d = v2;
197     }
198
199     if (gst_structure_get_boolean(structure, "interlaced", &b))
200         priv->is_interlaced = b;
201
202     v_codec_data = gst_structure_get_value(structure, "codec_data");
203     if (v_codec_data)
204         set_codec_data(decoder, gst_value_get_buffer(v_codec_data));
205 }
206
207 static void
208 clear_queue(GQueue *q, GDestroyNotify destroy)
209 {
210     while (!g_queue_is_empty(q))
211         destroy(g_queue_pop_head(q));
212 }
213
214 static void
215 gst_vaapi_decoder_finalize(GObject *object)
216 {
217     GstVaapiDecoder * const        decoder = GST_VAAPI_DECODER(object);
218     GstVaapiDecoderPrivate * const priv    = decoder->priv;
219
220     set_codec_data(decoder, NULL);
221
222     if (priv->caps) {
223         gst_caps_unref(priv->caps);
224         priv->caps = NULL;
225     }
226
227     if (priv->context) {
228         g_object_unref(priv->context);
229         priv->context = NULL;
230         priv->va_context = VA_INVALID_ID;
231     }
232  
233     if (priv->buffers) {
234         clear_queue(priv->buffers, (GDestroyNotify)destroy_buffer);
235         g_queue_free(priv->buffers);
236         priv->buffers = NULL;
237     }
238
239     if (priv->surfaces) {
240         clear_queue(priv->surfaces, (GDestroyNotify)g_object_unref);
241         g_queue_free(priv->surfaces);
242         priv->surfaces = NULL;
243     }
244
245     if (priv->display) {
246         g_object_unref(priv->display);
247         priv->display = NULL;
248         priv->va_display = NULL;
249     }
250
251     G_OBJECT_CLASS(gst_vaapi_decoder_parent_class)->finalize(object);
252 }
253
254 static void
255 gst_vaapi_decoder_set_property(
256     GObject      *object,
257     guint         prop_id,
258     const GValue *value,
259     GParamSpec   *pspec
260 )
261 {
262     GstVaapiDecoder * const        decoder = GST_VAAPI_DECODER(object);
263     GstVaapiDecoderPrivate * const priv    = decoder->priv;
264
265     switch (prop_id) {
266     case PROP_DISPLAY:
267         priv->display = g_object_ref(g_value_get_object(value));
268         if (priv->display)
269             priv->va_display = gst_vaapi_display_get_display(priv->display);
270         else
271             priv->va_display = NULL;
272         break;
273     case PROP_CAPS:
274         set_caps(decoder, g_value_get_pointer(value));
275         break;
276     default:
277         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
278         break;
279     }
280 }
281
282 static void
283 gst_vaapi_decoder_get_property(
284     GObject    *object,
285     guint       prop_id,
286     GValue     *value,
287     GParamSpec *pspec
288 )
289 {
290     GstVaapiDecoderPrivate * const priv = GST_VAAPI_DECODER(object)->priv;
291
292     switch (prop_id) {
293     case PROP_DISPLAY:
294         g_value_set_object(value, priv->display);
295         break;
296     case PROP_CAPS:
297         gst_value_set_caps(value, priv->caps);
298         break;
299     default:
300         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
301         break;
302     }
303 }
304
305 static void
306 gst_vaapi_decoder_class_init(GstVaapiDecoderClass *klass)
307 {
308     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
309
310     g_type_class_add_private(klass, sizeof(GstVaapiDecoderPrivate));
311
312     object_class->finalize     = gst_vaapi_decoder_finalize;
313     object_class->set_property = gst_vaapi_decoder_set_property;
314     object_class->get_property = gst_vaapi_decoder_get_property;
315
316     /**
317      * GstVaapiDecoder:display:
318      *
319      * The #GstVaapiDisplay this decoder is bound to.
320      */
321     g_properties[PROP_DISPLAY] =
322          g_param_spec_object("display",
323                              "Display",
324                              "The GstVaapiDisplay this decoder is bound to",
325                              GST_VAAPI_TYPE_DISPLAY,
326                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
327
328     g_properties[PROP_CAPS] =
329          g_param_spec_pointer("caps",
330                               "Decoder caps",
331                               "The decoder caps",
332                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
333
334     g_object_class_install_properties(object_class, N_PROPERTIES, g_properties);
335 }
336
337 static void
338 gst_vaapi_decoder_init(GstVaapiDecoder *decoder)
339 {
340     GstVaapiDecoderPrivate *priv = GST_VAAPI_DECODER_GET_PRIVATE(decoder);
341
342     decoder->priv               = priv;
343     priv->display               = NULL;
344     priv->va_display            = NULL;
345     priv->context               = NULL;
346     priv->va_context            = VA_INVALID_ID;
347     priv->caps                  = NULL;
348     priv->codec                 = 0;
349     priv->codec_data            = NULL;
350     priv->width                 = 0;
351     priv->height                = 0;
352     priv->fps_n                 = 0;
353     priv->fps_d                 = 0;
354     priv->par_n                 = 0;
355     priv->par_d                 = 0;
356     priv->buffers               = g_queue_new();
357     priv->surfaces              = g_queue_new();
358     priv->is_interlaced         = FALSE;
359 }
360
361 /**
362  * gst_vaapi_decoder_get_codec:
363  * @decoder: a #GstVaapiDecoder
364  *
365  * Retrieves the @decoder codec type.
366  *
367  * Return value: the #GstVaapiCodec type for @decoder
368  */
369 GstVaapiCodec
370 gst_vaapi_decoder_get_codec(GstVaapiDecoder *decoder)
371 {
372     g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), (GstVaapiCodec)0);
373
374     return decoder->priv->codec;
375 }
376
377 /**
378  * gst_vaapi_decoder_get_caps:
379  * @decoder: a #GstVaapiDecoder
380  *
381  * Retrieves the @decoder caps. The deocder owns the returned caps, so
382  * use gst_caps_ref() whenever necessary.
383  *
384  * Return value: the @decoder caps
385  */
386 GstCaps *
387 gst_vaapi_decoder_get_caps(GstVaapiDecoder *decoder)
388 {
389     return decoder->priv->caps;
390 }
391
392 /**
393  * gst_vaapi_decoder_put_buffer:
394  * @decoder: a #GstVaapiDecoder
395  * @buf: a #GstBuffer
396  *
397  * Queues a #GstBuffer to the HW decoder. The decoder holds a
398  * reference to @buf.
399  *
400  * Caller can notify an End-Of-Stream with @buf set to %NULL. However,
401  * if an empty buffer is passed, i.e. a buffer with %NULL data pointer
402  * or size equals to zero, then the function ignores this buffer and
403  * returns %TRUE.
404  *
405  * Return value: %TRUE on success
406  */
407 gboolean
408 gst_vaapi_decoder_put_buffer(GstVaapiDecoder *decoder, GstBuffer *buf)
409 {
410     g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), FALSE);
411
412     if (buf) {
413         if (!GST_BUFFER_DATA(buf) || GST_BUFFER_SIZE(buf) <= 0)
414             return TRUE;
415         buf = gst_buffer_ref(buf);
416     }
417     return push_buffer(decoder, buf);
418 }
419
420 /**
421  * gst_vaapi_decoder_get_surface:
422  * @decoder: a #GstVaapiDecoder
423  * @pstatus: return location for the decoder status, or %NULL
424  * @try_again: a #gboolean
425  *
426  * Flushes encoded buffers to the decoder and returns a decoded
427  * surface, if any.
428  *
429  * Return value: a #GstVaapiSurfaceProxy holding the decoded surface,
430  *   or %NULL if none is available (e.g. an error). Caller owns the
431  *   returned object. g_object_unref() after usage.
432  */
433 GstVaapiSurfaceProxy *
434 gst_vaapi_decoder_get_surface(
435     GstVaapiDecoder       *decoder,
436     GstVaapiDecoderStatus *pstatus,
437     gboolean              try_again
438 )
439 {
440     GstVaapiSurfaceProxy *proxy;
441     GstVaapiDecoderStatus status;
442
443     if (pstatus)
444         *pstatus = GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
445
446     g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), NULL);
447
448     proxy = pop_surface(decoder);
449     if (!proxy) {
450         do {
451             status = decode_step(decoder, &try_again);
452         } while (status == GST_VAAPI_DECODER_STATUS_SUCCESS);
453         proxy = pop_surface(decoder);
454     }
455
456     if (proxy)
457         status = GST_VAAPI_DECODER_STATUS_SUCCESS;
458
459     if (pstatus)
460         *pstatus = status;
461     return proxy;
462 }
463
464 void
465 gst_vaapi_decoder_clear_buffer(GstVaapiDecoder *decoder)
466 {
467     GstVaapiDecoderPrivate * const priv = decoder->priv;
468     GstVaapiDecoderClass * const klass = GST_VAAPI_DECODER_GET_CLASS(decoder);
469
470     if (klass->clear_buffer)
471         klass->clear_buffer(decoder);
472
473     if (priv->buffers)
474         clear_queue(priv->buffers, (GDestroyNotify)destroy_buffer);
475
476     if (priv->surfaces)
477         clear_queue(priv->surfaces, (GDestroyNotify)g_object_unref);
478 }
479
480 void
481 gst_vaapi_decoder_set_picture_size(
482     GstVaapiDecoder    *decoder,
483     guint               width,
484     guint               height
485 )
486 {
487     GstVaapiDecoderPrivate * const priv = decoder->priv;
488     gboolean size_changed = FALSE;
489
490     if (priv->width != width) {
491         GST_DEBUG("picture width changed to %d", width);
492         priv->width = width;
493         gst_caps_set_simple(priv->caps, "width", G_TYPE_INT, width, NULL);
494         size_changed = TRUE;
495     }
496
497     if (priv->height != height) {
498         GST_DEBUG("picture height changed to %d", height);
499         priv->height = height;
500         gst_caps_set_simple(priv->caps, "height", G_TYPE_INT, height, NULL);
501         size_changed = TRUE;
502     }
503
504     if (size_changed)
505         g_object_notify_by_pspec(G_OBJECT(decoder), g_properties[PROP_CAPS]);
506 }
507
508 void
509 gst_vaapi_decoder_set_framerate(
510     GstVaapiDecoder    *decoder,
511     guint               fps_n,
512     guint               fps_d
513 )
514 {
515     GstVaapiDecoderPrivate * const priv = decoder->priv;
516
517     if (!fps_n || !fps_d)
518         return;
519
520     if (priv->fps_n != fps_n || priv->fps_d != fps_d) {
521         GST_DEBUG("framerate changed to %u/%u", fps_n, fps_d);
522         priv->fps_n = fps_n;
523         priv->fps_d = fps_d;
524         gst_caps_set_simple(
525             priv->caps,
526             "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
527             NULL
528         );
529         g_object_notify_by_pspec(G_OBJECT(decoder), g_properties[PROP_CAPS]);
530     }
531 }
532
533 void
534 gst_vaapi_decoder_set_pixel_aspect_ratio(
535     GstVaapiDecoder    *decoder,
536     guint               par_n,
537     guint               par_d
538 )
539 {
540     GstVaapiDecoderPrivate * const priv = decoder->priv;
541
542     if (!par_n || !par_d)
543         return;
544
545     if (priv->par_n != par_n || priv->par_d != par_d) {
546         GST_DEBUG("pixel-aspect-ratio changed to %u/%u", par_n, par_d);
547         priv->par_n = par_n;
548         priv->par_d = par_d;
549         gst_caps_set_simple(
550             priv->caps,
551             "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d,
552             NULL
553         );
554         g_object_notify_by_pspec(G_OBJECT(decoder), g_properties[PROP_CAPS]);
555     }
556 }
557
558 void
559 gst_vaapi_decoder_set_interlaced(GstVaapiDecoder *decoder, gboolean interlaced)
560 {
561     GstVaapiDecoderPrivate * const priv = decoder->priv;
562
563     if (priv->is_interlaced != interlaced) {
564         GST_DEBUG("interlaced changed to %s", interlaced ? "true" : "false");
565         priv->is_interlaced = interlaced;
566         gst_caps_set_simple(
567             priv->caps,
568             "interlaced", G_TYPE_BOOLEAN, interlaced,
569             NULL
570         );
571         g_object_notify_by_pspec(G_OBJECT(decoder), g_properties[PROP_CAPS]);
572     }
573 }
574
575 gboolean
576 gst_vaapi_decoder_ensure_context(
577     GstVaapiDecoder     *decoder,
578     GstVaapiContextInfo *cip
579 )
580 {
581     GstVaapiDecoderPrivate * const priv = decoder->priv;
582
583     cip->rate_control = GST_VAAPI_RATECONTROL_NONE;
584     gst_vaapi_decoder_set_picture_size(decoder, cip->width, cip->height);
585
586     if (priv->context) {
587         if (!gst_vaapi_context_reset_full(priv->context, cip))
588             return FALSE;
589     }
590     else {
591         priv->context = gst_vaapi_context_new_full(priv->display, cip);
592         if (!priv->context)
593             return FALSE;
594     }
595     priv->va_context = gst_vaapi_context_get_id(priv->context);
596     return TRUE;
597 }
598
599 void
600 gst_vaapi_decoder_push_surface_proxy(
601     GstVaapiDecoder      *decoder,
602     GstVaapiSurfaceProxy *proxy
603 )
604 {
605     push_surface(decoder, proxy);
606 }
607
608 GstVaapiDecoderStatus
609 gst_vaapi_decoder_check_status(GstVaapiDecoder *decoder)
610 {
611     GstVaapiDecoderPrivate * const priv = decoder->priv;
612
613     if (priv->context && gst_vaapi_context_get_surface_count(priv->context) < 1)
614         return GST_VAAPI_DECODER_STATUS_ERROR_NO_SURFACE;
615     return GST_VAAPI_DECODER_STATUS_SUCCESS;
616 }