gstvaapidecoder_jpeg.c 29.2 KB
Newer Older
Wind Yuan's avatar
Wind Yuan committed
1 2 3
/*
 *  gstvaapidecoder_jpeg.c - JPEG decoder
 *
4
 *  Copyright (C) 2011-2013 Intel Corporation
5 6
 *    Author: Wind Yuan <feng.yuan@intel.com>
 *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
Wind Yuan's avatar
Wind Yuan committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2.1
 *  of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301 USA
 */

/**
 * SECTION:gstvaapidecoder_jpeg
 * @short_description: JPEG decoder
 */

#include "sysdeps.h"
#include <string.h>
#include <gst/codecparsers/gstjpegparser.h>
32
#include "gstvaapicompat.h"
Wind Yuan's avatar
Wind Yuan committed
33 34 35 36 37 38
#include "gstvaapidecoder_jpeg.h"
#include "gstvaapidecoder_objects.h"
#include "gstvaapidecoder_priv.h"
#include "gstvaapidisplay_priv.h"
#include "gstvaapiobject_priv.h"

39 40 41 42
#ifdef HAVE_VA_VA_DEC_JPEG_H
# include <va/va_dec_jpeg.h>
#endif

Wind Yuan's avatar
Wind Yuan committed
43 44 45
#define DEBUG 1
#include "gstvaapidebug.h"

46 47
#define GST_VAAPI_DECODER_JPEG_CAST(decoder) \
    ((GstVaapiDecoderJpeg *)(decoder))
Wind Yuan's avatar
Wind Yuan committed
48

49 50
typedef struct _GstVaapiDecoderJpegPrivate      GstVaapiDecoderJpegPrivate;
typedef struct _GstVaapiDecoderJpegClass        GstVaapiDecoderJpegClass;
Wind Yuan's avatar
Wind Yuan committed
51

52 53 54 55 56 57 58 59 60 61 62 63 64
typedef enum  {
    GST_JPEG_VIDEO_STATE_GOT_SOI        = 1 << 0,
    GST_JPEG_VIDEO_STATE_GOT_SOF        = 1 << 1,
    GST_JPEG_VIDEO_STATE_GOT_SOS        = 1 << 2,
    GST_JPEG_VIDEO_STATE_GOT_HUF_TABLE  = 1 << 3,
    GST_JPEG_VIDEO_STATE_GOT_IQ_TABLE   = 1 << 4,

    GST_JPEG_VIDEO_STATE_VALID_PICTURE = (
        GST_JPEG_VIDEO_STATE_GOT_SOI |
        GST_JPEG_VIDEO_STATE_GOT_SOF |
        GST_JPEG_VIDEO_STATE_GOT_SOS),
} GstJpegVideoState;

Wind Yuan's avatar
Wind Yuan committed
65 66 67 68 69
struct _GstVaapiDecoderJpegPrivate {
    GstVaapiProfile             profile;
    guint                       width;
    guint                       height;
    GstVaapiPicture            *current_picture;
70
    GstJpegFrameHdr             frame_hdr;
71
    GstJpegHuffmanTables        huf_tables;
72
    GstJpegQuantTables          quant_tables;
73
    guint                       mcu_restart;
74 75
    guint                       parser_state;
    guint                       decoder_state;
Wind Yuan's avatar
Wind Yuan committed
76 77
    guint                       is_opened       : 1;
    guint                       profile_changed : 1;
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
};

/**
 * GstVaapiDecoderJpeg:
 *
 * A decoder based on Jpeg.
 */
struct _GstVaapiDecoderJpeg {
    /*< private >*/
    GstVaapiDecoder             parent_instance;
    GstVaapiDecoderJpegPrivate  priv;
};

/**
 * GstVaapiDecoderJpegClass:
 *
 * A decoder class based on Jpeg.
 */
struct _GstVaapiDecoderJpegClass {
    /*< private >*/
    GstVaapiDecoderClass parent_class;
Wind Yuan's avatar
Wind Yuan committed
99 100
};

101 102 103 104 105 106 107 108 109 110 111
static inline void
unit_set_marker_code(GstVaapiDecoderUnit *unit, GstJpegMarkerCode marker)
{
    unit->parsed_info = GSIZE_TO_POINTER(marker);
}

static inline GstJpegMarkerCode
unit_get_marker_code(GstVaapiDecoderUnit *unit)
{
    return GPOINTER_TO_SIZE(unit->parsed_info);
}
Wind Yuan's avatar
Wind Yuan committed
112 113 114 115

static void
gst_vaapi_decoder_jpeg_close(GstVaapiDecoderJpeg *decoder)
{
116
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
Wind Yuan's avatar
Wind Yuan committed
117 118 119 120 121 122 123 124 125 126 127 128

    gst_vaapi_picture_replace(&priv->current_picture, NULL);

    /* Reset all */
    priv->profile               = GST_VAAPI_PROFILE_JPEG_BASELINE;
    priv->width                 = 0;
    priv->height                = 0;
    priv->is_opened             = FALSE;
    priv->profile_changed       = TRUE;
}

