gstreamer/gst-libs/gst/vaapi/gstvaapidecoder_vc1.c
Víctor Manuel Jáquez Leal 7ebef58093 decoder: vc1: fails only on advanced profile
In commit 2eb4394 the frame coding mode was verified for progressive
regardless the profile. But the FCM is only valid in the advanced
profile. This patch checks for the advanced profile before verifying FCM for
progressive.

https://bugzilla.gnome.org/show_bug.cgi?id=769250
2016-09-06 12:39:22 +02:00

1460 lines
47 KiB
C

/*
* gstvaapidecoder_vc1.c - VC-1 decoder
*
* Copyright (C) 2011-2013 Intel Corporation
* Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
*
* 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 <string.h>
#include <gst/codecparsers/gstvc1parser.h>
#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 is_first_field: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;
};
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;
}
}
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;
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_free (priv->rbdu_buffer);
priv->rbdu_buffer = NULL;
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->profile = (GstVaapiProfile) 0;
priv->rndctrl = 0;
return TRUE;
}
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;
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;
}
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_class_init (GstVaapiDecoderVC1Class * klass)
{
GstVaapiMiniObjectClass *const object_class =
GST_VAAPI_MINI_OBJECT_CLASS (klass);
GstVaapiDecoderClass *const decoder_class = GST_VAAPI_DECODER_CLASS (klass);
object_class->size = sizeof (GstVaapiDecoderVC1);
object_class->finalize = (GDestroyNotify) gst_vaapi_decoder_finalize;
decoder_class->create = gst_vaapi_decoder_vc1_create;
decoder_class->destroy = gst_vaapi_decoder_vc1_destroy;
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 inline const GstVaapiDecoderClass *
gst_vaapi_decoder_vc1_class (void)
{
static GstVaapiDecoderVC1Class g_class;
static gsize g_class_init = FALSE;
if (g_once_init_enter (&g_class_init)) {
gst_vaapi_decoder_vc1_class_init (&g_class);
g_once_init_leave (&g_class_init, TRUE);
}
return GST_VAAPI_DECODER_CLASS (&g_class);
}
/**
* 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 gst_vaapi_decoder_new (gst_vaapi_decoder_vc1_class (), display, caps);
}