13dad07ed1756948f2eaa4f8e8144b39e63465ea
[profile/ivi/gstreamer-vaapi.git] / gst / vaapidecode / gstvaapidecode.c
1 /*
2  *  gstvaapidecode.c - VA-API video decoder
3  *
4  *  gstreamer-vaapi (C) 2010-2011 Splitted-Desktop Systems
5  *  Copyright (C) 2011 Intel Corporation
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program 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
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
20  */
21
22 /**
23  * SECTION:gstvaapidecode
24  * @short_description: A VA-API based video decoder
25  *
26  * vaapidecode decodes from raw bitstreams to surfaces suitable for
27  * the vaapisink element.
28  */
29
30 #include "config.h"
31 #include "gstvaapidecode.h"
32 #include <gst/vaapi/gstvaapidisplay_x11.h>
33 #include <gst/vaapi/gstvaapivideosink.h>
34 #include <gst/vaapi/gstvaapivideobuffer.h>
35 #include <gst/vaapi/gstvaapidecoder_ffmpeg.h>
36 #include <gst/vaapi/gstvaapiutils_gst.h>
37
38 #define GST_PLUGIN_NAME "vaapidecode"
39 #define GST_PLUGIN_DESC "A VA-API based video decoder"
40
41 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapidecode);
42 #define GST_CAT_DEFAULT gst_debug_vaapidecode
43
44 /* ElementFactory information */
45 static const GstElementDetails gst_vaapidecode_details =
46     GST_ELEMENT_DETAILS(
47         "VA-API decoder",
48         "Codec/Decoder/Video",
49         GST_PLUGIN_DESC,
50         "Gwenole Beauchesne <gbeauchesne@splitted-desktop.com>");
51
52 /* Default templates */
53 #define GST_CAPS_CODEC(CODEC) CODEC "; "
54
55 static const char gst_vaapidecode_sink_caps_str[] =
56     GST_CAPS_CODEC("video/mpeg, mpegversion=2, systemstream=(boolean)false")
57     GST_CAPS_CODEC("video/mpeg, mpegversion=4")
58     GST_CAPS_CODEC("video/x-divx")
59     GST_CAPS_CODEC("video/x-xvid")
60     GST_CAPS_CODEC("video/x-h263")
61     GST_CAPS_CODEC("video/x-h264")
62     GST_CAPS_CODEC("video/x-wmv")
63     ;
64
65 static const char gst_vaapidecode_src_caps_str[] =
66     GST_VAAPI_SURFACE_CAPS;
67
68 static GstStaticPadTemplate gst_vaapidecode_sink_factory =
69     GST_STATIC_PAD_TEMPLATE(
70         "sink",
71         GST_PAD_SINK,
72         GST_PAD_ALWAYS,
73         GST_STATIC_CAPS(gst_vaapidecode_sink_caps_str));
74
75 static GstStaticPadTemplate gst_vaapidecode_src_factory =
76     GST_STATIC_PAD_TEMPLATE(
77         "src",
78         GST_PAD_SRC,
79         GST_PAD_ALWAYS,
80         GST_STATIC_CAPS(gst_vaapidecode_src_caps_str));
81
82 GST_BOILERPLATE(
83     GstVaapiDecode,
84     gst_vaapidecode,
85     GstElement,
86     GST_TYPE_ELEMENT);
87
88 enum {
89     PROP_0,
90
91     PROP_USE_FFMPEG,
92 };
93
94 static gboolean
95 gst_vaapidecode_update_src_caps(GstVaapiDecode *decode, GstCaps *caps);
96
97 static void
98 gst_vaapi_decoder_notify_caps(GObject *obj, GParamSpec *pspec, void *user_data)
99 {
100     GstVaapiDecode * const decode = GST_VAAPIDECODE(user_data);
101     GstCaps *caps;
102
103     g_assert(decode->decoder == GST_VAAPI_DECODER(obj));
104
105     caps = gst_vaapi_decoder_get_caps(decode->decoder);
106     gst_vaapidecode_update_src_caps(decode, caps);
107 }
108
109 static inline gboolean
110 gst_vaapidecode_update_sink_caps(GstVaapiDecode *decode, GstCaps *caps)
111 {
112     if (decode->sinkpad_caps)
113         gst_caps_unref(decode->sinkpad_caps);
114     decode->sinkpad_caps = gst_caps_ref(caps);
115     return TRUE;
116 }
117
118 static gboolean
119 gst_vaapidecode_update_src_caps(GstVaapiDecode *decode, GstCaps *caps)
120 {
121     GstCaps *other_caps;
122     GstStructure *structure;
123     const GValue *v_width, *v_height, *v_framerate, *v_par;
124     gboolean success;
125
126     if (!decode->srcpad_caps) {
127         decode->srcpad_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS_NAME);
128         if (!decode->srcpad_caps)
129             return FALSE;
130     }
131
132     structure    = gst_caps_get_structure(caps, 0);
133     v_width      = gst_structure_get_value(structure, "width");
134     v_height     = gst_structure_get_value(structure, "height");
135     v_framerate  = gst_structure_get_value(structure, "framerate");
136     v_par        = gst_structure_get_value(structure, "pixel-aspect-ratio");
137
138     structure = gst_caps_get_structure(decode->srcpad_caps, 0);
139     if (v_width && v_height) {
140         gst_structure_set_value(structure, "width", v_width);
141         gst_structure_set_value(structure, "height", v_height);
142     }
143     if (v_framerate)
144         gst_structure_set_value(structure, "framerate", v_framerate);
145     if (v_par)
146         gst_structure_set_value(structure, "pixel-aspect-ratio", v_par);
147
148     other_caps = gst_caps_copy(decode->srcpad_caps);
149     success = gst_pad_set_caps(decode->srcpad, other_caps);
150     gst_caps_unref(other_caps);
151     return success;
152 }
153
154 static void
155 gst_vaapidecode_release(GstVaapiDecode *decode, GObject *dead_object)
156 {
157     g_mutex_lock(decode->decoder_mutex);
158     g_cond_signal(decode->decoder_ready);
159     g_mutex_unlock(decode->decoder_mutex);
160 }
161
162 static GstFlowReturn
163 gst_vaapidecode_step(GstVaapiDecode *decode)
164 {
165     GstVaapiSurfaceProxy *proxy;
166     GstVaapiDecoderStatus status;
167     GstBuffer *buffer;
168     GstFlowReturn ret;
169     guint tries;
170
171     for (;;) {
172         tries = 0;
173     again:
174         proxy = gst_vaapi_decoder_get_surface(decode->decoder, &status);
175         if (!proxy) {
176             if (status == GST_VAAPI_DECODER_STATUS_ERROR_NO_SURFACE) {
177                 /* Wait for a VA surface to be displayed and free'd */
178                 if (++tries > 100)
179                     goto error_decode_timeout;
180                 GTimeVal timeout;
181                 g_get_current_time(&timeout);
182                 g_time_val_add(&timeout, 10000); /* 10 ms each step */
183                 g_mutex_lock(decode->decoder_mutex);
184                 g_cond_timed_wait(
185                     decode->decoder_ready,
186                     decode->decoder_mutex,
187                     &timeout
188                 );
189                 g_mutex_unlock(decode->decoder_mutex);
190                 goto again;
191             }
192             if (status != GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA)
193                 goto error_decode;
194             /* More data is needed */
195             break;
196         }
197
198         g_object_weak_ref(
199             G_OBJECT(proxy),
200             (GWeakNotify)gst_vaapidecode_release,
201             decode
202         );
203
204         buffer = NULL;
205         ret = gst_pad_alloc_buffer(
206             decode->srcpad,
207             0, 0,
208             GST_PAD_CAPS(decode->srcpad),
209             &buffer
210         );
211         if (ret != GST_FLOW_OK || !buffer)
212             goto error_create_buffer;
213
214         GST_BUFFER_TIMESTAMP(buffer) = GST_VAAPI_SURFACE_PROXY_TIMESTAMP(proxy);
215         gst_vaapi_video_buffer_set_surface_proxy(
216             GST_VAAPI_VIDEO_BUFFER(buffer),
217             proxy
218         );
219
220         ret = gst_pad_push(decode->srcpad, buffer);
221         if (ret != GST_FLOW_OK)
222             goto error_commit_buffer;
223
224         g_object_unref(proxy);
225     }
226     return GST_FLOW_OK;
227
228     /* ERRORS */
229 error_decode_timeout:
230     {
231         GST_DEBUG("decode timeout. Decoder required a VA surface but none "
232                   "got available within one second");
233         return GST_FLOW_UNEXPECTED;
234     }
235 error_decode:
236     {
237         GST_DEBUG("decode error %d", status);
238         return GST_FLOW_UNEXPECTED;
239     }
240 error_create_buffer:
241     {
242         const GstVaapiID surface_id =
243             gst_vaapi_surface_get_id(GST_VAAPI_SURFACE_PROXY_SURFACE(proxy));
244
245         GST_DEBUG("video sink failed to create video buffer for proxy'ed "
246                   "surface %" GST_VAAPI_ID_FORMAT " (error %d)",
247                   GST_VAAPI_ID_ARGS(surface_id), ret);
248         g_object_unref(proxy);
249         return GST_FLOW_UNEXPECTED;
250     }
251 error_commit_buffer:
252     {
253         GST_DEBUG("video sink rejected the video buffer (error %d)", ret);
254         g_object_unref(proxy);
255         return GST_FLOW_UNEXPECTED;
256     }
257 }
258
259 static inline gboolean
260 gst_vaapidecode_ensure_display(GstVaapiDecode *decode)
261 {
262     GstVaapiDisplay *display;
263
264     if (!decode->display) {
265         display = gst_vaapi_display_lookup_downstream(GST_ELEMENT(decode));
266         if (!display)
267             return FALSE;
268         decode->display = g_object_ref(display);
269     }
270     return TRUE;
271 }
272
273 static gboolean
274 gst_vaapidecode_create(GstVaapiDecode *decode, GstCaps *caps)
275 {
276     if (!gst_vaapidecode_ensure_display(decode))
277         return FALSE;
278
279     decode->decoder_mutex = g_mutex_new();
280     if (!decode->decoder_mutex)
281         return FALSE;
282
283     decode->decoder_ready = g_cond_new();
284     if (!decode->decoder_ready)
285         return FALSE;
286
287     if (decode->use_ffmpeg)
288         decode->decoder = gst_vaapi_decoder_ffmpeg_new(decode->display, caps);
289     if (!decode->decoder)
290         return FALSE;
291
292     g_signal_connect(
293         G_OBJECT(decode->decoder),
294         "notify::caps",
295         G_CALLBACK(gst_vaapi_decoder_notify_caps),
296         decode
297     );
298
299     decode->decoder_caps = gst_caps_ref(caps);
300     return TRUE;
301 }
302
303 static void
304 gst_vaapidecode_destroy(GstVaapiDecode *decode)
305 {
306     if (decode->decoder_ready) {
307         gst_vaapidecode_release(decode, NULL);
308         g_cond_free(decode->decoder_ready);
309         decode->decoder_ready = NULL;
310     }
311
312     if (decode->decoder_mutex) {
313         g_mutex_free(decode->decoder_mutex);
314         decode->decoder_mutex = NULL;
315     }
316
317     if (decode->decoder) {
318         gst_vaapi_decoder_put_buffer(decode->decoder, NULL);
319         g_object_unref(decode->decoder);
320         decode->decoder = NULL;
321     }
322
323     if (decode->decoder_caps) {
324         gst_caps_unref(decode->decoder_caps);
325         decode->decoder_caps = NULL;
326     }
327 }
328
329 static gboolean
330 gst_vaapidecode_reset(GstVaapiDecode *decode, GstCaps *caps)
331 {
332     if (decode->decoder &&
333         decode->decoder_caps &&
334         gst_caps_is_always_compatible(caps, decode->decoder_caps))
335         return TRUE;
336
337     gst_vaapidecode_destroy(decode);
338     return gst_vaapidecode_create(decode, caps);
339 }
340
341 static void
342 gst_vaapidecode_base_init(gpointer klass)
343 {
344     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
345
346     gst_element_class_set_details(element_class, &gst_vaapidecode_details);
347
348     /* sink pad */
349     gst_element_class_add_pad_template(
350         element_class,
351         gst_static_pad_template_get(&gst_vaapidecode_sink_factory)
352     );
353
354     /* src pad */
355     gst_element_class_add_pad_template(
356         element_class,
357         gst_static_pad_template_get(&gst_vaapidecode_src_factory)
358     );
359 }
360
361 static void
362 gst_vaapidecode_finalize(GObject *object)
363 {
364     GstVaapiDecode * const decode = GST_VAAPIDECODE(object);
365
366     gst_vaapidecode_destroy(decode);
367
368     if (decode->sinkpad_caps) {
369         gst_caps_unref(decode->sinkpad_caps);
370         decode->sinkpad_caps = NULL;
371     }
372
373     if (decode->srcpad_caps) {
374         gst_caps_unref(decode->srcpad_caps);
375         decode->srcpad_caps = NULL;
376     }
377
378     if (decode->display) {
379         g_object_unref(decode->display);
380         decode->display = NULL;
381     }
382
383     if (decode->allowed_caps) {
384         gst_caps_unref(decode->allowed_caps);
385         decode->allowed_caps = NULL;
386     }
387
388     G_OBJECT_CLASS(parent_class)->finalize(object);
389 }
390
391 static void
392 gst_vaapidecode_set_property(
393     GObject      *object,
394     guint         prop_id,
395     const GValue *value,
396     GParamSpec   *pspec
397 )
398 {
399     GstVaapiDecode * const decode = GST_VAAPIDECODE(object);
400
401     switch (prop_id) {
402     case PROP_USE_FFMPEG:
403         decode->use_ffmpeg = g_value_get_boolean(value);
404         break;
405     default:
406         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
407         break;
408     }
409 }
410
411 static void
412 gst_vaapidecode_get_property(
413     GObject    *object,
414     guint       prop_id,
415     GValue     *value,
416     GParamSpec *pspec
417 )
418 {
419     GstVaapiDecode * const decode = GST_VAAPIDECODE(object);
420
421     switch (prop_id) {
422     case PROP_USE_FFMPEG:
423         g_value_set_boolean(value, decode->use_ffmpeg);
424         break;
425     default:
426         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
427         break;
428     }
429 }
430
431 static GstStateChangeReturn
432 gst_vaapidecode_change_state(GstElement *element, GstStateChange transition)
433 {
434     GstVaapiDecode * const decode = GST_VAAPIDECODE(element);
435     GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
436
437     switch (transition) {
438     case GST_STATE_CHANGE_READY_TO_PAUSED:
439         break;
440     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
441         break;
442     default:
443         break;
444     }
445
446     ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
447     if (ret != GST_STATE_CHANGE_SUCCESS)
448         return ret;
449
450     switch (transition) {
451     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
452         break;
453     case GST_STATE_CHANGE_PAUSED_TO_READY:
454         break;
455     default:
456         break;
457     }
458     return GST_STATE_CHANGE_SUCCESS;
459 }
460
461 static void
462 gst_vaapidecode_class_init(GstVaapiDecodeClass *klass)
463 {
464     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
465     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
466
467     object_class->finalize      = gst_vaapidecode_finalize;
468     object_class->set_property  = gst_vaapidecode_set_property;
469     object_class->get_property  = gst_vaapidecode_get_property;
470
471     element_class->change_state = gst_vaapidecode_change_state;
472
473     g_object_class_install_property
474         (object_class,
475          PROP_USE_FFMPEG,
476          g_param_spec_boolean("use-ffmpeg",
477                               "Use FFmpeg/VAAPI for decoding",
478                               "Uses FFmpeg/VAAPI for decoding",
479                               TRUE,
480                               G_PARAM_READWRITE));
481 }
482
483 static gboolean
484 gst_vaapidecode_ensure_allowed_caps(GstVaapiDecode *decode)
485 {
486     GstVaapiDisplay *display;
487     GstCaps *decode_caps;
488     guint i, n_decode_caps;
489
490     if (decode->allowed_caps)
491         return TRUE;
492
493     if (gst_vaapidecode_ensure_display(decode))
494         display = g_object_ref(decode->display);
495     else {
496         display = gst_vaapi_display_x11_new(NULL);
497         if (!display)
498             goto error_no_display;
499     }
500
501     decode_caps = gst_vaapi_display_get_decode_caps(display);
502     if (!decode_caps)
503         goto error_no_decode_caps;
504     n_decode_caps = gst_caps_get_size(decode_caps);
505
506     decode->allowed_caps = gst_caps_new_empty();
507     if (!decode->allowed_caps)
508         goto error_no_memory;
509
510     for (i = 0; i < n_decode_caps; i++) {
511         GstStructure *structure;
512         structure = gst_caps_get_structure(decode_caps, i);
513         if (!structure)
514             continue;
515         structure = gst_structure_copy(structure);
516         if (!structure)
517             continue;
518         gst_structure_remove_field(structure, "profile");
519         gst_structure_set(
520             structure,
521             "width",  GST_TYPE_INT_RANGE, 1, G_MAXINT,
522             "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
523             NULL
524         );
525         gst_caps_merge_structure(decode->allowed_caps, structure);
526     }
527
528     gst_caps_unref(decode_caps);
529     g_object_unref(display);
530     return TRUE;
531
532     /* ERRORS */
533 error_no_display:
534     {
535         GST_DEBUG("failed to retrieve VA display");
536         return FALSE;
537     }
538 error_no_decode_caps:
539     {
540         GST_DEBUG("failed to retrieve VA decode caps");
541         g_object_unref(display);
542         return FALSE;
543     }
544 error_no_memory:
545     {
546         GST_DEBUG("failed to allocate allowed-caps set");
547         gst_caps_unref(decode_caps);
548         g_object_unref(display);
549         return FALSE;
550     }
551 }
552
553 static GstCaps *
554 gst_vaapidecode_get_caps(GstPad *pad)
555 {
556     GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
557
558     if (!gst_vaapidecode_ensure_allowed_caps(decode))
559         return gst_caps_new_empty();
560
561     return gst_caps_ref(decode->allowed_caps);
562 }
563
564 static gboolean
565 gst_vaapidecode_set_caps(GstPad *pad, GstCaps *caps)
566 {
567     GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
568
569     g_return_val_if_fail(pad == decode->sinkpad, FALSE);
570
571     if (!gst_vaapidecode_update_sink_caps(decode, caps))
572         return FALSE;
573     if (!gst_vaapidecode_update_src_caps(decode, caps))
574         return FALSE;
575     return gst_vaapidecode_reset(decode, decode->sinkpad_caps);
576 }
577
578 static GstFlowReturn
579 gst_vaapidecode_chain(GstPad *pad, GstBuffer *buf)
580 {
581     GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
582
583     if (!gst_vaapi_decoder_put_buffer(decode->decoder, buf))
584         goto error_push_buffer;
585
586     gst_buffer_unref(buf);
587     return gst_vaapidecode_step(decode);
588
589     /* ERRORS */
590 error_push_buffer:
591     {
592         GST_DEBUG("failed to push input buffer to decoder");
593         gst_buffer_unref(buf);
594         return GST_FLOW_UNEXPECTED;
595     }
596 }
597
598 static gboolean
599 gst_vaapidecode_sink_event(GstPad *pad, GstEvent *event)
600 {
601     GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
602
603     GST_DEBUG("handle sink event '%s'", GST_EVENT_TYPE_NAME(event));
604
605     /* Propagate event downstream */
606     return gst_pad_push_event(decode->srcpad, event);
607 }
608
609 static gboolean
610 gst_vaapidecode_src_event(GstPad *pad, GstEvent *event)
611 {
612     GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
613
614     GST_DEBUG("handle src event '%s'", GST_EVENT_TYPE_NAME(event));
615
616     /* Propagate event upstream */
617     return gst_pad_push_event(decode->sinkpad, event);
618 }
619
620 static void
621 gst_vaapidecode_init(GstVaapiDecode *decode, GstVaapiDecodeClass *klass)
622 {
623     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
624
625     decode->display             = NULL;
626     decode->decoder             = NULL;
627     decode->decoder_mutex       = NULL;
628     decode->decoder_ready       = NULL;
629     decode->decoder_caps        = NULL;
630     decode->allowed_caps        = NULL;
631     decode->use_ffmpeg          = TRUE;
632
633     /* Pad through which data comes in to the element */
634     decode->sinkpad = gst_pad_new_from_template(
635         gst_element_class_get_pad_template(element_class, "sink"),
636         "sink"
637     );
638     decode->sinkpad_caps = NULL;
639
640     gst_pad_set_getcaps_function(decode->sinkpad, gst_vaapidecode_get_caps);
641     gst_pad_set_setcaps_function(decode->sinkpad, gst_vaapidecode_set_caps);
642     gst_pad_set_chain_function(decode->sinkpad, gst_vaapidecode_chain);
643     gst_pad_set_event_function(decode->sinkpad, gst_vaapidecode_sink_event);
644     gst_element_add_pad(GST_ELEMENT(decode), decode->sinkpad);
645
646     /* Pad through which data goes out of the element */
647     decode->srcpad = gst_pad_new_from_template(
648         gst_element_class_get_pad_template(element_class, "src"),
649         "src"
650     );
651     decode->srcpad_caps = NULL;
652
653     gst_pad_use_fixed_caps(decode->srcpad);
654     gst_pad_set_event_function(decode->srcpad, gst_vaapidecode_src_event);
655     gst_element_add_pad(GST_ELEMENT(decode), decode->srcpad);
656 }
657
658 static gboolean
659 plugin_init(GstPlugin *plugin)
660 {
661     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapidecode,
662                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
663
664     return gst_element_register(plugin,
665                                 GST_PLUGIN_NAME,
666                                 GST_RANK_PRIMARY,
667                                 GST_TYPE_VAAPIDECODE);
668 }
669
670 GST_PLUGIN_DEFINE(
671     GST_VERSION_MAJOR, GST_VERSION_MINOR,
672     GST_PLUGIN_NAME,
673     GST_PLUGIN_DESC,
674     plugin_init,
675     PACKAGE_VERSION,
676     "GPL",
677     PACKAGE,
678     PACKAGE_BUGREPORT);