/* * gstvaapidecoder_vc1.c - VC-1 decoder * * Copyright (C) 2011-2013 Intel Corporation * Author: Gwenole Beauchesne * * 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_vc1 * @short_description: VC-1 decoder */ #include "sysdeps.h" #include #include "gstvaapidecoder_vc1.h" #include "gstvaapidecoder_objects.h" #include "gstvaapidecoder_dpb.h" #include "gstvaapidecoder_unit.h" #include "gstvaapidecoder_priv.h" #include "gstvaapidisplay_priv.h" #include "gstvaapiobject_priv.h" #define DEBUG 1 #include "gstvaapidebug.h" #define GST_VAAPI_DECODER_VC1_CAST(decoder) \ ((GstVaapiDecoderVC1 *)(decoder)) typedef struct _GstVaapiDecoderVC1Private GstVaapiDecoderVC1Private; typedef struct _GstVaapiDecoderVC1Class GstVaapiDecoderVC1Class; /** * GstVaapiDecoderVC1: * * A decoder based on VC1. */ struct _GstVaapiDecoderVC1Private { GstVaapiProfile profile; guint width; guint height; GstVC1SeqHdr seq_hdr; GstVC1EntryPointHdr entrypoint_hdr; GstVC1FrameHdr frame_hdr; GstVC1BitPlanes *bitplanes; GstVaapiPicture *current_picture; GstVaapiPicture *last_non_b_picture; GstVaapiDpb *dpb; gint32 next_poc; guint8 *rbdu_buffer; guint8 rndctrl; guint rbdu_buffer_size; guint is_opened:1; guint has_codec_data:1; guint has_entrypoint:1; guint size_changed:1; guint profile_changed:1; guint closed_entry:1; guint broken_link:1; }; /** * GstVaapiDecoderVC1: * * A decoder based on VC1. */ struct _GstVaapiDecoderVC1 { /*< private > */ GstVaapiDecoder parent_instance; GstVaapiDecoderVC1Private priv; }; /** * GstVaapiDecoderVC1Class: * * A decoder class based on VC1. */ struct _GstVaapiDecoderVC1Class { /*< private > */ GstVaapiDecoderClass parent_class; }; G_DEFINE_TYPE (GstVaapiDecoderVC1, gst_vaapi_decoder_vc1, GST_TYPE_VAAPI_DECODER); static GstVaapiDecoderStatus get_status (GstVC1ParserResult result) { GstVaapiDecoderStatus status; switch (result) { case GST_VC1_PARSER_OK: status = GST_VAAPI_DECODER_STATUS_SUCCESS; break; case GST_VC1_PARSER_NO_BDU_END: status = GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA; break; case GST_VC1_PARSER_ERROR: status = GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; break; default: status = GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; break; } return status; } static void gst_vaapi_decoder_vc1_close (GstVaapiDecoderVC1 * decoder) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; gst_vaapi_picture_replace (&priv->last_non_b_picture, NULL); gst_vaapi_picture_replace (&priv->current_picture, NULL); gst_vaapi_dpb_replace (&priv->dpb, NULL); if (priv->bitplanes) { gst_vc1_bitplanes_free (priv->bitplanes); priv->bitplanes = NULL; } priv->is_opened = FALSE; } static gboolean gst_vaapi_decoder_vc1_open (GstVaapiDecoderVC1 * decoder) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; gst_vaapi_decoder_vc1_close (decoder); priv->dpb = gst_vaapi_dpb_new (2); if (!priv->dpb) return FALSE; priv->bitplanes = gst_vc1_bitplanes_new (); if (!priv->bitplanes) return FALSE; memset (&priv->seq_hdr, 0, sizeof (GstVC1SeqHdr)); memset (&priv->entrypoint_hdr, 0, sizeof (GstVC1EntryPointHdr)); memset (&priv->frame_hdr, 0, sizeof (GstVC1FrameHdr)); return TRUE; } static void gst_vaapi_decoder_vc1_destroy (GstVaapiDecoder * base_decoder) { GstVaapiDecoderVC1 *const decoder = GST_VAAPI_DECODER_VC1_CAST (base_decoder); GstVaapiDecoderVC1Private *const priv = &decoder->priv; gst_vaapi_decoder_vc1_close (decoder); if (priv->rbdu_buffer) { g_clear_pointer (&priv->rbdu_buffer, g_free); priv->rbdu_buffer_size = 0; } } static gboolean gst_vaapi_decoder_vc1_create (GstVaapiDecoder * base_decoder) { GstVaapiDecoderVC1 *const decoder = GST_VAAPI_DECODER_VC1_CAST (base_decoder); GstVaapiDecoderVC1Private *const priv = &decoder->priv; priv->has_codec_data = priv->has_entrypoint = priv->size_changed = priv->profile_changed = priv->closed_entry = priv->broken_link = FALSE; priv->profile = GST_VAAPI_PROFILE_UNKNOWN; priv->rndctrl = 0; priv->width = priv->height = 0; return TRUE; } static GstVaapiDecoderStatus gst_vaapi_decoder_vc1_reset (GstVaapiDecoder * base_decoder) { gst_vaapi_decoder_vc1_destroy (base_decoder); gst_vaapi_decoder_vc1_create (base_decoder); return GST_VAAPI_DECODER_STATUS_SUCCESS; } static GstVaapiDecoderStatus ensure_context (GstVaapiDecoderVC1 * decoder) { GstVaapiDecoderVC1Private *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_VC1_SIMPLE) profiles[n_profiles++] = GST_VAAPI_PROFILE_VC1_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 (reset_context) { GstVaapiContextInfo info; info.profile = priv->profile; info.entrypoint = entrypoint; info.chroma_type = GST_VAAPI_CHROMA_TYPE_YUV420; info.width = priv->width; info.height = priv->height; info.ref_frames = 2; reset_context = gst_vaapi_decoder_ensure_context (GST_VAAPI_DECODER (decoder), &info); if (!reset_context) return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; } return GST_VAAPI_DECODER_STATUS_SUCCESS; } static GstVaapiDecoderStatus decode_current_picture (GstVaapiDecoderVC1 * decoder) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVaapiPicture *const picture = priv->current_picture; if (!picture) return GST_VAAPI_DECODER_STATUS_SUCCESS; if (!gst_vaapi_picture_decode (picture)) goto error; if (GST_VAAPI_PICTURE_IS_COMPLETE (picture)) { if (!gst_vaapi_dpb_add (priv->dpb, picture)) goto error; gst_vaapi_picture_replace (&priv->current_picture, NULL); } return GST_VAAPI_DECODER_STATUS_SUCCESS; /* ERRORS */ error: { /* XXX: fix for cases where first field failed to be decoded */ gst_vaapi_picture_replace (&priv->current_picture, NULL); return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; } } static GstVaapiDecoderStatus decode_sequence (GstVaapiDecoderVC1 * decoder, GstVC1BDU * rbdu, GstVC1BDU * ebdu) { GstVaapiDecoder *const base_decoder = GST_VAAPI_DECODER (decoder); GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVC1SeqHdr *const seq_hdr = &priv->seq_hdr; GstVC1AdvancedSeqHdr *const adv_hdr = &seq_hdr->advanced; GstVC1SeqStructC *const structc = &seq_hdr->struct_c; GstVC1ParserResult result; GstVaapiProfile profile; guint width, height, fps_n, fps_d, par_n, par_d; result = gst_vc1_parse_sequence_header (rbdu->data + rbdu->offset, rbdu->size, seq_hdr); if (result != GST_VC1_PARSER_OK) { GST_ERROR ("failed to parse sequence layer"); return get_status (result); } priv->has_entrypoint = FALSE; /* Reset POC */ if (priv->last_non_b_picture) { if (priv->last_non_b_picture->poc == priv->next_poc) priv->next_poc++; gst_vaapi_picture_replace (&priv->last_non_b_picture, NULL); } /* Validate profile */ switch (seq_hdr->profile) { case GST_VC1_PROFILE_SIMPLE: case GST_VC1_PROFILE_MAIN: case GST_VC1_PROFILE_ADVANCED: break; default: GST_ERROR ("unsupported profile %d", seq_hdr->profile); return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE; } fps_n = 0; fps_d = 0; par_n = 0; par_d = 0; switch (seq_hdr->profile) { case GST_VC1_PROFILE_SIMPLE: case GST_VC1_PROFILE_MAIN: if (structc->wmvp) { fps_n = structc->framerate; fps_d = 1; } break; case GST_VC1_PROFILE_ADVANCED: fps_n = adv_hdr->fps_n; fps_d = adv_hdr->fps_d; par_n = adv_hdr->par_n; par_d = adv_hdr->par_d; break; default: g_assert (0 && "XXX: we already validated the profile above"); break; } if (fps_n && fps_d) gst_vaapi_decoder_set_framerate (base_decoder, fps_n, fps_d); if (par_n > 0 && par_d > 0) gst_vaapi_decoder_set_pixel_aspect_ratio (base_decoder, par_n, par_d); width = 0; height = 0; switch (seq_hdr->profile) { case GST_VC1_PROFILE_SIMPLE: case GST_VC1_PROFILE_MAIN: width = seq_hdr->struct_c.coded_width; height = seq_hdr->struct_c.coded_height; break; case GST_VC1_PROFILE_ADVANCED: width = seq_hdr->advanced.max_coded_width; height = seq_hdr->advanced.max_coded_height; break; default: g_assert (0 && "XXX: we already validated the profile above"); break; } if (priv->width != width) { priv->width = width; priv->size_changed = TRUE; } if (priv->height != height) { priv->height = height; priv->size_changed = TRUE; } profile = GST_VAAPI_PROFILE_UNKNOWN; switch (seq_hdr->profile) { case GST_VC1_PROFILE_SIMPLE: profile = GST_VAAPI_PROFILE_VC1_SIMPLE; break; case GST_VC1_PROFILE_MAIN: profile = GST_VAAPI_PROFILE_VC1_MAIN; break; case GST_VC1_PROFILE_ADVANCED: profile = GST_VAAPI_PROFILE_VC1_ADVANCED; break; default: g_assert (0 && "XXX: we already validated the profile above"); break; } if (priv->profile != profile) { priv->profile = profile; priv->profile_changed = TRUE; } return GST_VAAPI_DECODER_STATUS_SUCCESS; } static GstVaapiDecoderStatus decode_sequence_end (GstVaapiDecoderVC1 * decoder) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVaapiDecoderStatus status; status = decode_current_picture (decoder); if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) return status; gst_vaapi_dpb_flush (priv->dpb); return GST_VAAPI_DECODER_STATUS_SUCCESS; } static GstVaapiDecoderStatus decode_entry_point (GstVaapiDecoderVC1 * decoder, GstVC1BDU * rbdu, GstVC1BDU * ebdu) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVC1SeqHdr *const seq_hdr = &priv->seq_hdr; GstVC1EntryPointHdr *const entrypoint_hdr = &priv->entrypoint_hdr; GstVC1ParserResult result; result = gst_vc1_parse_entry_point_header (rbdu->data + rbdu->offset, rbdu->size, entrypoint_hdr, seq_hdr); if (result != GST_VC1_PARSER_OK) { GST_ERROR ("failed to parse entrypoint layer"); return get_status (result); } if (entrypoint_hdr->coded_size_flag) { priv->width = entrypoint_hdr->coded_width; priv->height = entrypoint_hdr->coded_height; priv->size_changed = TRUE; } priv->has_entrypoint = TRUE; priv->closed_entry = entrypoint_hdr->closed_entry; priv->broken_link = entrypoint_hdr->broken_link; return GST_VAAPI_DECODER_STATUS_SUCCESS; } /* Reconstruct bitstream PTYPE (7.1.1.4, index into Table-35) */ static guint get_PTYPE (guint ptype) { switch (ptype) { case GST_VC1_PICTURE_TYPE_I: return 0; case GST_VC1_PICTURE_TYPE_P: return 1; case GST_VC1_PICTURE_TYPE_B: return 2; case GST_VC1_PICTURE_TYPE_BI: return 3; } return 4; /* skipped P-frame */ } /* Reconstruct bitstream BFRACTION (7.1.1.14, index into Table-40) */ static guint get_BFRACTION (guint bfraction) { guint i; static const struct { guint16 index; guint16 value; } bfraction_map[] = { { 0, GST_VC1_BFRACTION_BASIS / 2}, { 1, GST_VC1_BFRACTION_BASIS / 3}, { 2, (GST_VC1_BFRACTION_BASIS * 2) / 3}, { 3, GST_VC1_BFRACTION_BASIS / 4}, { 4, (GST_VC1_BFRACTION_BASIS * 3) / 4}, { 5, GST_VC1_BFRACTION_BASIS / 5}, { 6, (GST_VC1_BFRACTION_BASIS * 2) / 5}, { 7, (GST_VC1_BFRACTION_BASIS * 3) / 5}, { 8, (GST_VC1_BFRACTION_BASIS * 4) / 5}, { 9, GST_VC1_BFRACTION_BASIS / 6}, { 10, (GST_VC1_BFRACTION_BASIS * 5) / 6}, { 11, GST_VC1_BFRACTION_BASIS / 7}, { 12, (GST_VC1_BFRACTION_BASIS * 2) / 7}, { 13, (GST_VC1_BFRACTION_BASIS * 3) / 7}, { 14, (GST_VC1_BFRACTION_BASIS * 4) / 7}, { 15, (GST_VC1_BFRACTION_BASIS * 5) / 7}, { 16, (GST_VC1_BFRACTION_BASIS * 6) / 7}, { 17, GST_VC1_BFRACTION_BASIS / 8}, { 18, (GST_VC1_BFRACTION_BASIS * 3) / 8}, { 19, (GST_VC1_BFRACTION_BASIS * 5) / 8}, { 20, (GST_VC1_BFRACTION_BASIS * 7) / 8}, { 21, GST_VC1_BFRACTION_RESERVED}, { 22, GST_VC1_BFRACTION_PTYPE_BI} }; if (!bfraction) return 0; for (i = 0; i < G_N_ELEMENTS (bfraction_map); i++) { if (bfraction_map[i].value == bfraction) return bfraction_map[i].index; } return 21; /* RESERVED */ } /* Translate GStreamer MV modes to VA-API */ static guint get_VAMvModeVC1 (guint mvmode) { switch (mvmode) { case GST_VC1_MVMODE_1MV_HPEL_BILINEAR: return VAMvMode1MvHalfPelBilinear; case GST_VC1_MVMODE_1MV: return VAMvMode1Mv; case GST_VC1_MVMODE_1MV_HPEL: return VAMvMode1MvHalfPel; case GST_VC1_MVMODE_MIXED_MV: return VAMvModeMixedMv; case GST_VC1_MVMODE_INTENSITY_COMP: return VAMvModeIntensityCompensation; } return 0; } /* Reconstruct bitstream MVMODE (7.1.1.32) */ static guint get_MVMODE (GstVC1FrameHdr * frame_hdr) { guint mvmode; if (frame_hdr->profile == GST_VC1_PROFILE_ADVANCED) mvmode = frame_hdr->pic.advanced.mvmode; else mvmode = frame_hdr->pic.simple.mvmode; if (frame_hdr->ptype == GST_VC1_PICTURE_TYPE_P || frame_hdr->ptype == GST_VC1_PICTURE_TYPE_B) return get_VAMvModeVC1 (mvmode); return 0; } /* Reconstruct bitstream MVMODE2 (7.1.1.33) */ static guint get_MVMODE2 (GstVC1FrameHdr * frame_hdr) { guint mvmode, mvmode2; if (frame_hdr->profile == GST_VC1_PROFILE_ADVANCED) { mvmode = frame_hdr->pic.advanced.mvmode; mvmode2 = frame_hdr->pic.advanced.mvmode2; } else { mvmode = frame_hdr->pic.simple.mvmode; mvmode2 = frame_hdr->pic.simple.mvmode2; } if (frame_hdr->ptype == GST_VC1_PICTURE_TYPE_P && mvmode == GST_VC1_MVMODE_INTENSITY_COMP) return get_VAMvModeVC1 (mvmode2); return 0; } static inline int has_MVTYPEMB_bitplane (GstVaapiDecoderVC1 * decoder) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVC1SeqHdr *const seq_hdr = &priv->seq_hdr; GstVC1FrameHdr *const frame_hdr = &priv->frame_hdr; guint mvmode, mvmode2; if (seq_hdr->profile == GST_VC1_PROFILE_ADVANCED) { GstVC1PicAdvanced *const pic = &frame_hdr->pic.advanced; if (pic->mvtypemb) return 0; mvmode = pic->mvmode; mvmode2 = pic->mvmode2; } else { GstVC1PicSimpleMain *const pic = &frame_hdr->pic.simple; if (pic->mvtypemb) return 0; mvmode = pic->mvmode; mvmode2 = pic->mvmode2; } return (frame_hdr->ptype == GST_VC1_PICTURE_TYPE_P && (mvmode == GST_VC1_MVMODE_MIXED_MV || (mvmode == GST_VC1_MVMODE_INTENSITY_COMP && mvmode2 == GST_VC1_MVMODE_MIXED_MV))); } static inline int has_SKIPMB_bitplane (GstVaapiDecoderVC1 * decoder) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVC1SeqHdr *const seq_hdr = &priv->seq_hdr; GstVC1FrameHdr *const frame_hdr = &priv->frame_hdr; if (seq_hdr->profile == GST_VC1_PROFILE_ADVANCED) { GstVC1PicAdvanced *const pic = &frame_hdr->pic.advanced; if (pic->skipmb) return 0; } else { GstVC1PicSimpleMain *const pic = &frame_hdr->pic.simple; if (pic->skipmb) return 0; } return (frame_hdr->ptype == GST_VC1_PICTURE_TYPE_P || frame_hdr->ptype == GST_VC1_PICTURE_TYPE_B); } static inline int has_DIRECTMB_bitplane (GstVaapiDecoderVC1 * decoder) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVC1SeqHdr *const seq_hdr = &priv->seq_hdr; GstVC1FrameHdr *const frame_hdr = &priv->frame_hdr; if (seq_hdr->profile == GST_VC1_PROFILE_ADVANCED) { GstVC1PicAdvanced *const pic = &frame_hdr->pic.advanced; if (pic->directmb) return 0; } else { GstVC1PicSimpleMain *const pic = &frame_hdr->pic.simple; if (pic->directmb) return 0; } return frame_hdr->ptype == GST_VC1_PICTURE_TYPE_B; } static inline int has_ACPRED_bitplane (GstVaapiDecoderVC1 * decoder) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVC1SeqHdr *const seq_hdr = &priv->seq_hdr; GstVC1FrameHdr *const frame_hdr = &priv->frame_hdr; GstVC1PicAdvanced *const pic = &frame_hdr->pic.advanced; if (seq_hdr->profile != GST_VC1_PROFILE_ADVANCED) return 0; if (pic->acpred) return 0; return (frame_hdr->ptype == GST_VC1_PICTURE_TYPE_I || frame_hdr->ptype == GST_VC1_PICTURE_TYPE_BI); } static inline int has_OVERFLAGS_bitplane (GstVaapiDecoderVC1 * decoder) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVC1SeqHdr *const seq_hdr = &priv->seq_hdr; GstVC1EntryPointHdr *const entrypoint_hdr = &priv->entrypoint_hdr; GstVC1FrameHdr *const frame_hdr = &priv->frame_hdr; GstVC1PicAdvanced *const pic = &frame_hdr->pic.advanced; if (seq_hdr->profile != GST_VC1_PROFILE_ADVANCED) return 0; if (pic->overflags) return 0; return ((frame_hdr->ptype == GST_VC1_PICTURE_TYPE_I || frame_hdr->ptype == GST_VC1_PICTURE_TYPE_BI) && (entrypoint_hdr->overlap && frame_hdr->pquant <= 8) && pic->condover == GST_VC1_CONDOVER_SELECT); } static inline void pack_bitplanes (GstVaapiBitPlane * bitplane, guint n, const guint8 * bitplanes[3], guint x, guint y, guint stride) { const guint dst_index = n / 2; const guint src_index = y * stride + x; guint8 v = 0; if (bitplanes[0]) v |= bitplanes[0][src_index]; if (bitplanes[1]) v |= bitplanes[1][src_index] << 1; if (bitplanes[2]) v |= bitplanes[2][src_index] << 2; bitplane->data[dst_index] = (bitplane->data[dst_index] << 4) | v; } static gboolean fill_picture_structc (GstVaapiDecoderVC1 * decoder, GstVaapiPicture * picture) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; VAPictureParameterBufferVC1 *const pic_param = picture->param; GstVC1SeqStructC *const structc = &priv->seq_hdr.struct_c; GstVC1FrameHdr *const frame_hdr = &priv->frame_hdr; GstVC1PicSimpleMain *const pic = &frame_hdr->pic.simple; /* Fill in VAPictureParameterBufferVC1 (simple/main profile bits) */ pic_param->sequence_fields.bits.finterpflag = structc->finterpflag; pic_param->sequence_fields.bits.multires = structc->multires; pic_param->sequence_fields.bits.overlap = structc->overlap; pic_param->sequence_fields.bits.syncmarker = structc->syncmarker; pic_param->sequence_fields.bits.rangered = structc->rangered; pic_param->sequence_fields.bits.max_b_frames = structc->maxbframes; pic_param->conditional_overlap_flag = 0; /* advanced profile only */ pic_param->fast_uvmc_flag = structc->fastuvmc; pic_param->b_picture_fraction = get_BFRACTION (pic->bfraction); pic_param->cbp_table = pic->cbptab; pic_param->mb_mode_table = 0; /* XXX: interlaced frame */ pic_param->range_reduction_frame = pic->rangeredfrm; pic_param->post_processing = 0; /* advanced profile only */ pic_param->picture_resolution_index = pic->respic; pic_param->luma_scale = pic->lumscale; pic_param->luma_shift = pic->lumshift; pic_param->raw_coding.flags.mv_type_mb = pic->mvtypemb; pic_param->raw_coding.flags.direct_mb = pic->directmb; pic_param->raw_coding.flags.skip_mb = pic->skipmb; pic_param->bitplane_present.flags.bp_mv_type_mb = has_MVTYPEMB_bitplane (decoder); pic_param->bitplane_present.flags.bp_direct_mb = has_DIRECTMB_bitplane (decoder); pic_param->bitplane_present.flags.bp_skip_mb = has_SKIPMB_bitplane (decoder); pic_param->mv_fields.bits.mv_table = pic->mvtab; pic_param->mv_fields.bits.extended_mv_flag = structc->extended_mv; pic_param->mv_fields.bits.extended_mv_range = pic->mvrange; pic_param->transform_fields.bits.variable_sized_transform_flag = structc->vstransform; pic_param->transform_fields.bits.mb_level_transform_type_flag = pic->ttmbf; pic_param->transform_fields.bits.frame_level_transform_type = pic->ttfrm; pic_param->transform_fields.bits.transform_ac_codingset_idx2 = pic->transacfrm2; /* Refer to 8.3.7 Rounding control for Simple and Main Profile */ if (frame_hdr->ptype == GST_VC1_PICTURE_TYPE_I || frame_hdr->ptype == GST_VC1_PICTURE_TYPE_BI) priv->rndctrl = 1; else if (frame_hdr->ptype == GST_VC1_PICTURE_TYPE_P) priv->rndctrl ^= 1; pic_param->rounding_control = priv->rndctrl; return TRUE; } static gboolean fill_picture_advanced (GstVaapiDecoderVC1 * decoder, GstVaapiPicture * picture) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; VAPictureParameterBufferVC1 *const pic_param = picture->param; GstVC1AdvancedSeqHdr *const adv_hdr = &priv->seq_hdr.advanced; GstVC1EntryPointHdr *const entrypoint_hdr = &priv->entrypoint_hdr; GstVC1FrameHdr *const frame_hdr = &priv->frame_hdr; GstVC1PicAdvanced *const pic = &frame_hdr->pic.advanced; if (!priv->has_entrypoint) return FALSE; /* Fill in VAPictureParameterBufferVC1 (advanced profile bits) */ pic_param->sequence_fields.bits.pulldown = adv_hdr->pulldown; pic_param->sequence_fields.bits.interlace = adv_hdr->interlace; pic_param->sequence_fields.bits.tfcntrflag = adv_hdr->tfcntrflag; pic_param->sequence_fields.bits.finterpflag = adv_hdr->finterpflag; pic_param->sequence_fields.bits.psf = adv_hdr->psf; pic_param->sequence_fields.bits.overlap = entrypoint_hdr->overlap; pic_param->entrypoint_fields.bits.broken_link = entrypoint_hdr->broken_link; pic_param->entrypoint_fields.bits.closed_entry = entrypoint_hdr->closed_entry; pic_param->entrypoint_fields.bits.panscan_flag = entrypoint_hdr->panscan_flag; pic_param->entrypoint_fields.bits.loopfilter = entrypoint_hdr->loopfilter; pic_param->conditional_overlap_flag = pic->condover; pic_param->fast_uvmc_flag = entrypoint_hdr->fastuvmc; pic_param->range_mapping_fields.bits.luma_flag = entrypoint_hdr->range_mapy_flag; pic_param->range_mapping_fields.bits.luma = entrypoint_hdr->range_mapy; pic_param->range_mapping_fields.bits.chroma_flag = entrypoint_hdr->range_mapuv_flag; pic_param->range_mapping_fields.bits.chroma = entrypoint_hdr->range_mapuv; pic_param->b_picture_fraction = get_BFRACTION (pic->bfraction); pic_param->cbp_table = pic->cbptab; pic_param->mb_mode_table = 0; /* XXX: interlaced frame */ pic_param->range_reduction_frame = 0; /* simple/main profile only */ pic_param->rounding_control = pic->rndctrl; pic_param->post_processing = pic->postproc; pic_param->picture_resolution_index = 0; /* simple/main profile only */ pic_param->luma_scale = pic->lumscale; pic_param->luma_shift = pic->lumshift; pic_param->picture_fields.bits.frame_coding_mode = pic->fcm; pic_param->picture_fields.bits.top_field_first = pic->tff; pic_param->picture_fields.bits.is_first_field = pic->fcm == 0; /* XXX: interlaced frame */ pic_param->picture_fields.bits.intensity_compensation = pic->mvmode == GST_VC1_MVMODE_INTENSITY_COMP; pic_param->raw_coding.flags.mv_type_mb = pic->mvtypemb; pic_param->raw_coding.flags.direct_mb = pic->directmb; pic_param->raw_coding.flags.skip_mb = pic->skipmb; pic_param->raw_coding.flags.ac_pred = pic->acpred; pic_param->raw_coding.flags.overflags = pic->overflags; pic_param->bitplane_present.flags.bp_mv_type_mb = has_MVTYPEMB_bitplane (decoder); pic_param->bitplane_present.flags.bp_direct_mb = has_DIRECTMB_bitplane (decoder); pic_param->bitplane_present.flags.bp_skip_mb = has_SKIPMB_bitplane (decoder); pic_param->bitplane_present.flags.bp_ac_pred = has_ACPRED_bitplane (decoder); pic_param->bitplane_present.flags.bp_overflags = has_OVERFLAGS_bitplane (decoder); pic_param->reference_fields.bits.reference_distance_flag = entrypoint_hdr->refdist_flag; pic_param->mv_fields.bits.mv_table = pic->mvtab; pic_param->mv_fields.bits.extended_mv_flag = entrypoint_hdr->extended_mv; pic_param->mv_fields.bits.extended_mv_range = pic->mvrange; pic_param->mv_fields.bits.extended_dmv_flag = entrypoint_hdr->extended_dmv; pic_param->pic_quantizer_fields.bits.dquant = entrypoint_hdr->dquant; pic_param->pic_quantizer_fields.bits.quantizer = entrypoint_hdr->quantizer; pic_param->transform_fields.bits.variable_sized_transform_flag = entrypoint_hdr->vstransform; pic_param->transform_fields.bits.mb_level_transform_type_flag = pic->ttmbf; pic_param->transform_fields.bits.frame_level_transform_type = pic->ttfrm; pic_param->transform_fields.bits.transform_ac_codingset_idx2 = pic->transacfrm2; return TRUE; } static gboolean fill_picture (GstVaapiDecoderVC1 * decoder, GstVaapiPicture * picture) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; VAPictureParameterBufferVC1 *const pic_param = picture->param; GstVC1SeqHdr *const seq_hdr = &priv->seq_hdr; GstVC1FrameHdr *const frame_hdr = &priv->frame_hdr; GstVC1VopDquant *const vopdquant = &frame_hdr->vopdquant; GstVaapiPicture *prev_picture, *next_picture; /* Fill in VAPictureParameterBufferVC1 (common fields) */ pic_param->forward_reference_picture = VA_INVALID_ID; pic_param->backward_reference_picture = VA_INVALID_ID; pic_param->inloop_decoded_picture = VA_INVALID_ID; pic_param->sequence_fields.value = 0; #if VA_CHECK_VERSION(0,32,0) pic_param->sequence_fields.bits.profile = seq_hdr->profile; #endif pic_param->coded_width = priv->width; pic_param->coded_height = priv->height; pic_param->entrypoint_fields.value = 0; pic_param->range_mapping_fields.value = 0; pic_param->picture_fields.value = 0; pic_param->picture_fields.bits.picture_type = get_PTYPE (frame_hdr->ptype); pic_param->raw_coding.value = 0; pic_param->bitplane_present.value = 0; pic_param->reference_fields.value = 0; pic_param->mv_fields.value = 0; pic_param->mv_fields.bits.mv_mode = get_MVMODE (frame_hdr); pic_param->mv_fields.bits.mv_mode2 = get_MVMODE2 (frame_hdr); pic_param->pic_quantizer_fields.value = 0; pic_param->pic_quantizer_fields.bits.half_qp = frame_hdr->halfqp; pic_param->pic_quantizer_fields.bits.pic_quantizer_scale = frame_hdr->pquant; pic_param->pic_quantizer_fields.bits.pic_quantizer_type = frame_hdr->pquantizer; pic_param->pic_quantizer_fields.bits.dq_frame = vopdquant->dquantfrm; pic_param->pic_quantizer_fields.bits.dq_profile = vopdquant->dqprofile; pic_param->pic_quantizer_fields.bits.dq_sb_edge = vopdquant->dqprofile == GST_VC1_DQPROFILE_SINGLE_EDGE ? vopdquant->dqbedge : 0; pic_param->pic_quantizer_fields.bits.dq_db_edge = vopdquant->dqprofile == GST_VC1_DQPROFILE_DOUBLE_EDGES ? vopdquant->dqbedge : 0; pic_param->pic_quantizer_fields.bits.dq_binary_level = vopdquant->dqbilevel; pic_param->pic_quantizer_fields.bits.alt_pic_quantizer = vopdquant->altpquant; pic_param->transform_fields.value = 0; pic_param->transform_fields.bits.transform_ac_codingset_idx1 = frame_hdr->transacfrm; pic_param->transform_fields.bits.intra_transform_dc_table = frame_hdr->transdctab; if (seq_hdr->profile == GST_VC1_PROFILE_ADVANCED) { if (!fill_picture_advanced (decoder, picture)) return FALSE; } else { if (!fill_picture_structc (decoder, picture)) return FALSE; } gst_vaapi_dpb_get_neighbours (priv->dpb, picture, &prev_picture, &next_picture); switch (picture->type) { case GST_VAAPI_PICTURE_TYPE_B: if (next_picture) pic_param->backward_reference_picture = next_picture->surface_id; if (prev_picture) pic_param->forward_reference_picture = prev_picture->surface_id; else if (!priv->closed_entry) GST_VAAPI_PICTURE_FLAG_SET (picture, GST_VAAPI_PICTURE_FLAG_SKIPPED); break; case GST_VAAPI_PICTURE_TYPE_P: if (prev_picture) pic_param->forward_reference_picture = prev_picture->surface_id; break; default: break; } if (pic_param->bitplane_present.value) { const guint8 *bitplanes[3]; guint x, y, n; switch (picture->type) { case GST_VAAPI_PICTURE_TYPE_P: bitplanes[0] = pic_param->bitplane_present.flags.bp_direct_mb ? priv->bitplanes->directmb : NULL; bitplanes[1] = pic_param->bitplane_present.flags.bp_skip_mb ? priv->bitplanes->skipmb : NULL; bitplanes[2] = pic_param->bitplane_present.flags.bp_mv_type_mb ? priv->bitplanes->mvtypemb : NULL; break; case GST_VAAPI_PICTURE_TYPE_B: bitplanes[0] = pic_param->bitplane_present.flags.bp_direct_mb ? priv->bitplanes->directmb : NULL; bitplanes[1] = pic_param->bitplane_present.flags.bp_skip_mb ? priv->bitplanes->skipmb : NULL; bitplanes[2] = NULL; /* XXX: interlaced frame (FORWARD plane) */ break; case GST_VAAPI_PICTURE_TYPE_BI: case GST_VAAPI_PICTURE_TYPE_I: bitplanes[0] = NULL; /* XXX: interlaced frame (FIELDTX plane) */ bitplanes[1] = pic_param->bitplane_present.flags.bp_ac_pred ? priv->bitplanes->acpred : NULL; bitplanes[2] = pic_param->bitplane_present.flags.bp_overflags ? priv->bitplanes->overflags : NULL; break; default: bitplanes[0] = NULL; bitplanes[1] = NULL; bitplanes[2] = NULL; break; } picture->bitplane = GST_VAAPI_BITPLANE_NEW (decoder, (seq_hdr->mb_width * seq_hdr->mb_height + 1) / 2); if (!picture->bitplane) return FALSE; n = 0; for (y = 0; y < seq_hdr->mb_height; y++) for (x = 0; x < seq_hdr->mb_width; x++, n++) pack_bitplanes (picture->bitplane, n, bitplanes, x, y, seq_hdr->mb_stride); if (n & 1) /* move last nibble to the high order */ picture->bitplane->data[n / 2] <<= 4; } return TRUE; } static GstVaapiDecoderStatus decode_slice_chunk (GstVaapiDecoderVC1 * decoder, GstVC1BDU * ebdu, guint slice_addr, guint header_size) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVaapiPicture *const picture = priv->current_picture; GstVaapiSlice *slice; VASliceParameterBufferVC1 *slice_param; slice = GST_VAAPI_SLICE_NEW (VC1, decoder, ebdu->data + ebdu->sc_offset, ebdu->size + ebdu->offset - ebdu->sc_offset); if (!slice) { GST_ERROR ("failed to allocate slice"); return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; } gst_vaapi_picture_add_slice (picture, slice); /* Fill in VASliceParameterBufferVC1 */ slice_param = slice->param; slice_param->macroblock_offset = 8 * (ebdu->offset - ebdu->sc_offset) + header_size; slice_param->slice_vertical_position = slice_addr; return GST_VAAPI_DECODER_STATUS_SUCCESS; } static GstVaapiDecoderStatus decode_frame (GstVaapiDecoderVC1 * decoder, GstVC1BDU * rbdu, GstVC1BDU * ebdu) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVC1FrameHdr *const frame_hdr = &priv->frame_hdr; GstVC1ParserResult result; GstVaapiPicture *const picture = priv->current_picture; memset (frame_hdr, 0, sizeof (*frame_hdr)); result = gst_vc1_parse_frame_header (rbdu->data + rbdu->offset, rbdu->size, frame_hdr, &priv->seq_hdr, priv->bitplanes); if (result != GST_VC1_PARSER_OK) { GST_ERROR ("failed to parse frame layer"); return get_status (result); } /* @FIXME: intel-driver cannot handle interlaced frames */ if (priv->profile == GST_VAAPI_PROFILE_VC1_ADVANCED && frame_hdr->pic.advanced.fcm != GST_VC1_FRAME_PROGRESSIVE) { GST_ERROR ("interlaced video not supported"); return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE; } switch (frame_hdr->ptype) { case GST_VC1_PICTURE_TYPE_I: picture->type = GST_VAAPI_PICTURE_TYPE_I; GST_VAAPI_PICTURE_FLAG_SET (picture, GST_VAAPI_PICTURE_FLAG_REFERENCE); break; case GST_VC1_PICTURE_TYPE_SKIPPED: case GST_VC1_PICTURE_TYPE_P: picture->type = GST_VAAPI_PICTURE_TYPE_P; GST_VAAPI_PICTURE_FLAG_SET (picture, GST_VAAPI_PICTURE_FLAG_REFERENCE); break; case GST_VC1_PICTURE_TYPE_B: picture->type = GST_VAAPI_PICTURE_TYPE_B; break; case GST_VC1_PICTURE_TYPE_BI: picture->type = GST_VAAPI_PICTURE_TYPE_BI; break; default: GST_ERROR ("unsupported picture type %d", frame_hdr->ptype); return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; } /* Update presentation time */ if (GST_VAAPI_PICTURE_IS_REFERENCE (picture)) { picture->poc = priv->last_non_b_picture ? (priv->last_non_b_picture->poc + 1) : priv->next_poc; priv->next_poc = picture->poc + 1; gst_vaapi_picture_replace (&priv->last_non_b_picture, picture); } else if (!priv->last_non_b_picture) picture->poc = priv->next_poc++; else { /* B or BI */ picture->poc = priv->last_non_b_picture->poc++; priv->next_poc = priv->last_non_b_picture->poc + 1; } picture->pts = GST_VAAPI_DECODER_CODEC_FRAME (decoder)->pts; if (!fill_picture (decoder, picture)) return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; return decode_slice_chunk (decoder, ebdu, 0, frame_hdr->header_size); } static GstVaapiDecoderStatus decode_slice (GstVaapiDecoderVC1 * decoder, GstVC1BDU * rbdu, GstVC1BDU * ebdu) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVC1SliceHdr slice_hdr; GstVC1ParserResult result; memset (&slice_hdr, 0, sizeof (slice_hdr)); result = gst_vc1_parse_slice_header (rbdu->data + rbdu->offset, rbdu->size, &slice_hdr, &priv->seq_hdr); if (result != GST_VC1_PARSER_OK) { GST_ERROR ("failed to parse slice layer"); return get_status (result); } return decode_slice_chunk (decoder, ebdu, slice_hdr.slice_addr, slice_hdr.header_size); } static gboolean decode_rbdu (GstVaapiDecoderVC1 * decoder, GstVC1BDU * rbdu, GstVC1BDU * ebdu) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; guint8 *rbdu_buffer; guint i, j, rbdu_buffer_size; /* BDU are encapsulated in advanced profile mode only */ if (priv->profile != GST_VAAPI_PROFILE_VC1_ADVANCED) { memcpy (rbdu, ebdu, sizeof (*rbdu)); return TRUE; } /* Reallocate unescaped bitstream buffer */ rbdu_buffer = priv->rbdu_buffer; if (!rbdu_buffer || ebdu->size > priv->rbdu_buffer_size) { rbdu_buffer = g_realloc (priv->rbdu_buffer, ebdu->size); if (!rbdu_buffer) return FALSE; priv->rbdu_buffer = rbdu_buffer; priv->rbdu_buffer_size = ebdu->size; } /* Unescape bitstream buffer */ if (ebdu->size < 4) { memcpy (rbdu_buffer, ebdu->data + ebdu->offset, ebdu->size); rbdu_buffer_size = ebdu->size; } else { guint8 *const bdu_buffer = ebdu->data + ebdu->offset; for (i = 0, j = 0; i < ebdu->size; i++) { if (i >= 2 && i < ebdu->size - 1 && bdu_buffer[i - 1] == 0x00 && bdu_buffer[i - 2] == 0x00 && bdu_buffer[i] == 0x03 && bdu_buffer[i + 1] <= 0x03) i++; rbdu_buffer[j++] = bdu_buffer[i]; } rbdu_buffer_size = j; } /* Reconstruct RBDU */ rbdu->type = ebdu->type; rbdu->size = rbdu_buffer_size; rbdu->sc_offset = 0; rbdu->offset = 0; rbdu->data = rbdu_buffer; return TRUE; } static GstVaapiDecoderStatus decode_ebdu (GstVaapiDecoderVC1 * decoder, GstVC1BDU * ebdu) { GstVaapiDecoderStatus status; GstVC1BDU rbdu; if (!decode_rbdu (decoder, &rbdu, ebdu)) return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; switch (ebdu->type) { case GST_VC1_SEQUENCE: status = decode_sequence (decoder, &rbdu, ebdu); break; case GST_VC1_ENTRYPOINT: status = decode_entry_point (decoder, &rbdu, ebdu); break; case GST_VC1_FRAME: status = decode_frame (decoder, &rbdu, ebdu); break; case GST_VC1_SLICE: status = decode_slice (decoder, &rbdu, ebdu); break; case GST_VC1_END_OF_SEQ: status = decode_sequence_end (decoder); break; case GST_VC1_FIELD_USER: case GST_VC1_FRAME_USER: case GST_VC1_ENTRY_POINT_USER: case GST_VC1_SEQUENCE_USER: /* Let's just ignore them */ status = GST_VAAPI_DECODER_STATUS_SUCCESS; break; default: GST_WARNING ("unsupported BDU type %d", ebdu->type); status = GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; break; } return status; } static GstVaapiDecoderStatus decode_buffer (GstVaapiDecoderVC1 * decoder, guchar * buf, guint buf_size) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVC1BDU ebdu; if (priv->has_codec_data) { ebdu.type = GST_VC1_FRAME; ebdu.sc_offset = 0; ebdu.offset = 0; } else { ebdu.type = buf[3]; ebdu.sc_offset = 0; ebdu.offset = 4; } ebdu.data = buf; ebdu.size = buf_size - ebdu.offset; return decode_ebdu (decoder, &ebdu); } static GstVaapiDecoderStatus gst_vaapi_decoder_vc1_decode_codec_data (GstVaapiDecoder * base_decoder, const guchar * buf, guint buf_size) { GstVaapiDecoderVC1 *const decoder = GST_VAAPI_DECODER_VC1_CAST (base_decoder); GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVC1SeqHdr *const seq_hdr = &priv->seq_hdr; GstVaapiDecoderStatus status; GstVC1ParserResult result; GstVC1BDU ebdu; GstCaps *caps; GstStructure *structure; guint ofs; gint width, height; guint32 format; gint version; const gchar *s; priv->has_codec_data = TRUE; width = GST_VAAPI_DECODER_WIDTH (decoder); height = GST_VAAPI_DECODER_HEIGHT (decoder); if (!width || !height) { GST_ERROR ("failed to parse size from codec-data"); return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; } caps = GST_VAAPI_DECODER_CODEC_STATE (decoder)->caps; structure = gst_caps_get_structure (caps, 0); s = gst_structure_get_string (structure, "format"); if (s && strlen (s) == 4) { format = GST_MAKE_FOURCC (s[0], s[1], s[2], s[3]); } else { /* Try to determine format from "wmvversion" property */ if (gst_structure_get_int (structure, "wmvversion", &version)) format = (version >= 1 && version <= 3) ? GST_MAKE_FOURCC ('W', 'M', 'V', ('0' + version)) : 0; else format = 0; } if (!format) { GST_ERROR ("failed to parse profile from codec-data"); return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CODEC; } /* WMV3 -- expecting sequence header */ if (format == GST_MAKE_FOURCC ('W', 'M', 'V', '3')) { seq_hdr->struct_c.coded_width = width; seq_hdr->struct_c.coded_height = height; ebdu.type = GST_VC1_SEQUENCE; ebdu.size = buf_size; ebdu.sc_offset = 0; ebdu.offset = 0; ebdu.data = (guint8 *) buf; return decode_ebdu (decoder, &ebdu); } /* WVC1 -- expecting bitstream data units */ if (format != GST_MAKE_FOURCC ('W', 'V', 'C', '1')) return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE; seq_hdr->advanced.max_coded_width = width; seq_hdr->advanced.max_coded_height = height; ofs = 0; do { result = gst_vc1_identify_next_bdu (buf + ofs, buf_size - ofs, &ebdu); switch (result) { case GST_VC1_PARSER_NO_BDU_END: /* Assume the EBDU is complete within codec-data bounds */ ebdu.size = buf_size - ofs - ebdu.offset; // fall-through case GST_VC1_PARSER_OK: status = decode_ebdu (decoder, &ebdu); ofs += ebdu.offset + ebdu.size; break; default: status = get_status (result); break; } } while (status == GST_VAAPI_DECODER_STATUS_SUCCESS && ofs < buf_size); return status; } static GstVaapiDecoderStatus ensure_decoder (GstVaapiDecoderVC1 * decoder) { GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVaapiDecoderStatus status; if (!priv->is_opened) { priv->is_opened = gst_vaapi_decoder_vc1_open (decoder); if (!priv->is_opened) return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CODEC; status = gst_vaapi_decoder_decode_codec_data (GST_VAAPI_DECODER_CAST (decoder)); if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) return status; } return GST_VAAPI_DECODER_STATUS_SUCCESS; } static inline gint scan_for_start_code (GstAdapter * adapter, guint ofs, guint size, guint32 * scp) { return (gint) gst_adapter_masked_scan_uint32_peek (adapter, 0xffffff00, 0x00000100, ofs, size, scp); } static GstVaapiDecoderStatus gst_vaapi_decoder_vc1_parse (GstVaapiDecoder * base_decoder, GstAdapter * adapter, gboolean at_eos, GstVaapiDecoderUnit * unit) { GstVaapiDecoderVC1 *const decoder = GST_VAAPI_DECODER_VC1_CAST (base_decoder); GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVaapiDecoderStatus status; guint8 bdu_type; guint size, buf_size, flags = 0; gint ofs; status = ensure_decoder (decoder); if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) return status; size = gst_adapter_available (adapter); if (priv->has_codec_data) { // Assume demuxer sends out plain frames if (size < 1) return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA; buf_size = size; bdu_type = GST_VC1_FRAME; } else { if (size < 4) return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA; ofs = scan_for_start_code (adapter, 0, size, NULL); if (ofs < 0) return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA; gst_adapter_flush (adapter, ofs); size -= ofs; ofs = G_UNLIKELY (size < 8) ? -1 : scan_for_start_code (adapter, 4, size - 4, NULL); if (ofs < 0) { // Assume the whole packet is present if end-of-stream if (!at_eos) return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA; ofs = size; } buf_size = ofs; gst_adapter_copy (adapter, &bdu_type, 3, 1); } unit->size = buf_size; /* Check for new picture layer */ switch (bdu_type) { case GST_VC1_END_OF_SEQ: flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_END; flags |= GST_VAAPI_DECODER_UNIT_FLAG_STREAM_END; break; case GST_VC1_SEQUENCE: case GST_VC1_ENTRYPOINT: flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_START; break; case GST_VC1_FRAME: flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_START; flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE; break; case GST_VC1_SLICE: flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE; break; case GST_VC1_FIELD: /* @FIXME: intel-driver cannot handle interlaced frames */ GST_ERROR ("interlaced video not supported"); return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE; } GST_VAAPI_DECODER_UNIT_FLAG_SET (unit, flags); return GST_VAAPI_DECODER_STATUS_SUCCESS; } static GstVaapiDecoderStatus gst_vaapi_decoder_vc1_decode (GstVaapiDecoder * base_decoder, GstVaapiDecoderUnit * unit) { GstVaapiDecoderVC1 *const decoder = GST_VAAPI_DECODER_VC1_CAST (base_decoder); GstVaapiDecoderStatus status; GstBuffer *const buffer = GST_VAAPI_DECODER_CODEC_FRAME (decoder)->input_buffer; GstMapInfo map_info; status = ensure_decoder (decoder); if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) return status; if (!gst_buffer_map (buffer, &map_info, GST_MAP_READ)) { GST_ERROR ("failed to map buffer"); return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; } status = decode_buffer (decoder, map_info.data + unit->offset, unit->size); gst_buffer_unmap (buffer, &map_info); if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) return status; return GST_VAAPI_DECODER_STATUS_SUCCESS; } static GstVaapiDecoderStatus gst_vaapi_decoder_vc1_start_frame (GstVaapiDecoder * base_decoder, GstVaapiDecoderUnit * unit) { GstVaapiDecoderVC1 *const decoder = GST_VAAPI_DECODER_VC1_CAST (base_decoder); GstVaapiDecoderVC1Private *const priv = &decoder->priv; GstVaapiDecoderStatus status; GstVaapiPicture *picture; status = ensure_context (decoder); if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) { GST_ERROR ("failed to reset context"); return status; } status = ensure_decoder (decoder); if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) return status; picture = GST_VAAPI_PICTURE_NEW (VC1, 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); /* Update cropping rectangle */ do { GstVC1AdvancedSeqHdr *adv_hdr; GstVaapiRectangle crop_rect; if (priv->profile != GST_VAAPI_PROFILE_VC1_ADVANCED) break; adv_hdr = &priv->seq_hdr.advanced; if (!adv_hdr->display_ext) break; crop_rect.x = 0; crop_rect.y = 0; crop_rect.width = adv_hdr->disp_horiz_size; crop_rect.height = adv_hdr->disp_vert_size; if (crop_rect.width <= priv->width && crop_rect.height <= priv->height) gst_vaapi_picture_set_crop_rect (picture, &crop_rect); } while (0); if (!gst_vc1_bitplanes_ensure_size (priv->bitplanes, &priv->seq_hdr)) { GST_ERROR ("failed to allocate bitplanes"); return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; } return GST_VAAPI_DECODER_STATUS_SUCCESS; } static GstVaapiDecoderStatus gst_vaapi_decoder_vc1_end_frame (GstVaapiDecoder * base_decoder) { GstVaapiDecoderVC1 *const decoder = GST_VAAPI_DECODER_VC1_CAST (base_decoder); return decode_current_picture (decoder); } static GstVaapiDecoderStatus gst_vaapi_decoder_vc1_flush (GstVaapiDecoder * base_decoder) { GstVaapiDecoderVC1 *const decoder = GST_VAAPI_DECODER_VC1_CAST (base_decoder); GstVaapiDecoderVC1Private *const priv = &decoder->priv; if (priv->is_opened) gst_vaapi_dpb_flush (priv->dpb); return GST_VAAPI_DECODER_STATUS_SUCCESS; } static void gst_vaapi_decoder_vc1_finalize (GObject * object) { GstVaapiDecoder *const base_decoder = GST_VAAPI_DECODER (object); gst_vaapi_decoder_vc1_destroy (base_decoder); G_OBJECT_CLASS (gst_vaapi_decoder_vc1_parent_class)->finalize (object); } static void gst_vaapi_decoder_vc1_class_init (GstVaapiDecoderVC1Class * klass) { GObjectClass *const object_class = G_OBJECT_CLASS (klass); GstVaapiDecoderClass *const decoder_class = GST_VAAPI_DECODER_CLASS (klass); object_class->finalize = gst_vaapi_decoder_vc1_finalize; decoder_class->reset = gst_vaapi_decoder_vc1_reset; decoder_class->parse = gst_vaapi_decoder_vc1_parse; decoder_class->decode = gst_vaapi_decoder_vc1_decode; decoder_class->start_frame = gst_vaapi_decoder_vc1_start_frame; decoder_class->end_frame = gst_vaapi_decoder_vc1_end_frame; decoder_class->flush = gst_vaapi_decoder_vc1_flush; decoder_class->decode_codec_data = gst_vaapi_decoder_vc1_decode_codec_data; } static void gst_vaapi_decoder_vc1_init (GstVaapiDecoderVC1 * decoder) { GstVaapiDecoder *const base_decoder = GST_VAAPI_DECODER (decoder); gst_vaapi_decoder_vc1_create (base_decoder); } /** * gst_vaapi_decoder_vc1_new: * @display: a #GstVaapiDisplay * @caps: a #GstCaps holding codec information * * Creates a new #GstVaapiDecoder for VC-1 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_vc1_new (GstVaapiDisplay * display, GstCaps * caps) { return g_object_new (GST_TYPE_VAAPI_DECODER_VC1, "display", display, "caps", caps, NULL); }