2 * Copyright (C) 2022 Igalia, S.L.
3 * Author: Víctor Jáquez <vjaquez@igalia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the0
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * SECTION:element-vajpegdec
24 * @short_description: A VA-API based JPEG video decoder
26 * vajpegdec decodes JPEG images to VA surfaces using the installed
27 * and chosen [VA-API](https://01.org/linuxmedia/vaapi) driver.
29 * The decoding surfaces can be mapped onto main memory as video
32 * ## Example launch line
34 * gst-launch-1.0 filesrc location=sample.mjpg ! parsebin ! vajpegdec ! autovideosink
45 #include "gstvajpegdec.h"
47 #include <gst/va/gstvavideoformat.h>
49 #include "gstvabasedec.h"
51 GST_DEBUG_CATEGORY_STATIC (gst_va_jpegdec_debug);
52 #ifndef GST_DISABLE_GST_DEBUG
53 #define GST_CAT_DEFAULT gst_va_jpegdec_debug
55 #define GST_CAT_DEFAULT NULL
58 #define GST_VA_JPEG_DEC(obj) ((GstVaJpegDec *) obj)
59 #define GST_VA_JPEG_DEC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_FROM_INSTANCE (obj), GstVaJPEGDecClass))
60 #define GST_VA_JPEG_DEC_CLASS(klass) ((GstVaJpegDecClass *) klass)
62 typedef struct _GstVaJpegDec GstVaJpegDec;
63 typedef struct _GstVaJpegDecClass GstVaJpegDecClass;
65 struct _GstVaJpegDecClass
67 GstVaBaseDecClass parent_class;
74 GstVaDecodePicture *pic;
77 static GstElementClass *parent_class = NULL;
80 static const gchar *src_caps_str =
81 GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_VA,
83 GST_VIDEO_CAPS_MAKE ("{ NV12 }");
86 static const gchar *sink_caps_str = "image/jpeg";
89 _get_profile (GstJpegMarker marker)
92 case GST_JPEG_MARKER_SOF0:
93 return VAProfileJPEGBaseline;
101 /* taken from MediaSDK */
102 #define RT_FORMAT_RGB (VA_RT_FORMAT_RGB16 | VA_RT_FORMAT_RGB32)
105 static const struct sampling_rtformat {
106 const gchar *sampling;
108 } sampling_rtformat_map[] = {
109 { "RGB", RT_FORMAT_RGB },
110 { "YCbCr-4:4:4", VA_RT_FORMAT_YUV444 },
111 { "YCbCr-4:2:2", VA_RT_FORMAT_YUV422 },
112 { "YCbCr-4:2:0", VA_RT_FORMAT_YUV420 },
113 { "GRAYSCALE", VA_RT_FORMAT_YUV400 },
114 { "YCbCr-4:1:1", VA_RT_FORMAT_YUV411 },
119 _get_rt_format (GstCaps * caps)
121 GstStructure *structure;
122 const gchar *sampling;
125 structure = gst_caps_get_structure (caps, 0);
126 sampling = gst_structure_get_string (structure, "sampling");
128 for (i = 0; i < G_N_ELEMENTS (sampling_rtformat_map); i++) {
129 if (g_strcmp0 (sampling, sampling_rtformat_map[i].sampling) == 0)
130 return sampling_rtformat_map[i].rt_format;
137 gst_va_jpeg_dec_new_picture (GstJpegDecoder * decoder,
138 GstVideoCodecFrame * frame, GstJpegMarker marker,
139 GstJpegFrameHdr * frame_hdr)
141 GstVaJpegDec *self = GST_VA_JPEG_DEC (decoder);
142 GstVaBaseDec *base = GST_VA_BASE_DEC (decoder);
143 GstVideoInfo *info = &base->output_info;
146 VAPictureParameterBufferJPEGBaseline pic_param;
147 guint32 i, rt_format;
149 GST_LOG_OBJECT (self, "new picture");
151 g_clear_pointer (&self->pic, gst_va_decode_picture_free);
153 profile = _get_profile (marker);
154 if (profile == VAProfileNone)
155 return GST_FLOW_NOT_NEGOTIATED;
157 /* use caps to avoid re-parsing app14 */
158 rt_format = _get_rt_format (decoder->input_state->caps);
160 return GST_FLOW_NOT_NEGOTIATED;
162 if (!gst_va_decoder_config_is_equal (base->decoder, profile, rt_format,
163 frame_hdr->width, frame_hdr->height)) {
164 base->profile = profile;
165 base->rt_format = rt_format;
166 GST_VIDEO_INFO_WIDTH (info) = base->width = frame_hdr->width;
167 GST_VIDEO_INFO_HEIGHT (info) = base->height = frame_hdr->height;
169 base->need_negotiation = TRUE;
170 GST_INFO_OBJECT (self, "Format changed to %s [%x] (%dx%d)",
171 gst_va_profile_name (profile), rt_format, base->width, base->height);
174 g_clear_pointer (&base->input_state, gst_video_codec_state_unref);
175 base->input_state = gst_video_codec_state_ref (decoder->input_state);
177 ret = gst_va_base_dec_prepare_output_frame (base, frame);
178 if (ret != GST_FLOW_OK) {
179 GST_ERROR_OBJECT (self, "Failed to allocate output buffer: %s",
180 gst_flow_get_name (ret));
184 self->pic = gst_va_decode_picture_new (base->decoder, frame->output_buffer);
187 pic_param = (VAPictureParameterBufferJPEGBaseline) {
188 .picture_width = frame_hdr->width,
189 .picture_height = frame_hdr->height,
191 .num_components = frame_hdr->num_components,
192 .color_space = (rt_format == RT_FORMAT_RGB) ? 1 : 0, /* TODO: BGR */
193 .rotation = VA_ROTATION_NONE,
197 for (i = 0; i < frame_hdr->num_components; i++) {
198 pic_param.components[i].component_id = frame_hdr->components[i].identifier;
199 pic_param.components[i].h_sampling_factor =
200 frame_hdr->components[i].horizontal_factor;
201 pic_param.components[i].v_sampling_factor =
202 frame_hdr->components[i].vertical_factor;
203 pic_param.components[i].quantiser_table_selector =
204 frame_hdr->components[i].quant_table_selector;
207 if (!gst_va_decoder_add_param_buffer (base->decoder, self->pic,
208 VAPictureParameterBufferType, &pic_param, sizeof (pic_param)))
209 return GST_FLOW_ERROR;
215 gst_va_jpeg_dec_decode_scan (GstJpegDecoder * decoder,
216 GstJpegDecoderScan * scan, const guint8 * buffer, guint32 size)
218 GstVaJpegDec *self = GST_VA_JPEG_DEC (decoder);
219 GstVaBaseDec *base = GST_VA_BASE_DEC (decoder);
220 VAHuffmanTableBufferJPEGBaseline huff = { 0, };
221 VAIQMatrixBufferJPEGBaseline quant = { 0, };
222 VASliceParameterBufferJPEGBaseline slice_param;
225 GST_LOG_OBJECT (self, "decoding slice");
227 for (i = 0; i < G_N_ELEMENTS (quant.load_quantiser_table); i++) {
228 quant.load_quantiser_table[i] =
229 scan->quantization_tables->quant_tables[i].valid;
230 if (!scan->quantization_tables->quant_tables[i].valid)
232 for (j = 0; j < GST_JPEG_MAX_QUANT_ELEMENTS; j++) {
233 quant.quantiser_table[i][j] =
234 scan->quantization_tables->quant_tables[i].quant_table[j];
237 /* invalidate table */
238 scan->quantization_tables->quant_tables[i].valid = FALSE;
241 if (!gst_va_decoder_add_param_buffer (base->decoder, self->pic,
242 VAIQMatrixBufferType, &quant, sizeof (quant)))
243 return GST_FLOW_ERROR;
245 for (i = 0; i < G_N_ELEMENTS (huff.huffman_table); i++) {
246 huff.load_huffman_table[i] = scan->huffman_tables->dc_tables[i].valid
247 && scan->huffman_tables->ac_tables[i].valid;
249 if (!huff.load_huffman_table[i])
252 memcpy (huff.huffman_table[i].num_dc_codes,
253 scan->huffman_tables->dc_tables[i].huf_bits,
254 sizeof (huff.huffman_table[i].num_dc_codes));
256 memcpy (huff.huffman_table[i].dc_values,
257 scan->huffman_tables->dc_tables[i].huf_values,
258 sizeof (huff.huffman_table[i].dc_values));
260 memcpy (huff.huffman_table[i].num_ac_codes,
261 scan->huffman_tables->ac_tables[i].huf_bits,
262 sizeof (huff.huffman_table[i].num_ac_codes));
264 memcpy (huff.huffman_table[i].ac_values,
265 scan->huffman_tables->ac_tables[i].huf_values,
266 sizeof (huff.huffman_table[i].ac_values));
270 /* invalidate table */
271 for (i = 0; i < G_N_ELEMENTS (scan->huffman_tables->dc_tables); i++)
272 scan->huffman_tables->dc_tables[i].valid = FALSE;
273 for (i = 0; i < G_N_ELEMENTS (scan->huffman_tables->ac_tables); i++)
274 scan->huffman_tables->ac_tables[i].valid = FALSE;
276 if (!gst_va_decoder_add_param_buffer (base->decoder, self->pic,
277 VAHuffmanTableBufferType, &huff, sizeof (huff)))
278 return GST_FLOW_ERROR;
281 slice_param = (VASliceParameterBufferJPEGBaseline) {
282 .slice_data_size = size,
283 .slice_data_offset = 0,
284 .slice_data_flag = VA_SLICE_DATA_FLAG_ALL,
285 .slice_horizontal_position = 0,
286 .slice_vertical_position = 0,
287 .restart_interval = scan->restart_interval,
288 .num_mcus = scan->mcu_rows_in_scan * scan->mcus_per_row,
289 .num_components = scan->scan_hdr->num_components,
293 for (i = 0; i < scan->scan_hdr->num_components; i++) {
294 slice_param.components[i].component_selector =
295 scan->scan_hdr->components[i].component_selector;
296 slice_param.components[i].dc_table_selector =
297 scan->scan_hdr->components[i].dc_selector;
298 slice_param.components[i].ac_table_selector =
299 scan->scan_hdr->components[i].ac_selector;
302 if (!gst_va_decoder_add_slice_buffer (base->decoder, self->pic, &slice_param,
303 sizeof (slice_param), (void *) buffer, size))
304 return GST_FLOW_ERROR;
310 gst_va_jpeg_dec_end_picture (GstJpegDecoder * decoder)
312 GstVaJpegDec *self = GST_VA_JPEG_DEC (decoder);
313 GstVaBaseDec *base = GST_VA_BASE_DEC (decoder);
315 GST_LOG_OBJECT (self, "end picture");
317 if (!gst_va_decoder_decode (base->decoder, self->pic))
318 return GST_FLOW_ERROR;
323 gst_va_jpeg_dec_output_picture (GstJpegDecoder * decoder,
324 GstVideoCodecFrame * frame)
326 GstVaBaseDec *base = GST_VA_BASE_DEC (decoder);
327 GstVideoDecoder *vdec = GST_VIDEO_DECODER (decoder);
329 if (gst_va_base_dec_process_output (base, frame, NULL, 0))
330 return gst_video_decoder_finish_frame (vdec, frame);
331 return GST_FLOW_ERROR;
334 /* @XXX: Checks for drivers that can do color convertion to nv12
335 * regardless the input chroma, while it's YUV. */
337 has_internal_nv12_color_convertion (GstVaBaseDec * base, GstVideoFormat format)
339 if (!GST_VA_DISPLAY_IS_IMPLEMENTATION (base->display, INTEL_I965)
340 && !GST_VA_DISPLAY_IS_IMPLEMENTATION (base->display, INTEL_IHD))
343 if (base->rt_format != VA_RT_FORMAT_YUV420
344 && base->rt_format != VA_RT_FORMAT_YUV422)
347 if (format != GST_VIDEO_FORMAT_NV12)
354 gst_va_jpeg_dec_negotiate (GstVideoDecoder * decoder)
356 GstVaBaseDec *base = GST_VA_BASE_DEC (decoder);
357 GstVaJpegDec *self = GST_VA_JPEG_DEC (decoder);
358 GstVideoFormat format;
359 GstCapsFeatures *capsfeatures = NULL;
361 /* Ignore downstream renegotiation request. */
362 if (!base->need_negotiation)
365 base->need_negotiation = FALSE;
367 if (GST_VA_DISPLAY_IS_IMPLEMENTATION (base->display, INTEL_I965))
368 base->hacks = GST_VA_HACK_SURFACE_NO_FOURCC;
370 if (gst_va_decoder_is_open (base->decoder)
371 && !gst_va_decoder_close (base->decoder))
374 if (!gst_va_decoder_open (base->decoder, base->profile, base->rt_format))
377 if (!gst_va_decoder_set_frame_size (base->decoder, base->width, base->height))
380 if (base->output_state)
381 gst_video_codec_state_unref (base->output_state);
383 /* hack for RGBP rt_format, because only RGBP is exposed as pixel
385 if (base->rt_format == RT_FORMAT_RGB)
386 base->rt_format = VA_RT_FORMAT_RGBP;
388 gst_va_base_dec_get_preferred_format_and_caps_features (base, &format,
390 if (format == GST_VIDEO_FORMAT_UNKNOWN)
393 if (!has_internal_nv12_color_convertion (base, format)
394 && (gst_va_chroma_from_video_format (format) != base->rt_format))
397 /* hack for RGBP rt_format */
398 if (base->rt_format == VA_RT_FORMAT_RGBP)
399 base->rt_format = RT_FORMAT_RGB;
402 gst_video_decoder_set_output_state (decoder, format,
403 base->width, base->height, base->input_state);
405 base->output_state->caps = gst_video_info_to_caps (&base->output_state->info);
407 gst_caps_set_features_simple (base->output_state->caps, capsfeatures);
409 GST_INFO_OBJECT (self, "Negotiated caps %" GST_PTR_FORMAT,
410 base->output_state->caps);
412 return GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder);
416 gst_va_jpeg_dec_dispose (GObject * object)
418 GstVaJpegDec *self = GST_VA_JPEG_DEC (object);
420 gst_va_base_dec_close (GST_VIDEO_DECODER (object));
421 g_clear_pointer (&self->pic, gst_va_decode_picture_free);
423 G_OBJECT_CLASS (parent_class)->dispose (object);
427 gst_va_jpeg_dec_class_init (gpointer g_class, gpointer class_data)
429 GstCaps *src_doc_caps, *sink_doc_caps;
430 GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
431 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
432 GstJpegDecoderClass *jpegdecoder_class = GST_JPEG_DECODER_CLASS (g_class);
433 GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (g_class);
434 struct CData *cdata = class_data;
437 if (cdata->description) {
438 long_name = g_strdup_printf ("VA-API JPEG Decoder in %s",
441 long_name = g_strdup ("VA-API JPEG Decoder");
444 gst_element_class_set_metadata (element_class, long_name,
445 "Codec/Decoder/Image/Hardware",
446 "VA-API based JPEG image decoder",
447 "Víctor Jáquez <vjaquez@igalia.com>");
449 sink_doc_caps = gst_caps_from_string (sink_caps_str);
450 src_doc_caps = gst_caps_from_string (src_caps_str);
452 parent_class = g_type_class_peek_parent (g_class);
455 * GstVaJpegDec:device-path:
457 * It shows the DRM device path used for the VA operation, if any.
459 gst_va_base_dec_class_init (GST_VA_BASE_DEC_CLASS (g_class), JPEG,
460 cdata->render_device_path, cdata->sink_caps, cdata->src_caps,
461 src_doc_caps, sink_doc_caps);
463 gobject_class->dispose = gst_va_jpeg_dec_dispose;
465 decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_va_jpeg_dec_negotiate);
467 jpegdecoder_class->decode_scan =
468 GST_DEBUG_FUNCPTR (gst_va_jpeg_dec_decode_scan);
469 jpegdecoder_class->new_picture =
470 GST_DEBUG_FUNCPTR (gst_va_jpeg_dec_new_picture);
471 jpegdecoder_class->end_picture =
472 GST_DEBUG_FUNCPTR (gst_va_jpeg_dec_end_picture);
473 jpegdecoder_class->output_picture =
474 GST_DEBUG_FUNCPTR (gst_va_jpeg_dec_output_picture);
477 g_free (cdata->description);
478 g_free (cdata->render_device_path);
479 gst_caps_unref (cdata->src_caps);
480 gst_caps_unref (cdata->sink_caps);
485 gst_va_jpeg_dec_init (GTypeInstance * instance, gpointer g_class)
487 GstVaBaseDec *base = GST_VA_BASE_DEC (instance);
489 gst_va_base_dec_init (base, GST_CAT_DEFAULT);
490 base->min_buffers = 1;
494 _register_debug_category (gpointer data)
496 GST_DEBUG_CATEGORY_INIT (gst_va_jpegdec_debug, "vajpegdec", 0,
503 _fixup_sink_caps (GstVaDisplay * display, GstCaps * caps)
505 if (GST_VA_DISPLAY_IS_IMPLEMENTATION (display, INTEL_I965)) {
508 GValue sampling = G_VALUE_INIT;
509 const char *sampling_list[] = { "YCbCr-4:2:0", "YCbCr-4:2:2" };
511 ret = gst_caps_copy (caps);
512 gst_caps_set_simple (ret, "colorspace", G_TYPE_STRING, "sYUV", NULL);
514 gst_value_list_init (&sampling, G_N_ELEMENTS (sampling_list));
515 for (i = 0; i < G_N_ELEMENTS (sampling_list); i++) {
516 GValue samp = G_VALUE_INIT;
517 g_value_init (&samp, G_TYPE_STRING);
518 g_value_set_string (&samp, sampling_list[i]);
519 gst_value_list_append_value (&sampling, &samp);
520 g_value_unset (&samp);
523 gst_caps_set_value (ret, "sampling", &sampling);
524 g_value_unset (&sampling);
527 return gst_caps_ref (caps);
531 _fixup_src_caps (GstVaDisplay * display, GstCaps * caps)
533 if (GST_VA_DISPLAY_IS_IMPLEMENTATION (display, INTEL_IHD)) {
539 ret = gst_caps_copy (caps);
541 len = gst_caps_get_size (ret);
542 for (i = 0; i < len; i++) {
543 s = gst_caps_get_structure (ret, i);
544 f = gst_caps_get_features (ret, i);
545 if (gst_caps_features_is_equal (f,
546 GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY)) {
547 /* rgbp is not correctly mapped into memory */
549 GValue out = G_VALUE_INIT;
550 const GValue *in = gst_structure_get_value (s, "format");
552 size = gst_value_list_get_size (in);
553 gst_value_list_init (&out, size);
554 for (i = 0; i < size; i++) {
555 const GValue *fmt = gst_value_list_get_value (in, i);
556 if (g_strcmp0 (g_value_get_string (fmt), "RGBP") != 0)
557 gst_value_list_append_value (&out, fmt);
559 gst_structure_set_value (s, "format", &out);
560 g_value_unset (&out);
561 } else if (gst_caps_features_contains (f, GST_CAPS_FEATURE_MEMORY_DMABUF)) {
562 /* dmabuf exportation only handles NV12 */
563 gst_structure_set (s, "format", G_TYPE_STRING, "NV12", NULL);
568 } else if (GST_VA_DISPLAY_IS_IMPLEMENTATION (display, INTEL_I965)) {
573 ret = gst_caps_copy (caps);
575 len = gst_caps_get_size (ret);
576 for (i = 0; i < len; i++) {
577 s = gst_caps_get_structure (ret, i);
578 /* only NV12 works in this nigthmare */
579 gst_structure_set (s, "format", G_TYPE_STRING, "NV12", NULL);
584 return gst_caps_ref (caps);
588 gst_va_jpeg_dec_register (GstPlugin * plugin, GstVaDevice * device,
589 GstCaps * sink_caps, GstCaps * src_caps, guint rank)
591 static GOnce debug_once = G_ONCE_INIT;
593 GTypeInfo type_info = {
594 .class_size = sizeof (GstVaJpegDecClass),
595 .class_init = gst_va_jpeg_dec_class_init,
596 .instance_size = sizeof (GstVaJpegDec),
597 .instance_init = gst_va_jpeg_dec_init,
601 gchar *type_name, *feature_name;
603 g_return_val_if_fail (GST_IS_PLUGIN (plugin), FALSE);
604 g_return_val_if_fail (GST_IS_VA_DEVICE (device), FALSE);
605 g_return_val_if_fail (GST_IS_CAPS (sink_caps), FALSE);
606 g_return_val_if_fail (GST_IS_CAPS (src_caps), FALSE);
608 cdata = g_new (struct CData, 1);
609 cdata->description = NULL;
610 cdata->render_device_path = g_strdup (device->render_device_path);
611 cdata->sink_caps = _fixup_sink_caps (device->display, sink_caps);
612 cdata->src_caps = _fixup_src_caps (device->display, src_caps);
614 /* class data will be leaked if the element never gets instantiated */
615 GST_MINI_OBJECT_FLAG_SET (cdata->sink_caps,
616 GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
617 GST_MINI_OBJECT_FLAG_SET (cdata->src_caps,
618 GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
620 type_info.class_data = cdata;
623 /* The first decoder to be registered should use a constant name,
624 * like vajpegdec, for any additional decoders, we create unique
625 * names, using inserting the render device name. */
626 if (device->index == 0) {
627 type_name = g_strdup ("GstVaJpegDec");
628 feature_name = g_strdup ("vajpegdec");
630 gchar *basename = g_path_get_basename (device->render_device_path);
631 type_name = g_strdup_printf ("GstVa%sJpegDec", basename);
632 feature_name = g_strdup_printf ("va%sjpegdec", basename);
633 cdata->description = basename;
635 /* lower rank for non-first device */
640 g_once (&debug_once, _register_debug_category, NULL);
642 type = g_type_register_static (GST_TYPE_JPEG_DECODER,
643 type_name, &type_info, 0);
645 ret = gst_element_register (plugin, feature_name, rank, type);
648 g_free (feature_name);