static gboolean
129
gst_vaapi_decoder_jpeg_open(GstVaapiDecoderJpeg *decoder)
Wind Yuan's avatar
Wind Yuan committed
130
{
131 132
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;

Wind Yuan's avatar
Wind Yuan committed
133 134
    gst_vaapi_decoder_jpeg_close(decoder);

135 136
    priv->parser_state  = 0;
    priv->decoder_state = 0;
Wind Yuan's avatar
Wind Yuan committed
137 138 139 140
    return TRUE;
}

static void
141
gst_vaapi_decoder_jpeg_destroy(GstVaapiDecoder *base_decoder)
Wind Yuan's avatar
Wind Yuan committed
142
{
143 144 145
    GstVaapiDecoderJpeg * const decoder =
        GST_VAAPI_DECODER_JPEG_CAST(base_decoder);

Wind Yuan's avatar
Wind Yuan committed
146 147 148 149
    gst_vaapi_decoder_jpeg_close(decoder);
}

static gboolean
150
gst_vaapi_decoder_jpeg_create(GstVaapiDecoder *base_decoder)
Wind Yuan's avatar
Wind Yuan committed
151
{
152 153 154 155 156 157
    GstVaapiDecoderJpeg * const decoder =
        GST_VAAPI_DECODER_JPEG_CAST(base_decoder);
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;

    priv->profile               = GST_VAAPI_PROFILE_JPEG_BASELINE;
    priv->profile_changed       = TRUE;
Wind Yuan's avatar
Wind Yuan committed
158 159 160 161 162 163
    return TRUE;
}

