From 49b4702fb696df18168343e12d99801585981da8 Mon Sep 17 00:00:00 2001 From: Gwenole Beauchesne Date: Fri, 5 Aug 2011 11:55:11 +0200 Subject: [PATCH] Add initial MPEG-2 decoder. --- gst-libs/gst/vaapi/Makefile.am | 2 + gst-libs/gst/vaapi/gstvaapidecoder_mpeg2.c | 942 +++++++++++++++++++++++++++++ gst-libs/gst/vaapi/gstvaapidecoder_mpeg2.h | 87 +++ gst/vaapi/gstvaapidecode.c | 9 + tests/test-decode.c | 4 + 5 files changed, 1044 insertions(+) create mode 100644 gst-libs/gst/vaapi/gstvaapidecoder_mpeg2.c create mode 100644 gst-libs/gst/vaapi/gstvaapidecoder_mpeg2.h diff --git a/gst-libs/gst/vaapi/Makefile.am b/gst-libs/gst/vaapi/Makefile.am index 4333aa3..8023944 100644 --- a/gst-libs/gst/vaapi/Makefile.am +++ b/gst-libs/gst/vaapi/Makefile.am @@ -144,8 +144,10 @@ endif if USE_CODEC_PARSERS libgstvaapi_source_c += \ + gstvaapidecoder_mpeg2.c \ $(NULL) libgstvaapi_source_h += \ + gstvaapidecoder_mpeg2.h \ $(NULL) libgstvaapi_cflags += $(GST_CODEC_PARSERS_CFLAGS) libgstvaapi_libs += $(GST_CODEC_PARSERS_LIBS) diff --git a/gst-libs/gst/vaapi/gstvaapidecoder_mpeg2.c b/gst-libs/gst/vaapi/gstvaapidecoder_mpeg2.c new file mode 100644 index 0000000..5a5d376 --- /dev/null +++ b/gst-libs/gst/vaapi/gstvaapidecoder_mpeg2.c @@ -0,0 +1,942 @@ +/* + * gstvaapidecoder_mpeg2.c - MPEG-2 decoder + * + * Copyright (C) 2011 Intel Corporation + * + * 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_mpeg2 + * @short_description: MPEG-2 decoder + */ + +#include "config.h" +#include +#include +#include +#include "gstvaapidecoder_mpeg2.h" +#include "gstvaapidecoder_priv.h" +#include "gstvaapidisplay_priv.h" +#include "gstvaapiobject_priv.h" +#include "gstvaapiutils_tsb.h" + +#define DEBUG 1 +#include "gstvaapidebug.h" + +G_DEFINE_TYPE(GstVaapiDecoderMpeg2, + gst_vaapi_decoder_mpeg2, + GST_VAAPI_TYPE_DECODER); + +#define GST_VAAPI_DECODER_MPEG2_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE((obj), \ + GST_VAAPI_TYPE_DECODER_MPEG2, \ + GstVaapiDecoderMpeg2Private)) + +#define READ_UINT8(br, val, nbits) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint8 (br, &val, nbits)) { \ + GST_WARNING ("failed to read uint8, nbits: %d", nbits); \ + goto failed; \ + } \ +} G_STMT_END + +struct _GstVaapiDecoderMpeg2Private { + GstVaapiProfile profile; + guint width; + guint height; + guint fps_n; + guint fps_d; + GstMpegVideoSequenceHdr seq_hdr; + GstMpegVideoSequenceExt seq_ext; + GstMpegVideoPictureHdr pic_hdr; + GstMpegVideoPictureExt pic_ext; + GstMpegVideoQuantMatrixExt quant_matrix_ext; + GstVaapiPicture *current_picture; + GstVaapiPicture *next_picture; + GstVaapiPicture *prev_picture; + GstVaapiTSB *tsb; + GstBuffer *sub_buffer; + guint mb_y; + guint mb_height; + GstClockTime seq_pts; + GstClockTime gop_pts; + GstClockTime pts_diff; + guint is_constructed : 1; + guint is_opened : 1; + guint is_first_field : 1; + guint has_seq_ext : 1; + guint has_seq_scalable_ext : 1; + guint has_pic_ext : 1; + guint has_quant_matrix_ext : 1; + guint size_changed : 1; + guint profile_changed : 1; + guint quant_matrix_changed : 1; + guint progressive_sequence : 1; +}; + +static void +gst_vaapi_decoder_mpeg2_close(GstVaapiDecoderMpeg2 *decoder) +{ + GstVaapiDecoder * const base_decoder = GST_VAAPI_DECODER(decoder); + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + + if (priv->current_picture) { + gst_vaapi_decoder_free_picture(base_decoder, priv->current_picture); + priv->current_picture = NULL; + } + + if (priv->next_picture) { + gst_vaapi_decoder_free_picture(base_decoder, priv->next_picture); + priv->next_picture = NULL; + } + + if (priv->prev_picture) { + gst_vaapi_decoder_free_picture(base_decoder, priv->prev_picture); + priv->prev_picture = NULL; + } + + if (priv->sub_buffer) { + gst_buffer_unref(priv->sub_buffer); + priv->sub_buffer = NULL; + } + + if (priv->tsb) { + gst_vaapi_tsb_destroy(priv->tsb); + priv->tsb = NULL; + } +} + +static gboolean +gst_vaapi_decoder_mpeg2_open(GstVaapiDecoderMpeg2 *decoder, GstBuffer *buffer) +{ + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + + gst_vaapi_decoder_mpeg2_close(decoder); + + priv->tsb = gst_vaapi_tsb_new(); + if (!priv->tsb) + return FALSE; + return TRUE; +} + +static void +gst_vaapi_decoder_mpeg2_destroy(GstVaapiDecoderMpeg2 *decoder) +{ + gst_vaapi_decoder_mpeg2_close(decoder); +} + +static gboolean +gst_vaapi_decoder_mpeg2_create(GstVaapiDecoderMpeg2 *decoder) +{ + if (!GST_VAAPI_DECODER_CODEC(decoder)) + return FALSE; + return TRUE; +} + +static inline void +copy_quant_matrix(guint8 dst[64], const guint8 src[64]) +{ + memcpy(dst, src, 64); +} + +static GstVaapiDecoderStatus +ensure_context(GstVaapiDecoderMpeg2 *decoder) +{ + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + 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_MPEG2_SIMPLE) + profiles[n_profiles++] = GST_VAAPI_PROFILE_MPEG2_MAIN; + + 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 (priv->size_changed) { + GST_DEBUG("size changed"); + priv->size_changed = FALSE; + reset_context = TRUE; + + if (priv->progressive_sequence) + priv->mb_height = (priv->height + 15) / 16; + else + priv->mb_height = (priv->height + 31) / 32 * 2; + } + + if (reset_context) { + reset_context = gst_vaapi_decoder_ensure_context( + GST_VAAPI_DECODER(decoder), + priv->profile, + entrypoint, + priv->width, priv->height + ); + if (!reset_context) + return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; + } + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static GstVaapiDecoderStatus +ensure_quant_matrix(GstVaapiDecoderMpeg2 *decoder, GstVaapiPicture *picture) +{ + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + VAIQMatrixBufferMPEG2 *iq_matrix; + guint8 *intra_quant_matrix = NULL; + guint8 *non_intra_quant_matrix = NULL; + guint8 *chroma_intra_quant_matrix = NULL; + guint8 *chroma_non_intra_quant_matrix = NULL; + + if (!priv->quant_matrix_changed) + return GST_VAAPI_DECODER_STATUS_SUCCESS; + + priv->quant_matrix_changed = FALSE; + + picture->iq_matrix = gst_vaapi_decoder_new_iq_matrix(GST_VAAPI_DECODER(decoder)); + if (!picture->iq_matrix) { + GST_DEBUG("failed to allocate IQ matrix"); + return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; + } + iq_matrix = picture->iq_matrix->param; + + intra_quant_matrix = priv->seq_hdr.intra_quantizer_matrix; + non_intra_quant_matrix = priv->seq_hdr.non_intra_quantizer_matrix; + if (priv->has_quant_matrix_ext) { + if (priv->quant_matrix_ext.load_intra_quantiser_matrix) + intra_quant_matrix = priv->quant_matrix_ext.intra_quantiser_matrix; + if (priv->quant_matrix_ext.load_non_intra_quantiser_matrix) + non_intra_quant_matrix = priv->quant_matrix_ext.non_intra_quantiser_matrix; + if (priv->quant_matrix_ext.load_chroma_intra_quantiser_matrix) + chroma_intra_quant_matrix = priv->quant_matrix_ext.chroma_intra_quantiser_matrix; + if (priv->quant_matrix_ext.load_chroma_non_intra_quantiser_matrix) + chroma_non_intra_quant_matrix = priv->quant_matrix_ext.chroma_non_intra_quantiser_matrix; + } + + iq_matrix->load_intra_quantiser_matrix = intra_quant_matrix != NULL; + if (intra_quant_matrix) { + iq_matrix->load_intra_quantiser_matrix = 1; + copy_quant_matrix(iq_matrix->intra_quantiser_matrix, + intra_quant_matrix); + } + + iq_matrix->load_intra_quantiser_matrix = 1; + copy_quant_matrix(iq_matrix->intra_quantiser_matrix, + priv->seq_hdr.intra_quantizer_matrix); + + iq_matrix->load_non_intra_quantiser_matrix = 1; + copy_quant_matrix(iq_matrix->non_intra_quantiser_matrix, + priv->seq_hdr.non_intra_quantizer_matrix); + + iq_matrix->load_chroma_intra_quantiser_matrix = 0; + iq_matrix->load_chroma_non_intra_quantiser_matrix = 0; + if (priv->has_quant_matrix_ext) { + if (priv->quant_matrix_ext.load_intra_quantiser_matrix) + copy_quant_matrix(iq_matrix->intra_quantiser_matrix, + priv->quant_matrix_ext.intra_quantiser_matrix); + + if (priv->quant_matrix_ext.load_non_intra_quantiser_matrix) + copy_quant_matrix(iq_matrix->non_intra_quantiser_matrix, + priv->quant_matrix_ext.non_intra_quantiser_matrix); + + if (priv->quant_matrix_ext.load_chroma_intra_quantiser_matrix) { + iq_matrix->load_chroma_intra_quantiser_matrix = 1; + copy_quant_matrix(iq_matrix->chroma_intra_quantiser_matrix, + priv->quant_matrix_ext.chroma_intra_quantiser_matrix); + } + + if (priv->quant_matrix_ext.load_chroma_non_intra_quantiser_matrix) { + iq_matrix->load_chroma_non_intra_quantiser_matrix = 1; + copy_quant_matrix(iq_matrix->chroma_non_intra_quantiser_matrix, + priv->quant_matrix_ext.chroma_non_intra_quantiser_matrix); + } + } + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static inline GstVaapiDecoderStatus +render_picture(GstVaapiDecoderMpeg2 *decoder, GstVaapiPicture *picture) +{ + GstVaapiDecoder * const base_decoder = GST_VAAPI_DECODER(decoder); + + if (!gst_vaapi_decoder_push_surface(base_decoder, + picture->surface, + picture->pts)) + return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static GstVaapiDecoderStatus +decode_current_picture(GstVaapiDecoderMpeg2 *decoder) +{ + GstVaapiDecoder * const base_decoder = GST_VAAPI_DECODER(decoder); + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + GstVaapiPicture * const picture = priv->current_picture; + GstVaapiDecoderStatus status = GST_VAAPI_DECODER_STATUS_SUCCESS; + + if (picture) { + if (!gst_vaapi_decoder_decode_picture(base_decoder, picture)) + status = GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; + if (!GST_VAAPI_PICTURE_IS_REFERENCE(picture)) { + if (priv->prev_picture && priv->next_picture) + status = render_picture(decoder, picture); + gst_vaapi_decoder_free_picture(base_decoder, picture); + } + priv->current_picture = NULL; + } + return status; +} + +static GstVaapiDecoderStatus +decode_sequence(GstVaapiDecoderMpeg2 *decoder, guchar *buf, guint buf_size) +{ + GstVaapiDecoder * const base_decoder = GST_VAAPI_DECODER(decoder); + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + GstMpegVideoSequenceHdr * const seq_hdr = &priv->seq_hdr; + + if (!gst_mpeg_video_parse_sequence_header(seq_hdr, buf, buf_size, 0)) { + GST_DEBUG("failed to parse sequence header"); + return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; + } + + priv->fps_n = seq_hdr->fps_n; + priv->fps_d = seq_hdr->fps_d; + gst_vaapi_decoder_set_framerate(base_decoder, priv->fps_n, priv->fps_d); + + priv->seq_pts = gst_vaapi_tsb_get_timestamp(priv->tsb); + + priv->width = seq_hdr->width; + priv->height = seq_hdr->height; + priv->has_seq_ext = FALSE; + priv->size_changed = TRUE; + priv->quant_matrix_changed = TRUE; + priv->progressive_sequence = TRUE; + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static GstVaapiDecoderStatus +decode_sequence_ext(GstVaapiDecoderMpeg2 *decoder, guchar *buf, guint buf_size) +{ + GstVaapiDecoder * const base_decoder = GST_VAAPI_DECODER(decoder); + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + GstMpegVideoSequenceExt * const seq_ext = &priv->seq_ext; + GstVaapiProfile profile; + guint width, height; + + if (!gst_mpeg_video_parse_sequence_extension(seq_ext, buf, buf_size, 0)) { + GST_DEBUG("failed to parse sequence-extension"); + return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; + } + priv->has_seq_ext = TRUE; + priv->progressive_sequence = seq_ext->progressive; + + width = (priv->width & 0xffff) | ((guint32)seq_ext->horiz_size_ext << 16); + height = (priv->height & 0xffff) | ((guint32)seq_ext->vert_size_ext << 16); + GST_DEBUG("video resolution %ux%u", width, height); + + if (seq_ext->fps_n_ext && seq_ext->fps_d_ext) { + priv->fps_n *= seq_ext->fps_n_ext + 1; + priv->fps_d *= seq_ext->fps_d_ext + 1; + gst_vaapi_decoder_set_framerate(base_decoder, priv->fps_n, priv->fps_d); + } + + if (priv->width != width) { + priv->width = width; + priv->size_changed = TRUE; + } + + if (priv->height != height) { + priv->height = height; + priv->size_changed = TRUE; + } + + switch (seq_ext->profile) { + case GST_MPEG_VIDEO_PROFILE_SIMPLE: + profile = GST_VAAPI_PROFILE_MPEG2_SIMPLE; + break; + case GST_MPEG_VIDEO_PROFILE_MAIN: + profile = GST_VAAPI_PROFILE_MPEG2_MAIN; + break; + default: + GST_DEBUG("unsupported profile %d", seq_ext->profile); + return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE; + } + if (priv->profile != profile) { + priv->profile = profile; + priv->profile_changed = TRUE; + } + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static GstVaapiDecoderStatus +decode_sequence_end(GstVaapiDecoderMpeg2 *decoder) +{ + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + GstVaapiDecoderStatus status; + + if (priv->current_picture) { + status = decode_current_picture(decoder); + if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) + return status; + status = render_picture(decoder, priv->current_picture); + if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) + return status; + } + + if (priv->next_picture) { + status = render_picture(decoder, priv->next_picture); + if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) + return status; + } + return GST_VAAPI_DECODER_STATUS_END_OF_STREAM; +} + +static GstVaapiDecoderStatus +decode_quant_matrix_ext(GstVaapiDecoderMpeg2 *decoder, guchar *buf, guint buf_size) +{ + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + GstMpegVideoQuantMatrixExt * const quant_matrix_ext = &priv->quant_matrix_ext; + + if (!gst_mpeg_video_parse_quant_matrix_extension(quant_matrix_ext, buf, buf_size, 0)) { + GST_DEBUG("failed to parse quant-matrix-extension"); + return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; + } + priv->has_quant_matrix_ext = TRUE; + priv->quant_matrix_changed = TRUE; + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static GstVaapiDecoderStatus +decode_gop(GstVaapiDecoderMpeg2 *decoder, guchar *buf, guint buf_size) +{ + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + GstMpegVideoGop gop; + GstClockTime pts; + + if (!gst_mpeg_video_parse_gop(&gop, buf, buf_size, 0)) { + GST_DEBUG("failed to parse GOP"); + return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; + } + + GST_DEBUG("GOP %02u:%02u:%02u:%02u", + gop.hour, gop.minute, gop.second, gop.frame); + + pts = GST_SECOND * (gop.hour * 3600 + gop.minute * 60 + gop.second); + pts += gst_util_uint64_scale(gop.frame, GST_SECOND * priv->fps_d, priv->fps_n); + priv->gop_pts = pts; + if (!priv->pts_diff) + priv->pts_diff = priv->seq_pts - priv->gop_pts; + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static GstVaapiDecoderStatus +decode_picture(GstVaapiDecoderMpeg2 *decoder, guchar *buf, guint buf_size) +{ + GstVaapiDecoder * const base_decoder = GST_VAAPI_DECODER(decoder); + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + GstMpegVideoPictureHdr * const pic_hdr = &priv->pic_hdr; + GstVaapiPicture *picture; + GstVaapiDecoderStatus status; + GstClockTime pts; + + status = ensure_context(decoder); + if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) { + GST_DEBUG("failed to reset context"); + return status; + } + + if (priv->current_picture) { + status = decode_current_picture(decoder); + if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) + return status; + } + + priv->current_picture = gst_vaapi_decoder_new_picture(base_decoder); + if (!priv->current_picture) { + GST_DEBUG("failed to allocate picture"); + return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; + } + picture = priv->current_picture; + + status = ensure_quant_matrix(decoder, picture); + if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) { + GST_DEBUG("failed to reset quantizer matrix"); + return status; + } + + if (!gst_mpeg_video_parse_picture_header(pic_hdr, buf, buf_size, 0)) { + GST_DEBUG("failed to parse picture header"); + return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; + } + priv->has_pic_ext = FALSE; + + switch (pic_hdr->pic_type) { + case GST_MPEG_VIDEO_PICTURE_TYPE_I: + picture->type = GST_VAAPI_PICTURE_TYPE_I; + break; + case GST_MPEG_VIDEO_PICTURE_TYPE_P: + picture->type = GST_VAAPI_PICTURE_TYPE_P; + break; + case GST_MPEG_VIDEO_PICTURE_TYPE_B: + picture->type = GST_VAAPI_PICTURE_TYPE_B; + break; + default: + GST_DEBUG("unsupported picture type %d", pic_hdr->pic_type); + return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; + } + + priv->mb_y = 0; + if (pic_hdr->pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_I) + priv->is_first_field = TRUE; + else + priv->is_first_field ^= 1; + + /* Update presentation time */ + pts = priv->gop_pts; + pts += gst_util_uint64_scale(pic_hdr->tsn, GST_SECOND * priv->fps_d, priv->fps_n); + picture->pts = pts + priv->pts_diff; + + /* Update reference pictures */ + if (pic_hdr->pic_type != GST_MPEG_VIDEO_PICTURE_TYPE_B) { + picture->flags |= GST_VAAPI_PICTURE_REFERENCE; + if (priv->prev_picture) { + gst_vaapi_decoder_free_picture(base_decoder, priv->prev_picture); + priv->prev_picture = NULL; + } + if (priv->next_picture) { + priv->prev_picture = priv->next_picture; + priv->next_picture = NULL; + status = render_picture(decoder, priv->prev_picture); + } + priv->next_picture = picture; + } + return status; +} + +static GstVaapiDecoderStatus +decode_picture_ext(GstVaapiDecoderMpeg2 *decoder, guchar *buf, guint buf_size) +{ + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + GstMpegVideoPictureExt * const pic_ext = &priv->pic_ext; + + if (!gst_mpeg_video_parse_picture_extension(pic_ext, buf, buf_size, 0)) { + GST_DEBUG("failed to parse picture-extension"); + return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; + } + priv->has_pic_ext = TRUE; + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static inline guint32 +pack_f_code(guint8 f_code[2][2]) +{ + return (((guint32)f_code[0][0] << 12) | + ((guint32)f_code[0][1] << 8) | + ((guint32)f_code[1][0] << 4) | + ( f_code[1][1] )); +} + +static gboolean +fill_picture(GstVaapiDecoderMpeg2 *decoder, GstVaapiPicture *picture) +{ + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + VAPictureParameterBufferMPEG2 * const pic_param = picture->param; + GstMpegVideoPictureHdr * const pic_hdr = &priv->pic_hdr; + GstMpegVideoPictureExt * const pic_ext = &priv->pic_ext; + + if (!priv->has_pic_ext) + return FALSE; + + /* Fill in VAPictureParameterBufferMPEG2 */ + pic_param->horizontal_size = priv->width; + pic_param->vertical_size = priv->height; + pic_param->forward_reference_picture = VA_INVALID_ID; + pic_param->backward_reference_picture = VA_INVALID_ID; + pic_param->picture_coding_type = pic_hdr->pic_type; + pic_param->f_code = pack_f_code(pic_ext->f_code); + +#define COPY_FIELD(a, b, f) \ + pic_param->a.b.f = pic_ext->f + pic_param->picture_coding_extension.value = 0; + pic_param->picture_coding_extension.bits.is_first_field = priv->is_first_field; + COPY_FIELD(picture_coding_extension, bits, intra_dc_precision); + COPY_FIELD(picture_coding_extension, bits, picture_structure); + COPY_FIELD(picture_coding_extension, bits, top_field_first); + COPY_FIELD(picture_coding_extension, bits, frame_pred_frame_dct); + COPY_FIELD(picture_coding_extension, bits, concealment_motion_vectors); + COPY_FIELD(picture_coding_extension, bits, q_scale_type); + COPY_FIELD(picture_coding_extension, bits, intra_vlc_format); + COPY_FIELD(picture_coding_extension, bits, alternate_scan); + COPY_FIELD(picture_coding_extension, bits, repeat_first_field); + COPY_FIELD(picture_coding_extension, bits, progressive_frame); + + switch (pic_hdr->pic_type) { + case GST_MPEG_VIDEO_PICTURE_TYPE_B: + if (priv->next_picture) + pic_param->backward_reference_picture = priv->next_picture->surface_id; + // fall-through + case GST_MPEG_VIDEO_PICTURE_TYPE_P: + if (priv->prev_picture) + pic_param->forward_reference_picture = priv->prev_picture->surface_id; + break; + } + return TRUE; +} + +static GstVaapiDecoderStatus +decode_slice( + GstVaapiDecoderMpeg2 *decoder, + int slice_no, + guchar *buf, + guint buf_size +) +{ + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + GstVaapiPicture * const picture = priv->current_picture; + GstVaapiSlice *slice; + VASliceParameterBufferMPEG2 *slice_param; + GstVaapiDecoderStatus status; + GstBitReader br; + guint8 slice_vertical_position_extension; + guint8 quantiser_scale_code; + guint8 intra_slice_flag, intra_slice = 0; + guint8 extra_bit_slice, junk8; + + GST_DEBUG("slice %d @ %p, %u bytes)", slice_no, buf, buf_size); + + if (picture->slices->len == 0 && !fill_picture(decoder, picture)) + return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; + + priv->mb_y = slice_no; + + slice = gst_vaapi_decoder_new_slice( + GST_VAAPI_DECODER(decoder), + picture, + buf, buf_size + ); + if (!slice) { + GST_DEBUG("failed to allocate slice"); + return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; + } + + /* Parse slice */ + gst_bit_reader_init(&br, buf, buf_size); + if (priv->height > 2800) + READ_UINT8(&br, slice_vertical_position_extension, 3); + if (priv->has_seq_scalable_ext) { + GST_DEBUG("failed to parse slice %d. Unsupported sequence_scalable_extension()", slice_no); + return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; + } + READ_UINT8(&br, quantiser_scale_code, 5); + READ_UINT8(&br, extra_bit_slice, 1); + if (extra_bit_slice == 1) { + READ_UINT8(&br, intra_slice_flag, 1); + if (intra_slice_flag) { + READ_UINT8(&br, intra_slice, 1); + READ_UINT8(&br, junk8, 7); + } + READ_UINT8(&br, extra_bit_slice, 1); + while (extra_bit_slice == 1) { + READ_UINT8(&br, junk8, 8); + READ_UINT8(&br, extra_bit_slice, 1); + } + } + + /* Fill in VASliceParameterBufferMPEG2 */ + slice_param = slice->param; + slice_param->macroblock_offset = gst_bit_reader_get_pos(&br); + slice_param->slice_horizontal_position = 0; + slice_param->slice_vertical_position = priv->mb_y; + slice_param->quantiser_scale_code = quantiser_scale_code; + slice_param->intra_slice_flag = intra_slice; + + /* Commit picture for decoding if we reached the last slice */ + if (++priv->mb_y >= priv->mb_height) { + status = decode_current_picture(decoder); + if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) + return status; + GST_DEBUG("done"); + } + return GST_VAAPI_DECODER_STATUS_SUCCESS; + +failed: + return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; +} + +static GstVaapiDecoderStatus +decode_chunks(GstVaapiDecoderMpeg2 *decoder, GstBuffer *buffer, GList *chunks) +{ + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + GstMpegVideoTypeOffsetSize *tos; + GstVaapiDecoderStatus status; + guchar * const buf = GST_BUFFER_DATA(buffer); + guchar *data; + guint data_size, ofs, pos = 0; + GList *l; + + status = GST_VAAPI_DECODER_STATUS_SUCCESS; + for (l = chunks; l; l = g_list_next(l)) { + tos = l->data; + data = buf + tos->offset; + data_size = tos->size; + if (tos->size < 0) { + if (tos->offset < 4) + return GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; + priv->sub_buffer = gst_buffer_create_sub( + buffer, + tos->offset - 4, + GST_BUFFER_SIZE(buffer) - (tos->offset - 4) + ); + break; + } + + ofs = tos->offset - pos + tos->size; + gst_vaapi_tsb_pop(priv->tsb, ofs); + pos += ofs; + + switch (tos->type) { + case GST_MPEG_VIDEO_PACKET_PICTURE: + status = decode_picture(decoder, data, data_size); + break; + case GST_MPEG_VIDEO_PACKET_SEQUENCE: + status = decode_sequence(decoder, data, data_size); + break; + case GST_MPEG_VIDEO_PACKET_EXTENSION: { + const guchar id = data[0] >> 4; + switch (id) { + case GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE: + status = decode_sequence_ext(decoder, data, data_size); + break; + case GST_MPEG_VIDEO_PACKET_EXT_QUANT_MATRIX: + status = decode_quant_matrix_ext(decoder, data, data_size); + break; + case GST_MPEG_VIDEO_PACKET_EXT_PICTURE: + status = decode_picture_ext(decoder, data, data_size); + break; + default: + // Ignore unknown extensions + GST_DEBUG("unsupported start-code extension (0x%02x)", id); + break; + } + break; + } + case GST_MPEG_VIDEO_PACKET_SEQUENCE_END: + status = decode_sequence_end(decoder); + break; + case GST_MPEG_VIDEO_PACKET_GOP: + status = decode_gop(decoder, data, data_size); + break; + case GST_MPEG_VIDEO_PACKET_USER_DATA: + // Ignore user-data packets + status = GST_VAAPI_DECODER_STATUS_SUCCESS; + break; + default: + if (tos->type >= GST_MPEG_VIDEO_PACKET_SLICE_MIN && + tos->type <= GST_MPEG_VIDEO_PACKET_SLICE_MAX) { + status = decode_slice( + decoder, + tos->type - GST_MPEG_VIDEO_PACKET_SLICE_MIN, + data, data_size + ); + break; + } + GST_DEBUG("unsupported start code (0x%02x)", tos->type); + status = GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; + break; + } + if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) + break; + } + return status; +} + +static GstVaapiDecoderStatus +decode_buffer(GstVaapiDecoderMpeg2 *decoder, GstBuffer *buffer) +{ + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + GstVaapiDecoderStatus status; + guchar *buf; + guint buf_size; + GList *chunks; + + buf = GST_BUFFER_DATA(buffer); + buf_size = GST_BUFFER_SIZE(buffer); + if (!buf && buf_size == 0) + return decode_sequence_end(decoder); + + gst_vaapi_tsb_push(priv->tsb, buffer); + + if (priv->sub_buffer) { + buffer = gst_buffer_merge(priv->sub_buffer, buffer); + if (!buffer) + return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; + gst_buffer_unref(priv->sub_buffer); + priv->sub_buffer = NULL; + } + + buf = GST_BUFFER_DATA(buffer); + buf_size = GST_BUFFER_SIZE(buffer); + chunks = gst_mpeg_video_parse(buf, buf_size, 0); + if (!chunks) + return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA; + + status = decode_chunks(decoder, buffer, chunks); + g_list_free_full(chunks, (GDestroyNotify)g_free); + return status; +} + +GstVaapiDecoderStatus +gst_vaapi_decoder_mpeg2_decode(GstVaapiDecoder *base, GstBuffer *buffer) +{ + GstVaapiDecoderMpeg2 * const decoder = GST_VAAPI_DECODER_MPEG2(base); + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + + g_return_val_if_fail(priv->is_constructed, + GST_VAAPI_DECODER_STATUS_ERROR_INIT_FAILED); + + if (!priv->is_opened) { + priv->is_opened = gst_vaapi_decoder_mpeg2_open(decoder, buffer); + if (!priv->is_opened) + return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CODEC; + } + return decode_buffer(decoder, buffer); +} + +static void +gst_vaapi_decoder_mpeg2_finalize(GObject *object) +{ + GstVaapiDecoderMpeg2 * const decoder = GST_VAAPI_DECODER_MPEG2(object); + + gst_vaapi_decoder_mpeg2_destroy(decoder); + + G_OBJECT_CLASS(gst_vaapi_decoder_mpeg2_parent_class)->finalize(object); +} + +static void +gst_vaapi_decoder_mpeg2_constructed(GObject *object) +{ + GstVaapiDecoderMpeg2 * const decoder = GST_VAAPI_DECODER_MPEG2(object); + GstVaapiDecoderMpeg2Private * const priv = decoder->priv; + GObjectClass *parent_class; + + parent_class = G_OBJECT_CLASS(gst_vaapi_decoder_mpeg2_parent_class); + if (parent_class->constructed) + parent_class->constructed(object); + + priv->is_constructed = gst_vaapi_decoder_mpeg2_create(decoder); +} + +static void +gst_vaapi_decoder_mpeg2_class_init(GstVaapiDecoderMpeg2Class *klass) +{ + GObjectClass * const object_class = G_OBJECT_CLASS(klass); + GstVaapiDecoderClass * const decoder_class = GST_VAAPI_DECODER_CLASS(klass); + + g_type_class_add_private(klass, sizeof(GstVaapiDecoderMpeg2Private)); + + object_class->finalize = gst_vaapi_decoder_mpeg2_finalize; + object_class->constructed = gst_vaapi_decoder_mpeg2_constructed; + + decoder_class->decode = gst_vaapi_decoder_mpeg2_decode; +} + +static void +gst_vaapi_decoder_mpeg2_init(GstVaapiDecoderMpeg2 *decoder) +{ + GstVaapiDecoderMpeg2Private *priv; + + priv = GST_VAAPI_DECODER_MPEG2_GET_PRIVATE(decoder); + decoder->priv = priv; + priv->width = 0; + priv->height = 0; + priv->fps_n = 0; + priv->fps_d = 0; + priv->profile = GST_VAAPI_PROFILE_MPEG2_SIMPLE; + priv->current_picture = NULL; + priv->next_picture = NULL; + priv->prev_picture = NULL; + priv->tsb = NULL; + priv->sub_buffer = NULL; + priv->mb_y = 0; + priv->mb_height = 0; + priv->seq_pts = GST_CLOCK_TIME_NONE; + priv->gop_pts = GST_CLOCK_TIME_NONE; + priv->pts_diff = 0; + priv->is_constructed = FALSE; + priv->is_opened = FALSE; + priv->is_first_field = FALSE; + priv->has_seq_ext = FALSE; + priv->has_seq_scalable_ext = FALSE; + priv->has_pic_ext = FALSE; + priv->has_quant_matrix_ext = FALSE; + priv->size_changed = FALSE; + priv->profile_changed = FALSE; + priv->quant_matrix_changed = FALSE; + priv->progressive_sequence = FALSE; +} + +/** + * gst_vaapi_decoder_mpeg2_new: + * @display: a #GstVaapiDisplay + * @caps: a #GstCaps holding codec information + * + * Creates a new #GstVaapiDecoder for MPEG-2 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_mpeg2_new(GstVaapiDisplay *display, GstCaps *caps) +{ + GstVaapiDecoderMpeg2 *decoder; + + static const GstVaapiCodecInfo codec_info = { + .pic_size = sizeof(GstVaapiPicture), + .slice_size = sizeof(GstVaapiSlice), + .pic_param_size = sizeof(VAPictureParameterBufferMPEG2), + .slice_param_size = sizeof(VASliceParameterBufferMPEG2), + .iq_matrix_size = sizeof(VAIQMatrixBufferMPEG2), + }; + + g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL); + g_return_val_if_fail(GST_IS_CAPS(caps), NULL); + + decoder = g_object_new( + GST_VAAPI_TYPE_DECODER_MPEG2, + "display", display, + "caps", caps, + "codec-info", &codec_info, + NULL + ); + if (!decoder->priv->is_constructed) { + g_object_unref(decoder); + return NULL; + } + return GST_VAAPI_DECODER_CAST(decoder); +} diff --git a/gst-libs/gst/vaapi/gstvaapidecoder_mpeg2.h b/gst-libs/gst/vaapi/gstvaapidecoder_mpeg2.h new file mode 100644 index 0000000..84a9a18 --- /dev/null +++ b/gst-libs/gst/vaapi/gstvaapidecoder_mpeg2.h @@ -0,0 +1,87 @@ +/* + * gstvaapidecoder_mpeg2.h - MPEG-2 decoder + * + * Copyright (C) 2011 Intel Corporation + * + * 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 + */ + +#ifndef GST_VAAPI_DECODER_MPEG2_H +#define GST_VAAPI_DECODER_MPEG2_H + +#include + +G_BEGIN_DECLS + +#define GST_VAAPI_TYPE_DECODER_MPEG2 \ + (gst_vaapi_decoder_mpeg2_get_type()) + +#define GST_VAAPI_DECODER_MPEG2(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GST_VAAPI_TYPE_DECODER_MPEG2, \ + GstVaapiDecoderMpeg2)) + +#define GST_VAAPI_DECODER_MPEG2_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + GST_VAAPI_TYPE_DECODER_MPEG2, \ + GstVaapiDecoderMpeg2Class)) + +#define GST_VAAPI_IS_DECODER_MPEG2(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_VAAPI_TYPE_DECODER_MPEG2)) + +#define GST_VAAPI_IS_DECODER_MPEG2_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_VAAPI_TYPE_DECODER_MPEG2)) + +#define GST_VAAPI_DECODER_MPEG2_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), \ + GST_VAAPI_TYPE_DECODER_MPEG2, \ + GstVaapiDecoderMpeg2Class)) + +typedef struct _GstVaapiDecoderMpeg2 GstVaapiDecoderMpeg2; +typedef struct _GstVaapiDecoderMpeg2Private GstVaapiDecoderMpeg2Private; +typedef struct _GstVaapiDecoderMpeg2Class GstVaapiDecoderMpeg2Class; + +/** + * GstVaapiDecoderMpeg2: + * + * A decoder based on Mpeg2. + */ +struct _GstVaapiDecoderMpeg2 { + /*< private >*/ + GstVaapiDecoder parent_instance; + + GstVaapiDecoderMpeg2Private *priv; +}; + +/** + * GstVaapiDecoderMpeg2Class: + * + * A decoder class based on Mpeg2. + */ +struct _GstVaapiDecoderMpeg2Class { + /*< private >*/ + GstVaapiDecoderClass parent_class; +}; + +GType +gst_vaapi_decoder_mpeg2_get_type(void); + +GstVaapiDecoder * +gst_vaapi_decoder_mpeg2_new(GstVaapiDisplay *display, GstCaps *caps); + +G_END_DECLS + +#endif /* GST_VAAPI_DECODER_MPEG2_H */ diff --git a/gst/vaapi/gstvaapidecode.c b/gst/vaapi/gstvaapidecode.c index d4eb118..6077502 100644 --- a/gst/vaapi/gstvaapidecode.c +++ b/gst/vaapi/gstvaapidecode.c @@ -31,6 +31,7 @@ #include "config.h" #include +#include #include #include #include @@ -49,6 +50,7 @@ # include #endif #if USE_CODEC_PARSERS +# include #endif /* Favor codecparsers-based decoders for 0.3.x series */ @@ -283,6 +285,7 @@ gst_vaapidecode_create(GstVaapiDecode *decode, GstCaps *caps) { VADisplay dpy; GstStructure *structure; + int version; if (!gst_vaapi_ensure_display(decode, &decode->display)) return FALSE; @@ -306,6 +309,12 @@ gst_vaapidecode_create(GstVaapiDecode *decode, GstCaps *caps) structure = gst_caps_get_structure(caps, 0); if (!structure) return FALSE; + if (gst_structure_has_name(structure, "video/mpeg")) { + if (!gst_structure_get_int(structure, "mpegversion", &version)) + return FALSE; + if (version == 2) + decode->decoder = gst_vaapi_decoder_mpeg2_new(dpy, caps); + } #endif } if (!decode->decoder) diff --git a/tests/test-decode.c b/tests/test-decode.c index 67b00db..0f37286 100644 --- a/tests/test-decode.c +++ b/tests/test-decode.c @@ -33,6 +33,7 @@ # include #endif #if USE_CODEC_PARSERS +# include #endif /* Set to 1 to check display cache works (shared VA display) */ @@ -160,6 +161,9 @@ main(int argc, char *argv[]) else { #if USE_CODEC_PARSERS switch (gst_vaapi_profile_get_codec(info.profile)) { + case GST_VAAPI_CODEC_MPEG2: + decoder = gst_vaapi_decoder_mpeg2_new(display, decoder_caps); + break; default: decoder = NULL; break; -- 2.7.4