Drop excessive threading that over-complicates synchronisation.
[profile/ivi/gstreamer-vaapi.git] / gst / vaapidecode / gstvaapidecode.c
1 /*
2  *  gstvaapidecode.c - VA-API video decoder
3  *
4  *  gstreamer-vaapi (C) 2010 Splitted-Desktop Systems
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program 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
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
19  */
20
21 /**
22  * SECTION:gstvaapidecode
23  * @short_description: A VA-API based video decoder
24  *
25  * vaapidecode decodes from raw bitstreams to surfaces suitable for
26  * the vaapisink element.
27  */
28
29 #include "config.h"
30 #include "gstvaapidecode.h"
31 #include <gst/vaapi/gstvaapivideosink.h>
32 #include <gst/vaapi/gstvaapivideobuffer.h>
33 #include <gst/vaapi/gstvaapidecoder_ffmpeg.h>
34
35 #define GST_PLUGIN_NAME "vaapidecode"
36 #define GST_PLUGIN_DESC "A VA-API based video decoder"
37
38 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapidecode);
39 #define GST_CAT_DEFAULT gst_debug_vaapidecode
40
41 /* ElementFactory information */
42 static const GstElementDetails gst_vaapidecode_details =
43     GST_ELEMENT_DETAILS(
44         "Video decode",
45         "Codec/Decoder/Video",
46         GST_PLUGIN_DESC,
47         "Gwenole Beauchesne <gbeauchesne@splitted-desktop.com>");
48
49 /* Default templates */
50 #define GST_CAPS_CODEC(CODEC)                   \
51     CODEC ", "                                  \
52     "width = (int) [ 1, MAX ], "                \
53     "height = (int) [ 1, MAX ]; "
54
55 static const char gst_vaapidecode_sink_caps_str[] =
56     GST_CAPS_CODEC("video/mpeg, mpegversion=2")
57     GST_CAPS_CODEC("video/mpeg, mpegversion=4")
58     GST_CAPS_CODEC("video/x-h263")
59     GST_CAPS_CODEC("video/x-h264")
60     GST_CAPS_CODEC("video/x-vc1")
61     ;
62
63 static const char gst_vaapidecode_src_caps_str[] =
64     "video/x-vaapi-surface, "
65     "width = (int) [ 1, MAX ], "
66     "height = (int) [ 1, MAX ]; ";
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 GstFlowReturn
95 gst_vaapidecode_step(GstVaapiDecode *decode)
96 {
97     GstVaapiSurfaceProxy *proxy;
98     GstVaapiDecoderStatus status;
99     GstBuffer *buffer;
100     GstFlowReturn ret;
101
102     proxy = gst_vaapi_decoder_get_surface(decode->decoder, &status);
103     if (!proxy) {
104         if (status != GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA)
105             goto error_decode;
106         /* More data is needed */
107         return GST_FLOW_OK;
108     }
109
110     buffer = NULL;
111     ret = gst_pad_alloc_buffer(
112         decode->srcpad,
113         0, 0,
114         GST_PAD_CAPS(decode->srcpad),
115         &buffer
116     );
117     if (ret != GST_FLOW_OK || !buffer)
118         goto error_create_buffer;
119
120     GST_BUFFER_TIMESTAMP(buffer) = GST_VAAPI_SURFACE_PROXY_TIMESTAMP(proxy);
121     gst_vaapi_video_buffer_set_surface_proxy(
122         GST_VAAPI_VIDEO_BUFFER(buffer),
123         proxy
124     );
125
126     ret = gst_pad_push(decode->srcpad, buffer);
127     if (ret != GST_FLOW_OK)
128         goto error_commit_buffer;
129
130     g_object_unref(proxy);
131     return GST_FLOW_OK;
132
133     /* ERRORS */
134 error_decode:
135     {
136         GST_DEBUG("decode error %d", status);
137         return GST_FLOW_UNEXPECTED;
138     }
139 error_create_buffer:
140     {
141         const GstVaapiID surface_id =
142             gst_vaapi_surface_get_id(GST_VAAPI_SURFACE_PROXY_SURFACE(proxy));
143
144         GST_DEBUG("video sink failed to create video buffer for proxy'ed "
145                   "surface %" GST_VAAPI_ID_FORMAT " (error %d)",
146                   GST_VAAPI_ID_ARGS(surface_id), ret);
147         g_object_unref(proxy);
148         return GST_FLOW_UNEXPECTED;
149     }
150 error_commit_buffer:
151     {
152         GST_DEBUG("video sink rejected the video buffer (error %d)", ret);
153         g_object_unref(proxy);
154         return GST_FLOW_UNEXPECTED;
155     }
156 }
157
158 static gboolean
159 gst_vaapidecode_create(GstVaapiDecode *decode)
160 {
161     GstVaapiVideoSink *sink;
162     GstVaapiDisplay *display;
163     GstVaapiCodec codec;
164
165     /* Look for a downstream vaapisink */
166     sink = gst_vaapi_video_sink_lookup(GST_ELEMENT(decode));
167     if (!sink)
168         return FALSE;
169
170     display = gst_vaapi_video_sink_get_display(sink);
171     if (!display)
172         return FALSE;
173
174     decode->display = g_object_ref(display);
175
176     codec = gst_vaapi_profile_get_codec(decode->profile);
177     if (!codec)
178         return FALSE;
179
180     if (decode->use_ffmpeg)
181         decode->decoder =
182             gst_vaapi_decoder_ffmpeg_new(display, codec, decode->codec_data);
183     return decode->decoder != NULL;
184 }
185
186 static void
187 gst_vaapidecode_destroy(GstVaapiDecode *decode)
188 {
189     if (decode->decoder) {
190         gst_vaapi_decoder_put_buffer(decode->decoder, NULL);
191         g_object_unref(decode->decoder);
192         decode->decoder = NULL;
193     }
194
195     if (decode->codec_data) {
196         gst_buffer_unref(decode->codec_data);
197         decode->codec_data = NULL;
198     }
199
200     if (decode->display) {
201         g_object_unref(decode->display);
202         decode->display = NULL;
203     }
204 }
205
206 static void gst_vaapidecode_base_init(gpointer klass)
207 {
208     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
209
210     gst_element_class_set_details(element_class, &gst_vaapidecode_details);
211
212     /* sink pad */
213     gst_element_class_add_pad_template(
214         element_class,
215         gst_static_pad_template_get(&gst_vaapidecode_sink_factory)
216     );
217
218     /* src pad */
219     gst_element_class_add_pad_template(
220         element_class,
221         gst_static_pad_template_get(&gst_vaapidecode_src_factory)
222     );
223 }
224
225 static void
226 gst_vaapidecode_finalize(GObject *object)
227 {
228     gst_vaapidecode_destroy(GST_VAAPIDECODE(object));
229
230     G_OBJECT_CLASS(parent_class)->finalize(object);
231 }
232
233 static void
234 gst_vaapidecode_set_property(
235     GObject      *object,
236     guint         prop_id,
237     const GValue *value,
238     GParamSpec   *pspec
239 )
240 {
241     GstVaapiDecode * const decode = GST_VAAPIDECODE(object);
242
243     switch (prop_id) {
244     case PROP_USE_FFMPEG:
245         decode->use_ffmpeg = g_value_get_boolean(value);
246         break;
247     default:
248         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
249         break;
250     }
251 }
252
253 static void
254 gst_vaapidecode_get_property(
255     GObject    *object,
256     guint       prop_id,
257     GValue     *value,
258     GParamSpec *pspec
259 )
260 {
261     GstVaapiDecode * const decode = GST_VAAPIDECODE(object);
262
263     switch (prop_id) {
264     case PROP_USE_FFMPEG:
265         g_value_set_boolean(value, decode->use_ffmpeg);
266         break;
267     default:
268         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
269         break;
270     }
271 }
272
273 static GstStateChangeReturn
274 gst_vaapidecode_change_state(GstElement *element, GstStateChange transition)
275 {
276     GstVaapiDecode * const decode = GST_VAAPIDECODE(element);
277     GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
278
279     switch (transition) {
280     case GST_STATE_CHANGE_READY_TO_PAUSED:
281         break;
282     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
283         break;
284     default:
285         break;
286     }
287
288     ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
289     if (ret != GST_STATE_CHANGE_SUCCESS)
290         return ret;
291
292     switch (transition) {
293     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
294         break;
295     case GST_STATE_CHANGE_PAUSED_TO_READY:
296         break;
297     default:
298         break;
299     }
300     return GST_STATE_CHANGE_SUCCESS;
301 }
302
303 static void
304 gst_vaapidecode_class_init(GstVaapiDecodeClass *klass)
305 {
306     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
307     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
308
309     object_class->finalize      = gst_vaapidecode_finalize;
310     object_class->set_property  = gst_vaapidecode_set_property;
311     object_class->get_property  = gst_vaapidecode_get_property;
312
313     element_class->change_state = gst_vaapidecode_change_state;
314
315     g_object_class_install_property
316         (object_class,
317          PROP_USE_FFMPEG,
318          g_param_spec_boolean("use-ffmpeg",
319                               "Use FFmpeg/VAAPI for decoding",
320                               "Uses FFmpeg/VAAPI for decoding",
321                               TRUE,
322                               G_PARAM_READWRITE));
323 }
324
325 static gboolean
326 gst_vaapidecode_set_caps(GstPad *pad, GstCaps *caps)
327 {
328     GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
329     GstPad *other_pad;
330     GstCaps *other_caps = NULL;
331     GstStructure *structure;
332     const GValue *v_width, *v_height, *v_framerate, *v_par, *v_codec_data;
333
334     if (pad == decode->sinkpad) {
335         other_pad  = decode->srcpad;
336         other_caps = gst_caps_from_string(gst_vaapidecode_src_caps_str);
337     }
338     else {
339         other_pad  = decode->sinkpad;
340         other_caps = gst_caps_from_string(gst_vaapidecode_sink_caps_str);
341     }
342
343     /* Negotiation succeeded, so now configure ourselves */
344     structure    = gst_caps_get_structure(caps, 0);
345     v_width      = gst_structure_get_value(structure, "width");
346     v_height     = gst_structure_get_value(structure, "height");
347     v_framerate  = gst_structure_get_value(structure, "framerate");
348     v_par        = gst_structure_get_value(structure, "pixel-aspect-ratio");
349     v_codec_data = gst_structure_get_value(structure, "codec_data");
350
351     if (pad == decode->sinkpad) {
352         decode->profile = gst_vaapi_profile_from_caps(caps);
353         if (v_codec_data)
354             decode->codec_data =
355                 gst_buffer_ref(gst_value_get_buffer(v_codec_data));
356     }
357
358     structure = gst_caps_get_structure(other_caps, 0);
359     gst_structure_set_value(structure, "width", v_width);
360     gst_structure_set_value(structure, "height", v_height);
361     if (v_framerate)
362         gst_structure_set_value(structure, "framerate", v_framerate);
363     if (v_par)
364         gst_structure_set_value(structure, "pixel-aspect-ratio", v_par);
365
366     return gst_pad_set_caps(other_pad, other_caps);
367 }
368
369 static GstFlowReturn
370 gst_vaapidecode_chain(GstPad *pad, GstBuffer *buf)
371 {
372     GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
373
374     if (!decode->decoder) {
375         if (!gst_vaapidecode_create(decode))
376             goto error_create_decoder;
377     }
378
379     if (!gst_vaapi_decoder_put_buffer(decode->decoder, buf))
380         goto error_push_buffer;
381
382     gst_buffer_unref(buf);
383     return gst_vaapidecode_step(decode);
384
385     /* ERRORS */
386 error_create_decoder:
387     {
388         GST_DEBUG("failed to create decoder");
389         gst_buffer_unref(buf);
390         return GST_FLOW_UNEXPECTED;
391     }
392 error_push_buffer:
393     {
394         GST_DEBUG("failed to push input buffer to decoder");
395         gst_buffer_unref(buf);
396         return GST_FLOW_UNEXPECTED;
397     }
398 }
399
400 static gboolean
401 gst_vaapidecode_sink_event(GstPad *pad, GstEvent *event)
402 {
403     GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
404
405     GST_DEBUG("handle sink event '%s'", GST_EVENT_TYPE_NAME(event));
406
407     /* Propagate event downstream */
408     return gst_pad_push_event(decode->srcpad, event);
409 }
410
411 static gboolean
412 gst_vaapidecode_src_event(GstPad *pad, GstEvent *event)
413 {
414     GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
415
416     GST_DEBUG("handle src event '%s'", GST_EVENT_TYPE_NAME(event));
417
418     /* Propagate event upstream */
419     return gst_pad_push_event(decode->sinkpad, event);
420 }
421
422 static void
423 gst_vaapidecode_init(GstVaapiDecode *decode, GstVaapiDecodeClass *klass)
424 {
425     GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
426
427     decode->display     = NULL;
428     decode->profile     = 0;
429     decode->codec_data  = NULL;
430     decode->decoder     = NULL;
431     decode->use_ffmpeg  = TRUE;
432
433     /* Pad through which data comes in to the element */
434     decode->sinkpad = gst_pad_new_from_template(
435         gst_element_class_get_pad_template(element_class, "sink"),
436         "sink"
437     );
438
439     gst_pad_set_setcaps_function(decode->sinkpad, gst_vaapidecode_set_caps);
440     gst_pad_set_chain_function(decode->sinkpad, gst_vaapidecode_chain);
441     gst_pad_set_event_function(decode->sinkpad, gst_vaapidecode_sink_event);
442     gst_element_add_pad(GST_ELEMENT(decode), decode->sinkpad);
443
444     /* Pad through which data goes out of the element */
445     decode->srcpad = gst_pad_new_from_template(
446         gst_element_class_get_pad_template(element_class, "src"),
447         "src"
448     );
449
450     gst_pad_set_event_function(decode->srcpad, gst_vaapidecode_src_event);
451     gst_element_add_pad(GST_ELEMENT(decode), decode->srcpad);
452 }
453
454 static gboolean plugin_init(GstPlugin *plugin)
455 {
456     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapidecode,
457                             GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
458
459     return gst_element_register(plugin,
460                                 GST_PLUGIN_NAME,
461                                 GST_RANK_PRIMARY,
462                                 GST_TYPE_VAAPIDECODE);
463 }
464
465 GST_PLUGIN_DEFINE(
466     GST_VERSION_MAJOR, GST_VERSION_MINOR,
467     GST_PLUGIN_NAME,
468     GST_PLUGIN_DESC,
469     plugin_init,
470     PACKAGE_VERSION,
471     "GPL",
472     PACKAGE,
473     PACKAGE_BUGREPORT);