2 * gstvaapidecode.c - VA-API video decoder
4 * gstreamer-vaapi (C) 2010 Splitted-Desktop Systems
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.
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.
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
22 * SECTION:gstvaapidecode
23 * @short_description: A VA-API based video decoder
25 * vaapidecode decodes from raw bitstreams to surfaces suitable for
26 * the vaapisink element.
30 #include "gstvaapidecode.h"
31 #include <gst/vaapi/gstvaapivideosink.h>
32 #include <gst/vaapi/gstvaapivideobuffer.h>
33 #include <gst/vaapi/gstvaapidecoder_ffmpeg.h>
35 #define GST_PLUGIN_NAME "vaapidecode"
36 #define GST_PLUGIN_DESC "A VA-API based video decoder"
38 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapidecode);
39 #define GST_CAT_DEFAULT gst_debug_vaapidecode
41 /* ElementFactory information */
42 static const GstElementDetails gst_vaapidecode_details =
45 "Codec/Decoder/Video",
47 "Gwenole Beauchesne <gbeauchesne@splitted-desktop.com>");
49 /* Default templates */
50 #define GST_CAPS_CODEC(CODEC) \
52 "width = (int) [ 1, MAX ], " \
53 "height = (int) [ 1, MAX ]; "
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")
63 static const char gst_vaapidecode_src_caps_str[] =
64 "video/x-vaapi-surface, "
65 "width = (int) [ 1, MAX ], "
66 "height = (int) [ 1, MAX ]; ";
68 static GstStaticPadTemplate gst_vaapidecode_sink_factory =
69 GST_STATIC_PAD_TEMPLATE(
73 GST_STATIC_CAPS(gst_vaapidecode_sink_caps_str));
75 static GstStaticPadTemplate gst_vaapidecode_src_factory =
76 GST_STATIC_PAD_TEMPLATE(
80 GST_STATIC_CAPS(gst_vaapidecode_src_caps_str));
95 gst_vaapidecode_task_cb(gpointer data)
97 GstVaapiDecode * const decode = GST_VAAPIDECODE(data);
98 GstVaapiDecoderStatus status;
99 GstVaapiSurfaceProxy *proxy;
103 proxy = gst_vaapi_decoder_timed_get_surface(decode->decoder, 10000, &status);
104 if (!proxy || status != GST_VAAPI_DECODER_STATUS_SUCCESS)
108 ret = gst_pad_alloc_buffer(decode->srcpad, 0, 0, GST_PAD_CAPS(decode->srcpad), &buffer);
109 if (ret != GST_FLOW_OK || !buffer)
110 goto error_create_buffer;
112 GST_BUFFER_TIMESTAMP(buffer) = GST_VAAPI_SURFACE_PROXY_TIMESTAMP(proxy);
114 ret = gst_pad_push(decode->srcpad, buffer);
115 if (ret != GST_FLOW_OK)
116 goto error_commit_buffer;
118 g_object_unref(proxy);
124 const GstVaapiID surface_id =
125 gst_vaapi_surface_get_id(GST_VAAPI_SURFACE_PROXY_SURFACE(proxy));
127 GST_DEBUG("video sink failed to create video buffer for proxy'ed "
128 "surface %" GST_VAAPI_ID_FORMAT " (error %d)",
129 GST_VAAPI_ID_ARGS(surface_id), ret);
130 g_object_unref(proxy);
135 GST_DEBUG("video sink rejected the video buffer (error %d)", ret);
136 g_object_unref(proxy);
142 gst_vaapidecode_start(GstVaapiDecode *decode)
144 if (gst_task_get_state(decode->decoder_task) == GST_TASK_STARTED)
147 GST_DEBUG("start decoding threads");
148 if (!gst_vaapi_decoder_start(decode->decoder))
150 return gst_task_start(decode->decoder_task);
154 gst_vaapidecode_pause(GstVaapiDecode *decode)
156 if (gst_task_get_state(decode->decoder_task) == GST_TASK_PAUSED)
159 GST_DEBUG("pause decoding threads");
160 if (!gst_vaapi_decoder_pause(decode->decoder))
162 return gst_task_pause(decode->decoder_task);
166 gst_vaapidecode_stop(GstVaapiDecode *decode)
168 if (gst_task_get_state(decode->decoder_task) == GST_TASK_STOPPED)
171 GST_DEBUG("stop decoding threads");
172 if (!gst_vaapi_decoder_stop(decode->decoder))
174 return gst_task_stop(decode->decoder_task);
178 gst_vaapidecode_create(GstVaapiDecode *decode)
180 GstVaapiVideoSink *sink;
181 GstVaapiDisplay *display;
184 /* Look for a downstream vaapisink */
185 sink = gst_vaapi_video_sink_lookup(GST_ELEMENT(decode));
189 display = gst_vaapi_video_sink_get_display(sink);
193 decode->display = g_object_ref(display);
195 codec = gst_vaapi_profile_get_codec(decode->profile);
199 if (decode->use_ffmpeg)
201 gst_vaapi_decoder_ffmpeg_new(display, codec, decode->codec_data);
202 if (!decode->decoder)
205 decode->decoder_task = gst_task_create(gst_vaapidecode_task_cb, decode);
206 if (!decode->decoder_task)
209 gst_task_set_lock(decode->decoder_task, &decode->decoder_task_lock);
214 gst_vaapidecode_destroy(GstVaapiDecode *decode)
216 if (decode->decoder_task) {
217 gst_task_join(decode->decoder_task);
218 g_object_unref(decode->decoder_task);
219 decode->decoder_task = NULL;
222 if (decode->decoder) {
223 gst_vaapi_decoder_put_buffer(decode->decoder, NULL);
224 g_object_unref(decode->decoder);
225 decode->decoder = NULL;
228 if (decode->codec_data) {
229 gst_buffer_unref(decode->codec_data);
230 decode->codec_data = NULL;
233 if (decode->display) {
234 g_object_unref(decode->display);
235 decode->display = NULL;
239 static void gst_vaapidecode_base_init(gpointer klass)
241 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
243 gst_element_class_set_details(element_class, &gst_vaapidecode_details);
246 gst_element_class_add_pad_template(
248 gst_static_pad_template_get(&gst_vaapidecode_sink_factory)
252 gst_element_class_add_pad_template(
254 gst_static_pad_template_get(&gst_vaapidecode_src_factory)
259 gst_vaapidecode_finalize(GObject *object)
261 gst_vaapidecode_destroy(GST_VAAPIDECODE(object));
263 G_OBJECT_CLASS(parent_class)->finalize(object);
267 gst_vaapidecode_set_property(
274 GstVaapiDecode * const decode = GST_VAAPIDECODE(object);
277 case PROP_USE_FFMPEG:
278 decode->use_ffmpeg = g_value_get_boolean(value);
281 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
287 gst_vaapidecode_get_property(
294 GstVaapiDecode * const decode = GST_VAAPIDECODE(object);
297 case PROP_USE_FFMPEG:
298 g_value_set_boolean(value, decode->use_ffmpeg);
301 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
306 static GstStateChangeReturn
307 gst_vaapidecode_change_state(GstElement *element, GstStateChange transition)
309 GstVaapiDecode * const decode = GST_VAAPIDECODE(element);
310 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
312 switch (transition) {
313 case GST_STATE_CHANGE_READY_TO_PAUSED:
315 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
316 if (!gst_vaapidecode_start(decode))
317 return GST_STATE_CHANGE_FAILURE;
323 ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
324 if (ret != GST_STATE_CHANGE_SUCCESS)
327 switch (transition) {
328 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
329 if (!gst_vaapidecode_pause(decode))
330 return GST_STATE_CHANGE_FAILURE;
332 case GST_STATE_CHANGE_PAUSED_TO_READY:
333 if (!gst_vaapidecode_stop(decode))
334 return GST_STATE_CHANGE_FAILURE;
339 return GST_STATE_CHANGE_SUCCESS;
343 gst_vaapidecode_class_init(GstVaapiDecodeClass *klass)
345 GObjectClass * const object_class = G_OBJECT_CLASS(klass);
346 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
348 object_class->finalize = gst_vaapidecode_finalize;
349 object_class->set_property = gst_vaapidecode_set_property;
350 object_class->get_property = gst_vaapidecode_get_property;
352 element_class->change_state = gst_vaapidecode_change_state;
354 g_object_class_install_property
357 g_param_spec_boolean("use-ffmpeg",
358 "Use FFmpeg/VAAPI for decoding",
359 "Uses FFmpeg/VAAPI for decoding",
365 gst_vaapidecode_set_caps(GstPad *pad, GstCaps *caps)
367 GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
369 GstCaps *other_caps = NULL;
370 GstStructure *structure;
371 const GValue *v_width, *v_height, *v_framerate, *v_par, *v_codec_data;
373 if (pad == decode->sinkpad) {
374 other_pad = decode->srcpad;
375 other_caps = gst_caps_from_string(gst_vaapidecode_src_caps_str);
378 other_pad = decode->sinkpad;
379 other_caps = gst_caps_from_string(gst_vaapidecode_sink_caps_str);
382 /* Negotiation succeeded, so now configure ourselves */
383 structure = gst_caps_get_structure(caps, 0);
384 v_width = gst_structure_get_value(structure, "width");
385 v_height = gst_structure_get_value(structure, "height");
386 v_framerate = gst_structure_get_value(structure, "framerate");
387 v_par = gst_structure_get_value(structure, "pixel-aspect-ratio");
388 v_codec_data = gst_structure_get_value(structure, "codec_data");
390 if (pad == decode->sinkpad) {
391 decode->profile = gst_vaapi_profile_from_caps(caps);
394 gst_buffer_ref(gst_value_get_buffer(v_codec_data));
397 structure = gst_caps_get_structure(other_caps, 0);
398 gst_structure_set_value(structure, "width", v_width);
399 gst_structure_set_value(structure, "height", v_height);
401 gst_structure_set_value(structure, "framerate", v_framerate);
403 gst_structure_set_value(structure, "pixel-aspect-ratio", v_par);
405 return gst_pad_set_caps(other_pad, other_caps);
409 gst_vaapidecode_chain(GstPad *pad, GstBuffer *buf)
411 GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
413 if (!decode->decoder) {
414 if (!gst_vaapidecode_create(decode))
415 goto error_create_decoder;
418 if (!gst_vaapidecode_start(decode))
419 goto error_start_decoder;
421 if (!gst_vaapi_decoder_put_buffer(decode->decoder, buf))
422 goto error_push_buffer;
424 gst_buffer_unref(buf);
428 error_create_decoder:
430 GST_DEBUG("failed to create decoder");
431 gst_buffer_unref(buf);
432 return GST_FLOW_UNEXPECTED;
436 GST_DEBUG("failed to start decoder");
437 gst_buffer_unref(buf);
438 return GST_FLOW_UNEXPECTED;
442 GST_DEBUG("failed to push input buffer to decoder");
443 gst_buffer_unref(buf);
444 return GST_FLOW_UNEXPECTED;
449 gst_vaapidecode_sink_event(GstPad *pad, GstEvent *event)
451 GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
453 GST_DEBUG("handle sink event '%s'", GST_EVENT_TYPE_NAME(event));
455 /* Propagate event downstream */
456 return gst_pad_push_event(decode->srcpad, event);
460 gst_vaapidecode_src_event(GstPad *pad, GstEvent *event)
462 GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
464 GST_DEBUG("handle src event '%s'", GST_EVENT_TYPE_NAME(event));
466 /* Propagate event upstream */
467 return gst_pad_push_event(decode->sinkpad, event);
471 gst_vaapidecode_init(GstVaapiDecode *decode, GstVaapiDecodeClass *klass)
473 GstElementClass * const element_class = GST_ELEMENT_CLASS(klass);
475 decode->display = NULL;
477 decode->codec_data = NULL;
478 decode->decoder = NULL;
479 decode->decoder_task = NULL;
480 decode->use_ffmpeg = TRUE;
482 g_static_rec_mutex_init(&decode->decoder_task_lock);
484 /* Pad through which data comes in to the element */
485 decode->sinkpad = gst_pad_new_from_template(
486 gst_element_class_get_pad_template(element_class, "sink"),
490 gst_pad_set_setcaps_function(decode->sinkpad, gst_vaapidecode_set_caps);
491 gst_pad_set_chain_function(decode->sinkpad, gst_vaapidecode_chain);
492 gst_pad_set_event_function(decode->sinkpad, gst_vaapidecode_sink_event);
493 gst_element_add_pad(GST_ELEMENT(decode), decode->sinkpad);
495 /* Pad through which data goes out of the element */
496 decode->srcpad = gst_pad_new_from_template(
497 gst_element_class_get_pad_template(element_class, "src"),
501 gst_pad_set_event_function(decode->srcpad, gst_vaapidecode_src_event);
502 gst_element_add_pad(GST_ELEMENT(decode), decode->srcpad);
505 static gboolean plugin_init(GstPlugin *plugin)
507 GST_DEBUG_CATEGORY_INIT(gst_debug_vaapidecode,
508 GST_PLUGIN_NAME, 0, GST_PLUGIN_DESC);
510 return gst_element_register(plugin,
513 GST_TYPE_VAAPIDECODE);
517 GST_VERSION_MAJOR, GST_VERSION_MINOR,