static GstVaapiDecoderStatus
ensure_context(GstVaapiDecoderJpeg *decoder)
{
164
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
Wind Yuan's avatar
Wind Yuan committed
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
    GstVaapiProfile profiles[2];
    GstVaapiEntrypoint entrypoint = GST_VAAPI_ENTRYPOINT_VLD;
    guint i, n_profiles = 0;
    gboolean reset_context = FALSE;

    if (priv->profile_changed) {
        GST_DEBUG("profile changed");
        priv->profile_changed = FALSE;
        reset_context         = TRUE;

        profiles[n_profiles++] = priv->profile;
        //if (priv->profile == GST_VAAPI_PROFILE_JPEG_EXTENDED)
        //    profiles[n_profiles++] = GST_VAAPI_PROFILE_JPEG_BASELINE;

        for (i = 0; i < n_profiles; i++) {
            if (gst_vaapi_display_has_decoder(GST_VAAPI_DECODER_DISPLAY(decoder),
                                              profiles[i], entrypoint))
                break;
        }
        if (i == n_profiles)
            return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
        priv->profile = profiles[i];
    }

    if (reset_context) {
190 191 192 193 194 195 196 197
        GstVaapiContextInfo info;

        info.profile    = priv->profile;
        info.entrypoint = entrypoint;
        info.width      = priv->width;
        info.height     = priv->height;
        info.ref_frames = 2;
        reset_context   = gst_vaapi_decoder_ensure_context(
Wind Yuan's avatar
Wind Yuan committed
198
            GST_VAAPI_DECODER(decoder),
199
            &info
Wind Yuan's avatar
Wind Yuan committed
200 201 202 203 204 205 206
        );
        if (!reset_context)
            return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
    }
    return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

207 208 209 210 211 212 213 214 215 216
static inline gboolean
is_valid_state(guint state, guint ref_state)
{
    return (state & ref_state) == ref_state;
}

#define VALID_STATE(TYPE, STATE)                \
    is_valid_state(priv->G_PASTE(TYPE,_state),  \
        G_PASTE(GST_JPEG_VIDEO_STATE_,STATE))

217
static GstVaapiDecoderStatus
Wind Yuan's avatar
Wind Yuan committed
218 219
decode_current_picture(GstVaapiDecoderJpeg *decoder)
{
220
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
Wind Yuan's avatar
Wind Yuan committed
221
    GstVaapiPicture * const picture = priv->current_picture;
222

223 224 225 226
    if (!VALID_STATE(decoder, VALID_PICTURE))
        goto drop_frame;
    priv->decoder_state = 0;

227 228 229 230 231 232 233 234 235 236 237 238 239
    if (!picture)
        return GST_VAAPI_DECODER_STATUS_SUCCESS;

    if (!gst_vaapi_picture_decode(picture))
        goto error;
    if (!gst_vaapi_picture_output(picture))
        goto error;
    gst_vaapi_picture_replace(&priv->current_picture, NULL);
    return GST_VAAPI_DECODER_STATUS_SUCCESS;

error:
    gst_vaapi_picture_replace(&priv->current_picture, NULL);
    return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
240 241 242 243

drop_frame:
    priv->decoder_state = 0;
    return GST_VAAPI_DECODER_STATUS_DROP_FRAME;
Wind Yuan's avatar
Wind Yuan committed
244 245 246 247 248 249
}

static gboolean
fill_picture(
    GstVaapiDecoderJpeg *decoder, 
    GstVaapiPicture     *picture,
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
250
    GstJpegFrameHdr     *frame_hdr
Wind Yuan's avatar
Wind Yuan committed
251 252
)
{
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
253
    VAPictureParameterBufferJPEGBaseline * const pic_param = picture->param;
Wind Yuan's avatar
Wind Yuan committed
254 255
    guint i;

256
    memset(pic_param, 0, sizeof(VAPictureParameterBufferJPEGBaseline));
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
257 258
    pic_param->picture_width    = frame_hdr->width;
    pic_param->picture_height   = frame_hdr->height;
Wind Yuan's avatar
Wind Yuan committed
259

Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
260 261
    pic_param->num_components   = frame_hdr->num_components;
    if (frame_hdr->num_components > 4)
262
        return FALSE;
Wind Yuan's avatar
Wind Yuan committed
263 264
    for (i = 0; i < pic_param->num_components; i++) {
        pic_param->components[i].component_id =
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
265
            frame_hdr->components[i].identifier;
Wind Yuan's avatar
Wind Yuan committed
266
        pic_param->components[i].h_sampling_factor =
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
267
            frame_hdr->components[i].horizontal_factor;
Wind Yuan's avatar
Wind Yuan committed
268
        pic_param->components[i].v_sampling_factor =
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
269
            frame_hdr->components[i].vertical_factor;
Wind Yuan's avatar
Wind Yuan committed
270
        pic_param->components[i].quantiser_table_selector =
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
271
            frame_hdr->components[i].quant_table_selector;
Wind Yuan's avatar
Wind Yuan committed
272 273 274 275
    }
    return TRUE;
}

Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
276 277
static GstVaapiDecoderStatus
fill_quantization_table(GstVaapiDecoderJpeg *decoder, GstVaapiPicture *picture)
Wind Yuan's avatar
Wind Yuan committed
278
{
279
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
280
    VAIQMatrixBufferJPEGBaseline *iq_matrix;
281
    guint i, j, num_tables;
282

283
    if (!VALID_STATE(decoder, GOT_IQ_TABLE))
284
        gst_jpeg_get_default_quantization_tables(&priv->quant_tables);
Wind Yuan's avatar
Wind Yuan committed
285
    
286
    picture->iq_matrix = GST_VAAPI_IQ_MATRIX_NEW(JPEGBaseline, decoder);
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
287 288 289 290
    if (!picture->iq_matrix) {
        GST_ERROR("failed to allocate quantiser table");
        return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
    }
Wind Yuan's avatar
Wind Yuan committed
291
    iq_matrix = picture->iq_matrix->param;
292 293 294 295 296

    num_tables = MIN(G_N_ELEMENTS(iq_matrix->quantiser_table),
                     GST_JPEG_MAX_QUANT_ELEMENTS);

    for (i = 0; i < num_tables; i++) {
297 298
        GstJpegQuantTable * const quant_table =
            &priv->quant_tables.quant_tables[i];
299 300 301 302 303

        iq_matrix->load_quantiser_table[i] = quant_table->valid;
        if (!iq_matrix->load_quantiser_table[i])
            continue;

Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
304 305 306 307 308 309
        if (quant_table->quant_precision != 0) {
            // Only Baseline profile is supported, thus 8-bit Qk values
            GST_ERROR("unsupported quantization table element precision");
            return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CHROMA_FORMAT;
        }

310 311 312 313
        for (j = 0; j < GST_JPEG_MAX_QUANT_ELEMENTS; j++)
            iq_matrix->quantiser_table[i][j] = quant_table->quant_table[j];
        iq_matrix->load_quantiser_table[i] = 1;
        quant_table->valid = FALSE;
Wind Yuan's avatar
Wind Yuan committed
314
    }
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
315
    return GST_VAAPI_DECODER_STATUS_SUCCESS;
Wind Yuan's avatar
Wind Yuan committed
316 317
}

318 319
static gboolean
huffman_tables_updated(const GstJpegHuffmanTables *huf_tables)
Wind Yuan's avatar
Wind Yuan committed
320
{
321
    guint i;
322

323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
    for (i = 0; i < G_N_ELEMENTS(huf_tables->dc_tables); i++)
        if (huf_tables->dc_tables[i].valid)
            return TRUE;
    for (i = 0; i < G_N_ELEMENTS(huf_tables->ac_tables); i++)
        if (huf_tables->ac_tables[i].valid)
            return TRUE;
    return FALSE;
}

static void
huffman_tables_reset(GstJpegHuffmanTables *huf_tables)
{
    guint i;

    for (i = 0; i < G_N_ELEMENTS(huf_tables->dc_tables); i++)
        huf_tables->dc_tables[i].valid = FALSE;
    for (i = 0; i < G_N_ELEMENTS(huf_tables->ac_tables); i++)
        huf_tables->ac_tables[i].valid = FALSE;
}

static void
fill_huffman_table(GstVaapiHuffmanTable *huf_table,
    const GstJpegHuffmanTables *huf_tables)
{
    VAHuffmanTableBufferJPEGBaseline * const huffman_table = huf_table->param;
    guint i, num_tables;
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370

    num_tables = MIN(G_N_ELEMENTS(huffman_table->huffman_table),
                     GST_JPEG_MAX_SCAN_COMPONENTS);

    for (i = 0; i < num_tables; i++) {
        huffman_table->load_huffman_table[i] =
            huf_tables->dc_tables[i].valid && huf_tables->ac_tables[i].valid;
        if (!huffman_table->load_huffman_table[i])
            continue;

        memcpy(huffman_table->huffman_table[i].num_dc_codes,
               huf_tables->dc_tables[i].huf_bits,
               sizeof(huffman_table->huffman_table[i].num_dc_codes));
        memcpy(huffman_table->huffman_table[i].dc_values,
               huf_tables->dc_tables[i].huf_values,
               sizeof(huffman_table->huffman_table[i].dc_values));
        memcpy(huffman_table->huffman_table[i].num_ac_codes,
               huf_tables->ac_tables[i].huf_bits,
               sizeof(huffman_table->huffman_table[i].num_ac_codes));
        memcpy(huffman_table->huffman_table[i].ac_values,
               huf_tables->ac_tables[i].huf_values,
               sizeof(huffman_table->huffman_table[i].ac_values));
371 372 373
        memset(huffman_table->huffman_table[i].pad,
               0,
               sizeof(huffman_table->huffman_table[i].pad));
Wind Yuan's avatar
Wind Yuan committed
374 375 376
    }
}

377 378 379
static void
get_max_sampling_factors(const GstJpegFrameHdr *frame_hdr,
    guint *h_max_ptr, guint *v_max_ptr)
Wind Yuan's avatar
Wind Yuan committed
380
{
381 382 383
    guint h_max = frame_hdr->components[0].horizontal_factor;
    guint v_max = frame_hdr->components[0].vertical_factor;
    guint i;
Wind Yuan's avatar
Wind Yuan committed
384

385 386 387 388 389 390
    for (i = 1; i < frame_hdr->num_components; i++) {
        const GstJpegFrameComponent * const fcp = &frame_hdr->components[i];
        if (h_max < fcp->horizontal_factor)
            h_max = fcp->horizontal_factor;
        if (v_max < fcp->vertical_factor)
            v_max = fcp->vertical_factor;
Wind Yuan's avatar
Wind Yuan committed
391
    }
392 393 394 395 396

    if (h_max_ptr)
        *h_max_ptr = h_max;
    if (v_max_ptr)
        *v_max_ptr = v_max;
Wind Yuan's avatar
Wind Yuan committed
397 398
}

399 400
static const GstJpegFrameComponent *
get_component(const GstJpegFrameHdr *frame_hdr, guint selector)
Wind Yuan's avatar
Wind Yuan committed
401
{
402
    guint i;
Wind Yuan's avatar
Wind Yuan committed
403

404
    for (i = 0; i < frame_hdr->num_components; i++) {
405 406 407
        const GstJpegFrameComponent * const fcp = &frame_hdr->components[i];
        if (fcp->identifier == selector)
            return fcp;
Wind Yuan's avatar
Wind Yuan committed
408
    }
409
    return NULL;
Wind Yuan's avatar
Wind Yuan committed
410 411 412
}

static GstVaapiDecoderStatus
413 414
decode_picture(GstVaapiDecoderJpeg *decoder, GstJpegMarkerSegment *seg,
    const guchar *buf)
Wind Yuan's avatar
Wind Yuan committed
415
{
416
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
417
    GstJpegFrameHdr * const frame_hdr = &priv->frame_hdr;
Wind Yuan's avatar
Wind Yuan committed
418

419 420 421
    if (!VALID_STATE(decoder, GOT_SOI))
        return GST_VAAPI_DECODER_STATUS_SUCCESS;

422
    switch (seg->marker) {
423 424 425 426
    case GST_JPEG_MARKER_SOF_MIN:
        priv->profile = GST_VAAPI_PROFILE_JPEG_BASELINE;
        break;
    default:
427
        GST_ERROR("unsupported profile %d", seg->marker);
428
        return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
429 430
    }

431
    memset(frame_hdr, 0, sizeof(*frame_hdr));
432
    if (!gst_jpeg_parse_frame_hdr(frame_hdr, buf + seg->offset, seg->size, 0)) {
Wind Yuan's avatar
Wind Yuan committed
433
        GST_ERROR("failed to parse image");
434
        return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
Wind Yuan's avatar
Wind Yuan committed
435
    }
436 437
    priv->height = frame_hdr->height;
    priv->width  = frame_hdr->width;
438 439

    priv->decoder_state |= GST_JPEG_VIDEO_STATE_GOT_SOF;
440
    return GST_VAAPI_DECODER_STATUS_SUCCESS;
441 442 443 444 445
}

static GstVaapiDecoderStatus
decode_huffman_table(
    GstVaapiDecoderJpeg *decoder,
446
    const guchar        *buf,
447 448 449
    guint                buf_size
)
{
450
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
451

452 453 454
    if (!VALID_STATE(decoder, GOT_SOI))
        return GST_VAAPI_DECODER_STATUS_SUCCESS;

455
    if (!gst_jpeg_parse_huffman_table(&priv->huf_tables, buf, buf_size, 0)) {
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
456
        GST_ERROR("failed to parse Huffman table");
457
        return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
Wind Yuan's avatar
Wind Yuan committed
458
    }
459 460

    priv->decoder_state |= GST_JPEG_VIDEO_STATE_GOT_HUF_TABLE;
461 462 463 464 465 466
    return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

static GstVaapiDecoderStatus
decode_quant_table(
    GstVaapiDecoderJpeg *decoder,
467
    const guchar        *buf,
468 469 470
    guint                buf_size
)
{
471
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
Wind Yuan's avatar
Wind Yuan committed
472

473 474 475
    if (!VALID_STATE(decoder, GOT_SOI))
        return GST_VAAPI_DECODER_STATUS_SUCCESS;

476
    if (!gst_jpeg_parse_quant_table(&priv->quant_tables, buf, buf_size, 0)) {
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
477
        GST_ERROR("failed to parse quantization table");
478
        return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
479
    }
480 481

    priv->decoder_state |= GST_JPEG_VIDEO_STATE_GOT_IQ_TABLE;
482 483 484 485 486 487
    return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

static GstVaapiDecoderStatus
decode_restart_interval(
    GstVaapiDecoderJpeg *decoder,
488
    const guchar        *buf,
489 490 491
    guint                buf_size
)
{
492
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
493

494 495 496
    if (!VALID_STATE(decoder, GOT_SOI))
        return GST_VAAPI_DECODER_STATUS_SUCCESS;

497
    if (!gst_jpeg_parse_restart_interval(&priv->mcu_restart, buf, buf_size, 0)) {
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
498
        GST_ERROR("failed to parse restart interval");
499
        return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
500 501 502 503 504
    }
    return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

static GstVaapiDecoderStatus
505 506
decode_scan(GstVaapiDecoderJpeg *decoder, GstJpegMarkerSegment *seg,
    const guchar *buf)
507
{
508
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
509 510
    GstVaapiPicture * const picture = priv->current_picture;
    GstVaapiSlice *slice;
511
    VASliceParameterBufferJPEGBaseline *slice_param;
512 513
    GstJpegScanHdr scan_hdr;
    guint scan_hdr_size, scan_data_size;
514
    guint i, h_max, v_max, mcu_width, mcu_height;
515

516 517 518
    if (!VALID_STATE(decoder, GOT_SOF))
        return GST_VAAPI_DECODER_STATUS_SUCCESS;

519 520
    scan_hdr_size = (buf[seg->offset] << 8) | buf[seg->offset + 1];
    scan_data_size = seg->size - scan_hdr_size;
Wind Yuan's avatar
Wind Yuan committed
521

522
    memset(&scan_hdr, 0, sizeof(scan_hdr));
523
    if (!gst_jpeg_parse_scan_hdr(&scan_hdr, buf + seg->offset, seg->size, 0)) {
Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
524
        GST_ERROR("failed to parse scan header");
525
        return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER;
526
    }
527

528 529 530 531 532 533 534
    slice = GST_VAAPI_SLICE_NEW(JPEGBaseline, decoder,
        buf + seg->offset + scan_hdr_size, scan_data_size);
    if (!slice) {
        GST_ERROR("failed to allocate slice");
        return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
    }
    gst_vaapi_picture_add_slice(picture, slice);
535

536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
    if (!VALID_STATE(decoder, GOT_HUF_TABLE))
        gst_jpeg_get_default_huffman_tables(&priv->huf_tables);

    // Update VA Huffman table if it changed for this scan
    if (huffman_tables_updated(&priv->huf_tables)) {
        slice->huf_table = GST_VAAPI_HUFFMAN_TABLE_NEW(JPEGBaseline, decoder);
        if (!slice->huf_table) {
            GST_ERROR("failed to allocate Huffman tables");
            huffman_tables_reset(&priv->huf_tables);
            return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
        }
        fill_huffman_table(slice->huf_table, &priv->huf_tables);
        huffman_tables_reset(&priv->huf_tables);
    }

551
    slice_param = slice->param;
552 553
    slice_param->num_components = scan_hdr.num_components;
    for (i = 0; i < scan_hdr.num_components; i++) {
554 555 556 557 558 559
        slice_param->components[i].component_selector =
            scan_hdr.components[i].component_selector;
        slice_param->components[i].dc_table_selector =
            scan_hdr.components[i].dc_selector;
        slice_param->components[i].ac_table_selector =
            scan_hdr.components[i].ac_selector;
560 561
    }
    slice_param->restart_interval = priv->mcu_restart;
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
    slice_param->slice_horizontal_position = 0;
    slice_param->slice_vertical_position = 0;

    get_max_sampling_factors(&priv->frame_hdr, &h_max, &v_max);
    mcu_width = 8 * h_max;
    mcu_height = 8 * v_max;

    if (scan_hdr.num_components == 1) { // Non-interleaved
        const guint Csj = slice_param->components[0].component_selector;
        const GstJpegFrameComponent * const fcp =
            get_component(&priv->frame_hdr, Csj);

        if (!fcp || fcp->horizontal_factor == 0 || fcp->vertical_factor == 0) {
            GST_ERROR("failed to validate image component %u", Csj);
            return GST_VAAPI_DECODER_STATUS_ERROR_INVALID_PARAMETER;
577
        }
578 579
        mcu_width /= fcp->horizontal_factor;
        mcu_height /= fcp->vertical_factor;
580
    }
581 582 583
    slice_param->num_mcus =
        ((priv->frame_hdr.width + mcu_width - 1) / mcu_width) *
        ((priv->frame_hdr.height + mcu_height - 1) / mcu_height);
584 585

    priv->decoder_state |= GST_JPEG_VIDEO_STATE_GOT_SOS;
586
    return GST_VAAPI_DECODER_STATUS_SUCCESS;
Wind Yuan's avatar
Wind Yuan committed
587 588 589
}

static GstVaapiDecoderStatus
590 591
decode_segment(GstVaapiDecoderJpeg *decoder, GstJpegMarkerSegment *seg,
    const guchar *buf)
Wind Yuan's avatar
Wind Yuan committed
592
{
593
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
594
    GstVaapiDecoderStatus status;
595

596 597 598 599 600
    // Decode segment
    status = GST_VAAPI_DECODER_STATUS_SUCCESS;
    switch (seg->marker) {
    case GST_JPEG_MARKER_SOI:
        priv->mcu_restart = 0;
601 602 603 604
        priv->decoder_state |= GST_JPEG_VIDEO_STATE_GOT_SOI;
        break;
    case GST_JPEG_MARKER_EOI:
        priv->decoder_state = 0;
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
        break;
    case GST_JPEG_MARKER_DAC:
        GST_ERROR("unsupported arithmetic coding mode");
        status = GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE;
        break;
    case GST_JPEG_MARKER_DHT:
        status = decode_huffman_table(decoder, buf + seg->offset, seg->size);
        break;
    case GST_JPEG_MARKER_DQT:
        status = decode_quant_table(decoder, buf + seg->offset, seg->size);
        break;
    case GST_JPEG_MARKER_DRI:
        status = decode_restart_interval(decoder, buf + seg->offset, seg->size);
        break;
    case GST_JPEG_MARKER_SOS:
        status = decode_scan(decoder, seg, buf);
        break;
    default:
        // SOFn segments
        if (seg->marker >= GST_JPEG_MARKER_SOF_MIN &&
            seg->marker <= GST_JPEG_MARKER_SOF_MAX)
            status = decode_picture(decoder, seg, buf);
        break;
628 629
    }
    return status;
Wind Yuan's avatar
Wind Yuan committed
630 631
}

632 633
static GstVaapiDecoderStatus
ensure_decoder(GstVaapiDecoderJpeg *decoder)
Wind Yuan's avatar
Wind Yuan committed
634
{
635
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
636

Wind Yuan's avatar
Wind Yuan committed
637
    if (!priv->is_opened) {
638
        priv->is_opened = gst_vaapi_decoder_jpeg_open(decoder);
Wind Yuan's avatar
Wind Yuan committed
639 640 641
        if (!priv->is_opened)
            return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CODEC;
    }
642 643 644
    return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

645 646
static gboolean
is_scan_complete(GstJpegMarkerCode marker)
647
{
648 649
    // Scan is assumed to be complete when the new segment is not RSTi
    return marker < GST_JPEG_MARKER_RST_MIN || marker > GST_JPEG_MARKER_RST_MAX;
650 651 652 653
}

static GstVaapiDecoderStatus
gst_vaapi_decoder_jpeg_parse(GstVaapiDecoder *base_decoder,
654
    GstAdapter *adapter, gboolean at_eos, GstVaapiDecoderUnit *unit)
655
{
656 657
    GstVaapiDecoderJpeg * const decoder =
        GST_VAAPI_DECODER_JPEG_CAST(base_decoder);
658
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
659
    GstVaapiParserState * const ps = GST_VAAPI_PARSER_STATE(base_decoder);
660
    GstVaapiDecoderStatus status;
661 662
    GstJpegMarkerCode marker;
    GstJpegMarkerSegment seg;
663 664
    const guchar *buf;
    guint buf_size, flags;
665
    gint ofs1, ofs2;
666 667 668 669 670

    status = ensure_decoder(decoder);
    if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
        return status;

671
    /* Expect at least 2 bytes for the marker */
672
    buf_size = gst_adapter_available(adapter);
673
    if (buf_size < 2)
674 675
        return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;

676 677
    buf = gst_adapter_map(adapter, buf_size);
    if (!buf)
678 679
        return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;

680 681 682
    ofs1 = ps->input_offset1 - 2;
    if (ofs1 < 0)
        ofs1 = 0;
683

684 685 686 687 688
    for (;;) {
        // Skip any garbage until we reach SOI, if needed
        if (!gst_jpeg_parse(&seg, buf, buf_size, ofs1)) {
            gst_adapter_unmap(adapter);
            ps->input_offset1 = buf_size;
689
            return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
690
        }
691 692 693
        ofs1 = seg.offset;

        marker = seg.marker;
694
        if (!VALID_STATE(parser, GOT_SOI) && marker != GST_JPEG_MARKER_SOI)
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
            continue;
        if (marker == GST_JPEG_MARKER_SOS) {
            ofs2 = ps->input_offset2 - 2;
            if (ofs2 < ofs1 + seg.size)
                ofs2 = ofs1 + seg.size;

            // Parse the whole scan + ECSs, including RSTi
            for (;;) {
                if (!gst_jpeg_parse(&seg, buf, buf_size, ofs2)) {
                    gst_adapter_unmap(adapter);
                    ps->input_offset1 = ofs1;
                    ps->input_offset2 = buf_size;
                    return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
                }

                if (is_scan_complete(seg.marker))
                    break;
                ofs2 = seg.offset + seg.size;
            }
            ofs2 = seg.offset - 2;
        }
        else {
            // Check that the whole segment is actually available (in buffer)
            ofs2 = ofs1 + seg.size;
            if (ofs2 > buf_size) {
                gst_adapter_unmap(adapter);
                ps->input_offset1 = ofs1;
                return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
            }
        }
        break;
726
    }
727
    gst_adapter_unmap(adapter);
728

729 730
    unit->size = ofs2 - ofs1;
    unit_set_marker_code(unit, marker);
731
    gst_adapter_flush(adapter, ofs1);
732
    ps->input_offset1 = 2;
733
    ps->input_offset2 = 2;
734

735 736 737 738
    flags = 0;
    switch (marker) {
    case GST_JPEG_MARKER_SOI:
        flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_START;
739
        priv->parser_state |= GST_JPEG_VIDEO_STATE_GOT_SOI;
740 741 742
        break;
    case GST_JPEG_MARKER_EOI:
        flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_END;
743
        priv->parser_state = 0;
744 745 746
        break;
    case GST_JPEG_MARKER_SOS:
        flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE;
747
        priv->parser_state |= GST_JPEG_VIDEO_STATE_GOT_SOS;
748
        break;
749 750 751 752 753 754 755 756 757 758
    case GST_JPEG_MARKER_DAC:
    case GST_JPEG_MARKER_DHT:
    case GST_JPEG_MARKER_DQT:
        if (priv->parser_state & GST_JPEG_VIDEO_STATE_GOT_SOF)
            flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE;
        break;
    case GST_JPEG_MARKER_DRI:
        if (priv->parser_state & GST_JPEG_VIDEO_STATE_GOT_SOS)
            flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE;
        break;
759 760 761 762 763 764 765
    case GST_JPEG_MARKER_DNL:
        flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE;
        break;
    case GST_JPEG_MARKER_COM:
        flags |= GST_VAAPI_DECODER_UNIT_FLAG_SKIP;
        break;
    default:
766 767 768 769 770
        /* SOFn segments */
        if (marker >= GST_JPEG_MARKER_SOF_MIN &&
            marker <= GST_JPEG_MARKER_SOF_MAX)
            priv->parser_state |= GST_JPEG_VIDEO_STATE_GOT_SOF;

771
        /* Application segments */
772 773
        else if (marker >= GST_JPEG_MARKER_APP_MIN &&
                 marker <= GST_JPEG_MARKER_APP_MAX)
774 775 776 777 778 779 780
            flags |= GST_VAAPI_DECODER_UNIT_FLAG_SKIP;

        /* Reserved */
        else if (marker >= 0x02 && marker <= 0xbf)
            flags |= GST_VAAPI_DECODER_UNIT_FLAG_SKIP;
        break;
    }
781 782 783 784 785 786 787 788
    GST_VAAPI_DECODER_UNIT_FLAG_SET(unit, flags);
    return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

static GstVaapiDecoderStatus
gst_vaapi_decoder_jpeg_decode(GstVaapiDecoder *base_decoder,
    GstVaapiDecoderUnit *unit)
{
789 790
    GstVaapiDecoderJpeg * const decoder =
        GST_VAAPI_DECODER_JPEG_CAST(base_decoder);
791
    GstVaapiDecoderStatus status;
792
    GstJpegMarkerSegment seg;
793 794
    GstBuffer * const buffer =
        GST_VAAPI_DECODER_CODEC_FRAME(decoder)->input_buffer;
795
    GstMapInfo map_info;
796 797 798 799 800

    status = ensure_decoder(decoder);
    if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
        return status;

801 802 803 804
    if (!gst_buffer_map(buffer, &map_info, GST_MAP_READ)) {
        GST_ERROR("failed to map buffer");
        return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;
    }
805

806 807 808 809 810
    seg.marker = unit_get_marker_code(unit);
    seg.offset = unit->offset;
    seg.size = unit->size;

    status = decode_segment(decoder, &seg, map_info.data);
811
    gst_buffer_unmap(buffer, &map_info);
812 813 814
    if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
        return status;
    return GST_VAAPI_DECODER_STATUS_SUCCESS;
Wind Yuan's avatar
Wind Yuan committed
815 816
}

817 818 819 820 821 822 823 824 825 826
static GstVaapiDecoderStatus
gst_vaapi_decoder_jpeg_start_frame(GstVaapiDecoder *base_decoder,
    GstVaapiDecoderUnit *base_unit)
{
    GstVaapiDecoderJpeg * const decoder =
        GST_VAAPI_DECODER_JPEG_CAST(base_decoder);
    GstVaapiDecoderJpegPrivate * const priv = &decoder->priv;
    GstVaapiPicture *picture;
    GstVaapiDecoderStatus status;

827 828 829
    if (!VALID_STATE(decoder, GOT_SOF))
        return GST_VAAPI_DECODER_STATUS_SUCCESS;

830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
    status = ensure_context(decoder);
    if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) {
        GST_ERROR("failed to reset context");
        return status;
    }

    picture = GST_VAAPI_PICTURE_NEW(JPEGBaseline, decoder);
    if (!picture) {
        GST_ERROR("failed to allocate picture");
        return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
    }
    gst_vaapi_picture_replace(&priv->current_picture, picture);
    gst_vaapi_picture_unref(picture);

    if (!fill_picture(decoder, picture, &priv->frame_hdr))
        return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN;

Gwenole Beauchesne's avatar
Gwenole Beauchesne committed
847 848 849
    status = fill_quantization_table(decoder, picture);
    if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
        return status;
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864

    /* Update presentation time */
    picture->pts = GST_VAAPI_DECODER_CODEC_FRAME(decoder)->pts;
    return GST_VAAPI_DECODER_STATUS_SUCCESS;
}

static GstVaapiDecoderStatus
gst_vaapi_decoder_jpeg_end_frame(GstVaapiDecoder *base_decoder)
{
    GstVaapiDecoderJpeg * const decoder =
        GST_VAAPI_DECODER_JPEG_CAST(base_decoder);

    return decode_current_picture(decoder);
}

Wind Yuan's avatar
Wind Yuan committed
865 866 867
static void
gst_vaapi_decoder_jpeg_class_init(GstVaapiDecoderJpegClass *klass)
{
868 869
    GstVaapiMiniObjectClass * const object_class =
        GST_VAAPI_MINI_OBJECT_CLASS(klass);
Wind Yuan's avatar
Wind Yuan committed
870 871
    GstVaapiDecoderClass * const decoder_class = GST_VAAPI_DECODER_CLASS(klass);

872 873
    object_class->size          = sizeof(GstVaapiDecoderJpeg);
    object_class->finalize      = (GDestroyNotify)gst_vaapi_decoder_finalize;
Wind Yuan's avatar
Wind Yuan committed
874

875 876
    decoder_class->create       = gst_vaapi_decoder_jpeg_create;
    decoder_class->destroy      = gst_vaapi_decoder_jpeg_destroy;
877
    decoder_class->parse        = gst_vaapi_decoder_jpeg_parse;
Wind Yuan's avatar
Wind Yuan committed
878
    decoder_class->decode       = gst_vaapi_decoder_jpeg_decode;
879 880
    decoder_class->start_frame  = gst_vaapi_decoder_jpeg_start_frame;
    decoder_class->end_frame    = gst_vaapi_decoder_jpeg_end_frame;
Wind Yuan's avatar
Wind Yuan committed
881 882
}

883 884
static inline const GstVaapiDecoderClass *
gst_vaapi_decoder_jpeg_class(void)
Wind Yuan's avatar
Wind Yuan committed
885
{
886 887
    static GstVaapiDecoderJpegClass g_class;
    static gsize g_class_init = FALSE;
Wind Yuan's avatar
Wind Yuan committed
888

889 890 891 892 893
    if (g_once_init_enter(&g_class_init)) {
        gst_vaapi_decoder_jpeg_class_init(&g_class);
        g_once_init_leave(&g_class_init, TRUE);
    }
    return GST_VAAPI_DECODER_CLASS(&g_class);
Wind Yuan's avatar
Wind Yuan committed
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
}

/**
 * gst_vaapi_decoder_jpeg_new:
 * @display: a #GstVaapiDisplay
 * @caps: a #GstCaps holding codec information
 *
 * Creates a new #GstVaapiDecoder for JPEG decoding.  The @caps can
 * hold extra information like codec-data and pictured coded size.
 *
 * Return value: the newly allocated #GstVaapiDecoder object
 */
GstVaapiDecoder *
gst_vaapi_decoder_jpeg_new(GstVaapiDisplay *display, GstCaps *caps)
{
909
    return gst_vaapi_decoder_new(gst_vaapi_decoder_jpeg_class(), display, caps);
Wind Yuan's avatar
Wind Yuan committed
910
}