Tizen 2.0 Release
[framework/multimedia/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 void
77 push_back_buffer(GstVaapiDecoder *decoder, GstBuffer *buffer)
78 {
79     GstVaapiDecoderPrivate * const priv = decoder->priv;
80
81     GST_DEBUG("requeue encoded data buffer %p (%d bytes)",
82               buffer, GST_BUFFER_SIZE(buffer));
83
84     g_queue_push_head(priv->buffers, buffer);
85 }
86
87 static GstBuffer *
88 pop_buffer(GstVaapiDecoder *decoder)
89 {
90     GstVaapiDecoderPrivate * const priv = decoder->priv;
91     GstBuffer *buffer;
92
93     buffer = g_queue_pop_head(priv->buffers);
94     if (!buffer)
95         return NULL;
96
97     GST_DEBUG("dequeue buffer %p for decoding (%d bytes)",
98               buffer, GST_BUFFER_SIZE(buffer));
99
100     return buffer;
101 }
102
103 static GstVaapiDecoderStatus
104 decode_step(GstVaapiDecoder *decoder)
105 {
106     GstVaapiDecoderStatus status;
107     GstBuffer *buffer;
108
109     /* Decoding will fail if there is no surface left */
110     status = gst_vaapi_decoder_check_status(decoder);
111     if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
112         return status;
113
114     do {
115         buffer = pop_buffer(decoder);
116         if (!buffer)
117             return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
118
119         status = GST_VAAPI_DECODER_GET_CLASS(decoder)->decode(decoder, buffer);
120         GST_DEBUG("decode frame (status = %d)", status);
121         if (status != GST_VAAPI_DECODER_STATUS_SUCCESS && GST_BUFFER_IS_EOS(buffer))
122             status = GST_VAAPI_DECODER_STATUS_END_OF_STREAM;
123         gst_buffer_unref(buffer);
124     } while (status == GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA);
125     return status;
126 }
127
128 static inline void
129 push_surface(GstVaapiDecoder *decoder, GstVaapiSurfaceProxy *proxy)
130 {
131     GstVaapiDecoderPrivate * const priv = decoder->priv;
132
133     GST_DEBUG("queue decoded surface %" GST_VAAPI_ID_FORMAT,
134               GST_VAAPI_ID_ARGS(gst_vaapi_surface_proxy_get_surface_id(proxy)));
135
136     g_queue_push_tail(priv->surfaces, proxy);
137 }
138
139 static inline GstVaapiSurfaceProxy *
140 pop_surface(GstVaapiDecoder *decoder)
141 {
142     GstVaapiDecoderPrivate * const priv = decoder->priv;
143
144     return g_queue_pop_head(priv->surfaces);
145 }
146
147 static inline void
148 set_codec_data(GstVaapiDecoder *decoder, GstBuffer *codec_data)
149 {
150     GstVaapiDecoderPrivate * const priv = decoder->priv;
151
152     if (priv->codec_data) {
153         gst_buffer_unref(priv->codec_data);
154         priv->codec_data = NULL;
155     }
156
157     if (codec_data)
158         priv->codec_data = gst_buffer_ref(codec_data);
159 }
160
161 static void
162 set_caps(GstVaapiDecoder *decoder, GstCaps *caps)
163 {
164     GstVaapiDecoderPrivate * const priv = decoder->priv;
165     GstStructure * const structure = gst_caps_get_structure(caps, 0);
166     GstVaapiProfile profile;
167     const GValue *v_codec_data;
168     gint v1, v2;
169     gboolean b;
170
171     profile = gst_vaapi_profile_from_caps(caps);
172     if (!profile)
173         return;
174
175     priv->caps = gst_caps_copy(caps);
176
177     priv->codec = gst_vaapi_profile_get_codec(profile);
178     if (!priv->codec)
179         return;
180
181     if (gst_structure_get_int(structure, "width", &v1))
182         priv->width = v1;
183     if (gst_structure_get_int(structure, "height", &v2))
184         priv->height = v2;
185
186     if (gst_structure_get_fraction(structure, "framerate", &v1, &v2)) {
187         priv->fps_n = v1;
188         priv->fps_d = v2;
189     }
190
191     if (gst_structure_get_fraction(structure, "pixel-aspect-ratio", &v1, &v2)) {
192         priv->par_n = v1;
193         priv->par_d = v2;
194     }
195
196     if (gst_structure_get_boolean(structure, "interlaced", &b))
197         priv->is_interlaced = b;
198
199     v_codec_data = gst_structure_get_value(structure, "codec_data");
200     if (v_codec_data)
201         set_codec_data(decoder, gst_value_get_buffer(v_codec_data));
202 }
203
204 static void
205 clear_queue(GQueue *q, GDestroyNotify destroy)
206 {
207     while (!g_queue_is_empty(q))
208         destroy(g_queue_pop_head(q));
209 }
210
211 static void
212 gst_vaapi_decoder_finalize(GObject *object)
213 {
214     GstVaapiDecoder * const        decoder = GST_VAAPI_DECODER(object);
215     GstVaapiDecoderPrivate * const priv    = decoder->priv;
216
217     set_codec_data(decoder, NULL);
218
219     if (priv->caps) {
220         gst_caps_unref(priv->caps);
221         priv->caps = NULL;
222     }
223
224     if (priv->context) {
225         g_object_unref(priv->context);
226         priv->context = NULL;
227         priv->va_context = VA_INVALID_ID;
228     }
229  
230     if (priv->buffers) {
231         clear_queue(priv->buffers, (GDestroyNotify)destroy_buffer);
232         g_queue_free(priv->buffers);
233         priv->buffers = NULL;
234     }
235
236     if (priv->surfaces) {
237         clear_queue(priv->surfaces, (GDestroyNotify)g_object_unref);
238         g_queue_free(priv->surfaces);
239         priv->surfaces = NULL;
240     }
241
242     if (priv->display) {
243         g_object_unref(priv->display);
244         priv->display = NULL;
245         priv->va_display = NULL;
246     }
247
248     G_OBJECT_CLASS(gst_vaapi_decoder_parent_class)->finalize(object);
249 }
250
251 static void
252 gst_vaapi_decoder_set_property(
253     GObject      *object,
254     guint         prop_id,
255     const GValue *value,
256     GParamSpec   *pspec
257 )
258 {
259     GstVaapiDecoder * const        decoder = GST_VAAPI_DECODER(object);
260     GstVaapiDecoderPrivate * const priv    = decoder->priv;
261
262     switch (prop_id) {
263     case PROP_DISPLAY:
264         priv->display = g_object_ref(g_value_get_object(value));
265         if (priv->display)
266             priv->va_display = gst_vaapi_display_get_display(priv->display);
267         else
268             priv->va_display = NULL;
269         break;
270     case PROP_CAPS:
271         set_caps(decoder, g_value_get_pointer(value));
272         break;
273     default:
274         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
275         break;
276     }
277 }
278
279 static void
280 gst_vaapi_decoder_get_property(
281     GObject    *object,
282     guint       prop_id,
283     GValue     *value,
284     GParamSpec *pspec
285 )
286 {
287     GstVaapiDecoderPrivate * const priv = GST_VAAPI_DECODER(object)->priv;
288
289     switch (prop_id) {
290     case PROP_DISPLAY:
291         g_value_set_object(value, priv->display);
292         break;
293     case PROP_CAPS:
294         gst_value_set_caps(value, priv->caps);
295         break;
296     default:
297         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
298         break;
299     }
300 }
301
302 static void
303 gst_vaapi_decoder_class_init(GstVaapiDecoderClass *klass)
304 {
305     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
306
307     g_type_class_add_private(klass, sizeof(GstVaapiDecoderPrivate));
308
309     object_class->finalize     = gst_vaapi_decoder_finalize;
310     object_class->set_property = gst_vaapi_decoder_set_property;
311     object_class->get_property = gst_vaapi_decoder_get_property;
312
313     /**
314      * GstVaapiDecoder:display:
315      *
316      * The #GstVaapiDisplay this decoder is bound to.
317      */
318     g_properties[PROP_DISPLAY] =
319          g_param_spec_object("display",
320                              "Display",
321                              "The GstVaapiDisplay this decoder is bound to",
322                              GST_VAAPI_TYPE_DISPLAY,
323                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
324
325     g_properties[PROP_CAPS] =
326          g_param_spec_pointer("caps",
327                               "Decoder caps",
328                               "The decoder caps",
329                               G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
330
331     g_object_class_install_properties(object_class, N_PROPERTIES, g_properties);
332 }
333
334 static void
335 gst_vaapi_decoder_init(GstVaapiDecoder *decoder)
336 {
337     GstVaapiDecoderPrivate *priv = GST_VAAPI_DECODER_GET_PRIVATE(decoder);
338
339     decoder->priv               = priv;
340     priv->display               = NULL;
341     priv->va_display            = NULL;
342     priv->context               = NULL;
343     priv->va_context            = VA_INVALID_ID;
344     priv->caps                  = NULL;
345     priv->codec                 = 0;
346     priv->codec_data            = NULL;
347     priv->width                 = 0;
348     priv->height                = 0;
349     priv->fps_n                 = 0;
350     priv->fps_d                 = 0;
351     priv->par_n                 = 0;
352     priv->par_d                 = 0;
353     priv->buffers               = g_queue_new();
354     priv->surfaces              = g_queue_new();
355     priv->is_interlaced         = FALSE;
356 }
357
358 /**
359  * gst_vaapi_decoder_get_caps:
360  * @decoder: a #GstVaapiDecoder
361  *
362  * Retrieves the @decoder caps. The deocder owns the returned caps, so
363  * use gst_caps_ref() whenever necessary.
364  *
365  * Return value: the @decoder caps
366  */
367 GstCaps *
368 gst_vaapi_decoder_get_caps(GstVaapiDecoder *decoder)
369 {
370     return decoder->priv->caps;
371 }
372
373 /**
374  * gst_vaapi_decoder_put_buffer:
375  * @decoder: a #GstVaapiDecoder
376  * @buf: a #GstBuffer
377  *
378  * Queues a #GstBuffer to the HW decoder. The decoder holds a
379  * reference to @buf.
380  *
381  * Caller can notify an End-Of-Stream with @buf set to %NULL.
382  *
383  * Return value: %TRUE on success
384  */
385 gboolean
386 gst_vaapi_decoder_put_buffer(GstVaapiDecoder *decoder, GstBuffer *buf)
387 {
388     g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), FALSE);
389
390     return push_buffer(decoder, buf ? gst_buffer_ref(buf) : NULL);
391 }
392
393 /**
394  * gst_vaapi_decoder_get_surface:
395  * @decoder: a #GstVaapiDecoder
396  * @pstatus: return location for the decoder status, or %NULL
397  *
398  * Flushes encoded buffers to the decoder and returns a decoded
399  * surface, if any.
400  *
401  * Return value: a #GstVaapiSurfaceProxy holding the decoded surface,
402  *   or %NULL if none is available (e.g. an error). Caller owns the
403  *   returned object. g_object_unref() after usage.
404  */
405 GstVaapiSurfaceProxy *
406 gst_vaapi_decoder_get_surface(
407     GstVaapiDecoder       *decoder,
408     GstVaapiDecoderStatus *pstatus
409 )
410 {
411     GstVaapiSurfaceProxy *proxy;
412     GstVaapiDecoderStatus status;
413
414     if (pstatus)
415         *pstatus = GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
416
417     g_return_val_if_fail(GST_VAAPI_IS_DECODER(decoder), NULL);
418
419     proxy = pop_surface(decoder);
420     if (!proxy) {
421         do {
422             status = decode_step(decoder);
423         } while (status == GST_VAAPI_DECODER_STATUS_SUCCESS);
424         proxy = pop_surface(decoder);
425     }
426
427     if (proxy)
428         status = GST_VAAPI_DECODER_STATUS_SUCCESS;
429
430     if (pstatus)
431         *pstatus = status;
432     return proxy;
433 }
434
435 void
436 gst_vaapi_decoder_clear_buffer(GstVaapiDecoder *decoder)
437 {
438     GstVaapiDecoderPrivate * const priv = decoder->priv;
439     GstVaapiDecoderClass * const klass = GST_VAAPI_DECODER_GET_CLASS(decoder);
440
441     if (klass->clear_buffer)
442         klass->clear_buffer(decoder);
443
444     if (priv->buffers)
445         clear_queue(priv->buffers, (GDestroyNotify)destroy_buffer);
446
447     if (priv->surfaces)
448         clear_queue(priv->surfaces, (GDestroyNotify)g_object_unref);
449 }
450
451 void
452 gst_vaapi_decoder_set_picture_size(
453     GstVaapiDecoder    *decoder,
454     guint               width,
455     guint               height
456 )
457 {
458     GstVaapiDecoderPrivate * const priv = decoder->priv;
459     gboolean size_changed = FALSE;
460
461     if (priv->width != width) {
462         GST_DEBUG("picture width changed to %d", width);
463         priv->width = width;
464         gst_caps_set_simple(priv->caps, "width", G_TYPE_INT, width, NULL);
465         size_changed = TRUE;
466     }
467
468     if (priv->height != height) {
469         GST_DEBUG("picture height changed to %d", height);
470         priv->height = height;
471         gst_caps_set_simple(priv->caps, "height", G_TYPE_INT, height, NULL);
472         size_changed = TRUE;
473     }
474
475     if (size_changed)
476         g_object_notify_by_pspec(G_OBJECT(decoder), g_properties[PROP_CAPS]);
477 }
478
479 void
480 gst_vaapi_decoder_set_framerate(
481     GstVaapiDecoder    *decoder,
482     guint               fps_n,
483     guint               fps_d
484 )
485 {
486     GstVaapiDecoderPrivate * const priv = decoder->priv;
487
488     if (!fps_n || !fps_d)
489         return;
490
491     if (priv->fps_n != fps_n || priv->fps_d != fps_d) {
492         GST_DEBUG("framerate changed to %u/%u", fps_n, fps_d);
493         priv->fps_n = fps_n;
494         priv->fps_d = fps_d;
495         gst_caps_set_simple(
496             priv->caps,
497             "framerate", GST_TYPE_FRACTION, fps_n, fps_d,
498             NULL
499         );
500         g_object_notify_by_pspec(G_OBJECT(decoder), g_properties[PROP_CAPS]);
501     }
502 }
503
504 void
505 gst_vaapi_decoder_set_pixel_aspect_ratio(
506     GstVaapiDecoder    *decoder,
507     guint               par_n,
508     guint               par_d
509 )
510 {
511     GstVaapiDecoderPrivate * const priv = decoder->priv;
512
513     if (!par_n || !par_d)
514         return;
515
516     if (priv->par_n != par_n || priv->par_d != par_d) {
517         GST_DEBUG("pixel-aspect-ratio changed to %u/%u", par_n, par_d);
518         priv->par_n = par_n;
519         priv->par_d = par_d;
520         gst_caps_set_simple(
521             priv->caps,
522             "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d,
523             NULL
524         );
525         g_object_notify_by_pspec(G_OBJECT(decoder), g_properties[PROP_CAPS]);
526     }
527 }
528
529 void
530 gst_vaapi_decoder_set_interlaced(GstVaapiDecoder *decoder, gboolean interlaced)
531 {
532     GstVaapiDecoderPrivate * const priv = decoder->priv;
533
534     if (priv->is_interlaced != interlaced) {
535         GST_DEBUG("interlaced changed to %s", interlaced ? "true" : "false");
536         priv->is_interlaced = interlaced;
537         gst_caps_set_simple(
538             priv->caps,
539             "interlaced", G_TYPE_BOOLEAN, interlaced,
540             NULL
541         );
542         g_object_notify_by_pspec(G_OBJECT(decoder), g_properties[PROP_CAPS]);
543     }
544 }
545
546 gboolean
547 gst_vaapi_decoder_ensure_context(
548     GstVaapiDecoder    *decoder,
549     GstVaapiProfile     profile,
550     GstVaapiEntrypoint  entrypoint,
551     guint               width,
552     guint               height,
553     guint               surface_num
554 )
555 {
556     GstVaapiDecoderPrivate * const priv = decoder->priv;
557     gboolean ret = TRUE;
558
559     gst_vaapi_decoder_set_picture_size(decoder, width, height);
560
561     if (priv->context) {
562         ret = gst_vaapi_context_reset(priv->context,
563                                        profile, entrypoint,
564                                        width, height,
565                                        surface_num);
566         goto end;
567     }
568
569     priv->context = gst_vaapi_context_new(
570         priv->display,
571         profile,
572         entrypoint,
573         GST_VAAPI_RATECONTROL_NONE,
574         width,
575         height,
576         surface_num
577     );
578     if (!priv->context)
579         return FALSE;
580 end:
581     priv->va_context = gst_vaapi_context_get_id(priv->context);
582     return ret;
583 }
584
585 gboolean
586 gst_vaapi_decoder_push_buffer_sub(
587     GstVaapiDecoder *decoder,
588     GstBuffer       *buffer,
589     guint            offset,
590     guint            size
591 )
592 {
593     GstBuffer *subbuffer;
594
595     subbuffer = gst_buffer_create_sub(buffer, offset, size);
596     if (!subbuffer)
597         return FALSE;
598
599     push_back_buffer(decoder, subbuffer);
600     return TRUE;
601 }
602
603 void
604 gst_vaapi_decoder_push_surface_proxy(
605     GstVaapiDecoder      *decoder,
606     GstVaapiSurfaceProxy *proxy
607 )
608 {
609     return push_surface(decoder, proxy);
610 }
611
612 static gboolean
613 gst_vaapi_decoder_context_increase_surfaces(
614     GstVaapiDecoder *decoder,
615     guint            increment
616 )
617 {
618     GstVaapiDecoderPrivate * const priv = decoder->priv;
619     guint profile, entrypoint;
620     guint width, height;
621     guint surface_num;
622     gboolean ret = TRUE;
623
624     if (!increment)
625         return TRUE;
626
627     if (!priv->context ||
628          (gst_vaapi_context_get_surface_left_capability(priv->context) < increment))
629         return FALSE;
630     g_object_get(G_OBJECT(priv->context),
631                  "profile",     &profile,
632                  "entrypoint",  &entrypoint,
633                  "width",       &width,
634                  "height",      &height,
635                  "surface_num", &surface_num,
636                  NULL);
637     surface_num += increment;
638     ret = gst_vaapi_context_reset(priv->context,
639                                    (GstVaapiProfile)profile,
640                                    (GstVaapiEntrypoint)entrypoint,
641                                    width,
642                                    height,
643                                    surface_num);
644     priv->va_context = gst_vaapi_context_get_id(priv->context);
645     return ret;
646 }
647
648 GstVaapiDecoderStatus
649 gst_vaapi_decoder_check_status(GstVaapiDecoder *decoder)
650 {
651     GstVaapiDecoderPrivate * const priv = decoder->priv;
652     guint increment;
653
654     if (!priv->context || gst_vaapi_context_get_surface_count(priv->context) > 0)
655         return GST_VAAPI_DECODER_STATUS_SUCCESS;
656
657     increment = gst_vaapi_context_get_surface_left_capability(priv->context);
658     if (increment < 1) {
659         GST_DEBUG("decoder context surface pool is out of capability");
660         return GST_VAAPI_DECODER_STATUS_ERROR_NO_SURFACE;
661     }
662     if (increment > 2)
663         increment = 2;
664
665     if (!gst_vaapi_decoder_context_increase_surfaces(decoder, increment)) {
666         GST_ERROR("increase decoder surface failed");
667         return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
668     }
669     return GST_VAAPI_DECODER_STATUS_SUCCESS;
670 }