mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-24 02:31:03 +00:00
h264: add initial support for interlaced streams.
Decoded frames are only output when they are complete, i.e. when both fields are decoded. This also means that the "interlaced" caps is not propagated to vaapipostproc or vaapisink elements. Another limitation is that interlaced bitstreams with MMCO are unlikely to work.
This commit is contained in:
parent
c59d935505
commit
2c13b17cdb
1 changed files with 232 additions and 23 deletions
|
@ -128,6 +128,7 @@ struct _GstVaapiPictureH264 {
|
|||
gint32 long_term_frame_idx; // Temporary for ref pic marking: LongTermFrameIdx
|
||||
gint32 pic_num; // Temporary for ref pic marking: PicNum
|
||||
gint32 long_term_pic_num; // Temporary for ref pic marking: LongTermPicNum
|
||||
GstVaapiPictureH264 *other_field; // Temporary for ref pic marking: other field in the same frame store
|
||||
guint output_flag : 1;
|
||||
guint output_needed : 1;
|
||||
};
|
||||
|
@ -193,6 +194,19 @@ gst_vaapi_picture_h264_set_reference(
|
|||
GST_VAAPI_PICTURE_FLAG_SET(picture, reference_flags);
|
||||
}
|
||||
|
||||
static inline GstVaapiPictureH264 *
|
||||
gst_vaapi_picture_h264_new_field(GstVaapiPictureH264 *picture)
|
||||
{
|
||||
GstVaapiPicture *base_picture;
|
||||
|
||||
g_return_val_if_fail(GST_VAAPI_IS_PICTURE_H264(picture), NULL);
|
||||
|
||||
base_picture = gst_vaapi_picture_new_field(&picture->base);
|
||||
if (!base_picture)
|
||||
return NULL;
|
||||
return GST_VAAPI_PICTURE_H264_CAST(base_picture);
|
||||
}
|
||||
|
||||
static inline GstVaapiSliceH264 *
|
||||
gst_vaapi_picture_h264_get_last_slice(GstVaapiPictureH264 *picture)
|
||||
{
|
||||
|
@ -390,6 +404,61 @@ gst_vaapi_frame_store_new(GstVaapiPictureH264 *picture)
|
|||
return fs;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_vaapi_frame_store_add(GstVaapiFrameStore *fs, GstVaapiPictureH264 *picture)
|
||||
{
|
||||
guint field;
|
||||
|
||||
g_return_val_if_fail(GST_VAAPI_IS_FRAME_STORE(fs), FALSE);
|
||||
g_return_val_if_fail(fs->num_buffers == 1, FALSE);
|
||||
g_return_val_if_fail(GST_VAAPI_IS_PICTURE_H264(picture), FALSE);
|
||||
g_return_val_if_fail(!GST_VAAPI_PICTURE_IS_FRAME(picture), FALSE);
|
||||
|
||||
gst_vaapi_picture_replace(&fs->buffers[fs->num_buffers++], picture);
|
||||
if (picture->output_flag) {
|
||||
picture->output_needed = TRUE;
|
||||
fs->output_needed++;
|
||||
}
|
||||
|
||||
fs->structure = GST_VAAPI_PICTURE_STRUCTURE_FRAME;
|
||||
|
||||
field = picture->structure == GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD ?
|
||||
TOP_FIELD : BOTTOM_FIELD;
|
||||
g_return_val_if_fail(fs->buffers[0]->field_poc[field] == G_MAXINT32, FALSE);
|
||||
fs->buffers[0]->field_poc[field] = picture->field_poc[field];
|
||||
g_return_val_if_fail(picture->field_poc[!field] == G_MAXINT32, FALSE);
|
||||
picture->field_poc[!field] = fs->buffers[0]->field_poc[!field];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_vaapi_frame_store_split_fields(GstVaapiFrameStore *fs)
|
||||
{
|
||||
GstVaapiPictureH264 * const first_field = fs->buffers[0];
|
||||
GstVaapiPictureH264 *second_field;
|
||||
|
||||
g_return_val_if_fail(fs->num_buffers == 1, FALSE);
|
||||
|
||||
first_field->base.structure = GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD;
|
||||
GST_VAAPI_PICTURE_FLAG_SET(first_field, GST_VAAPI_PICTURE_FLAG_INTERLACED);
|
||||
|
||||
second_field = gst_vaapi_picture_h264_new_field(first_field);
|
||||
if (!second_field)
|
||||
return FALSE;
|
||||
gst_vaapi_picture_replace(&fs->buffers[fs->num_buffers++], second_field);
|
||||
gst_vaapi_picture_unref(second_field);
|
||||
|
||||
second_field->frame_num = first_field->frame_num;
|
||||
second_field->field_poc[0] = first_field->field_poc[0];
|
||||
second_field->field_poc[1] = first_field->field_poc[1];
|
||||
second_field->output_flag = first_field->output_flag;
|
||||
if (second_field->output_flag) {
|
||||
second_field->output_needed = TRUE;
|
||||
fs->output_needed++;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
gst_vaapi_frame_store_has_frame(GstVaapiFrameStore *fs)
|
||||
{
|
||||
|
@ -448,9 +517,9 @@ struct _GstVaapiDecoderH264Private {
|
|||
GstVaapiProfile profile;
|
||||
GstVaapiEntrypoint entrypoint;
|
||||
GstVaapiChromaType chroma_type;
|
||||
GstVaapiPictureH264 *short_ref[16];
|
||||
GstVaapiPictureH264 *short_ref[32];
|
||||
guint short_ref_count;
|
||||
GstVaapiPictureH264 *long_ref[16];
|
||||
GstVaapiPictureH264 *long_ref[32];
|
||||
guint long_ref_count;
|
||||
GstVaapiPictureH264 *RefPicList0[32];
|
||||
guint RefPicList0_count;
|
||||
|
@ -473,6 +542,7 @@ struct _GstVaapiDecoderH264Private {
|
|||
guint is_opened : 1;
|
||||
guint is_avc : 1;
|
||||
guint has_context : 1;
|
||||
guint progressive_sequence : 1;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
|
@ -602,8 +672,11 @@ dpb_output(
|
|||
{
|
||||
picture->output_needed = FALSE;
|
||||
|
||||
if (fs)
|
||||
fs->output_needed--;
|
||||
if (fs) {
|
||||
if (--fs->output_needed > 0)
|
||||
return TRUE;
|
||||
picture = fs->buffers[0];
|
||||
}
|
||||
|
||||
/* XXX: update cropping rectangle */
|
||||
return gst_vaapi_picture_output(GST_VAAPI_PICTURE_CAST(picture));
|
||||
|
@ -686,13 +759,27 @@ dpb_add(GstVaapiDecoderH264 *decoder, GstVaapiPictureH264 *picture)
|
|||
}
|
||||
}
|
||||
|
||||
// Create new frame store
|
||||
// Check if picture is the second field and the first field is still in DPB
|
||||
fs = priv->prev_frame;
|
||||
if (fs && !gst_vaapi_frame_store_has_frame(fs)) {
|
||||
g_return_val_if_fail(fs->num_buffers == 1, FALSE);
|
||||
g_return_val_if_fail(!GST_VAAPI_PICTURE_IS_FRAME(picture), FALSE);
|
||||
g_return_val_if_fail(!GST_VAAPI_PICTURE_IS_FIRST_FIELD(picture), FALSE);
|
||||
return gst_vaapi_frame_store_add(fs, picture);
|
||||
}
|
||||
|
||||
// Create new frame store, and split fields if necessary
|
||||
fs = gst_vaapi_frame_store_new(picture);
|
||||
if (!fs)
|
||||
return FALSE;
|
||||
gst_vaapi_frame_store_replace(&priv->prev_frame, fs);
|
||||
gst_vaapi_frame_store_unref(fs);
|
||||
|
||||
if (!priv->progressive_sequence && gst_vaapi_frame_store_has_frame(fs)) {
|
||||
if (!gst_vaapi_frame_store_split_fields(fs))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// C.4.5.1 - Storage and marking of a reference decoded picture into the DPB
|
||||
if (GST_VAAPI_PICTURE_IS_REFERENCE(picture)) {
|
||||
while (priv->dpb_count == priv->dpb_size) {
|
||||
|
@ -928,6 +1015,12 @@ ensure_context(GstVaapiDecoderH264 *decoder, GstH264SPS *sps)
|
|||
priv->height = sps->height;
|
||||
}
|
||||
|
||||
priv->progressive_sequence = sps->frame_mbs_only_flag;
|
||||
#if 0
|
||||
/* XXX: we only output complete frames for now */
|
||||
gst_vaapi_decoder_set_interlaced(base_decoder, !priv->progressive_sequence);
|
||||
#endif
|
||||
|
||||
gst_vaapi_decoder_set_pixel_aspect_ratio(
|
||||
base_decoder,
|
||||
sps->vui_parameters.par_n,
|
||||
|
@ -1045,10 +1138,12 @@ decode_current_picture(GstVaapiDecoderH264 *decoder)
|
|||
goto error;
|
||||
if (!gst_vaapi_picture_decode(GST_VAAPI_PICTURE_CAST(picture)))
|
||||
goto error;
|
||||
gst_vaapi_picture_replace(&priv->current_picture, NULL);
|
||||
if (priv->prev_frame && gst_vaapi_frame_store_has_frame(priv->prev_frame))
|
||||
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;
|
||||
}
|
||||
|
@ -1448,6 +1543,59 @@ init_picture_refs_pic_num(
|
|||
#define SORT_REF_LIST(list, n, compare_func) \
|
||||
qsort(list, n, sizeof(*(list)), compare_picture_##compare_func)
|
||||
|
||||
static void
|
||||
init_picture_refs_fields_1(
|
||||
guint picture_structure,
|
||||
GstVaapiPictureH264 *RefPicList[32],
|
||||
guint *RefPicList_count,
|
||||
GstVaapiPictureH264 *ref_list[32],
|
||||
guint ref_list_count
|
||||
)
|
||||
{
|
||||
guint i, j, n;
|
||||
|
||||
i = 0;
|
||||
j = 0;
|
||||
n = *RefPicList_count;
|
||||
do {
|
||||
g_assert(n < 32);
|
||||
for (; i < ref_list_count; i++) {
|
||||
if (ref_list[i]->structure == picture_structure) {
|
||||
RefPicList[n++] = ref_list[i++];
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (; j < ref_list_count; j++) {
|
||||
if (ref_list[j]->structure != picture_structure) {
|
||||
RefPicList[n++] = ref_list[j++];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (i < ref_list_count || j < ref_list_count);
|
||||
*RefPicList_count = n;
|
||||
}
|
||||
|
||||
static inline void
|
||||
init_picture_refs_fields(
|
||||
GstVaapiPictureH264 *picture,
|
||||
GstVaapiPictureH264 *RefPicList[32],
|
||||
guint *RefPicList_count,
|
||||
GstVaapiPictureH264 *short_ref[32],
|
||||
guint short_ref_count,
|
||||
GstVaapiPictureH264 *long_ref[32],
|
||||
guint long_ref_count
|
||||
)
|
||||
{
|
||||
guint n = 0;
|
||||
|
||||
/* 8.2.4.2.5 - reference picture lists in fields */
|
||||
init_picture_refs_fields_1(picture->structure, RefPicList, &n,
|
||||
short_ref, short_ref_count);
|
||||
init_picture_refs_fields_1(picture->structure, RefPicList, &n,
|
||||
long_ref, long_ref_count);
|
||||
*RefPicList_count = n;
|
||||
}
|
||||
|
||||
static void
|
||||
init_picture_refs_p_slice(
|
||||
GstVaapiDecoderH264 *decoder,
|
||||
|
@ -1486,8 +1634,6 @@ init_picture_refs_p_slice(
|
|||
GstVaapiPictureH264 *long_ref[32];
|
||||
guint long_ref_count = 0;
|
||||
|
||||
// XXX: handle second field if current field is marked as
|
||||
// "used for short-term reference"
|
||||
if (priv->short_ref_count > 0) {
|
||||
for (i = 0; i < priv->short_ref_count; i++)
|
||||
short_ref[i] = priv->short_ref[i];
|
||||
|
@ -1495,8 +1641,6 @@ init_picture_refs_p_slice(
|
|||
short_ref_count = i;
|
||||
}
|
||||
|
||||
// XXX: handle second field if current field is marked as
|
||||
// "used for long-term reference"
|
||||
if (priv->long_ref_count > 0) {
|
||||
for (i = 0; i < priv->long_ref_count; i++)
|
||||
long_ref[i] = priv->long_ref[i];
|
||||
|
@ -1504,7 +1648,12 @@ init_picture_refs_p_slice(
|
|||
long_ref_count = i;
|
||||
}
|
||||
|
||||
// XXX: handle 8.2.4.2.5
|
||||
init_picture_refs_fields(
|
||||
picture,
|
||||
priv->RefPicList0, &priv->RefPicList0_count,
|
||||
short_ref, short_ref_count,
|
||||
long_ref, long_ref_count
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1637,8 +1786,20 @@ init_picture_refs_b_slice(
|
|||
long_ref_count = i;
|
||||
}
|
||||
|
||||
// XXX: handle 8.2.4.2.5
|
||||
}
|
||||
init_picture_refs_fields(
|
||||
picture,
|
||||
priv->RefPicList0, &priv->RefPicList0_count,
|
||||
short_ref0, short_ref0_count,
|
||||
long_ref, long_ref_count
|
||||
);
|
||||
|
||||
init_picture_refs_fields(
|
||||
picture,
|
||||
priv->RefPicList1, &priv->RefPicList1_count,
|
||||
short_ref1, short_ref1_count,
|
||||
long_ref, long_ref_count
|
||||
);
|
||||
}
|
||||
|
||||
/* Check whether RefPicList1 is identical to RefPicList0, then
|
||||
swap if necessary */
|
||||
|
@ -1837,7 +1998,7 @@ static void
|
|||
init_picture_ref_lists(GstVaapiDecoderH264 *decoder)
|
||||
{
|
||||
GstVaapiDecoderH264Private * const priv = decoder->priv;
|
||||
guint i, short_ref_count, long_ref_count;
|
||||
guint i, j, short_ref_count, long_ref_count;
|
||||
|
||||
short_ref_count = 0;
|
||||
long_ref_count = 0;
|
||||
|
@ -1853,6 +2014,21 @@ init_picture_ref_lists(GstVaapiDecoderH264 *decoder)
|
|||
else if (GST_VAAPI_PICTURE_IS_LONG_TERM_REFERENCE(picture))
|
||||
priv->long_ref[long_ref_count++] = picture;
|
||||
picture->structure = GST_VAAPI_PICTURE_STRUCTURE_FRAME;
|
||||
picture->other_field = fs->buffers[1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < priv->dpb_count; i++) {
|
||||
GstVaapiFrameStore * const fs = priv->dpb[i];
|
||||
for (j = 0; j < fs->num_buffers; j++) {
|
||||
GstVaapiPictureH264 * const picture = fs->buffers[j];
|
||||
if (GST_VAAPI_PICTURE_IS_SHORT_TERM_REFERENCE(picture))
|
||||
priv->short_ref[short_ref_count++] = picture;
|
||||
else if (GST_VAAPI_PICTURE_IS_LONG_TERM_REFERENCE(picture))
|
||||
priv->long_ref[long_ref_count++] = picture;
|
||||
picture->structure = picture->base.structure;
|
||||
picture->other_field = fs->buffers[j ^ 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1963,10 +2139,13 @@ init_picture(
|
|||
/* Initialize picture structure */
|
||||
if (!slice_hdr->field_pic_flag)
|
||||
base_picture->structure = GST_VAAPI_PICTURE_STRUCTURE_FRAME;
|
||||
else if (!slice_hdr->bottom_field_flag)
|
||||
base_picture->structure = GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD;
|
||||
else
|
||||
base_picture->structure = GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD;
|
||||
else {
|
||||
GST_VAAPI_PICTURE_FLAG_SET(picture, GST_VAAPI_PICTURE_FLAG_INTERLACED);
|
||||
if (!slice_hdr->bottom_field_flag)
|
||||
base_picture->structure = GST_VAAPI_PICTURE_STRUCTURE_TOP_FIELD;
|
||||
else
|
||||
base_picture->structure = GST_VAAPI_PICTURE_STRUCTURE_BOTTOM_FIELD;
|
||||
}
|
||||
picture->structure = base_picture->structure;
|
||||
|
||||
/* Initialize reference flags */
|
||||
|
@ -2000,9 +2179,14 @@ exec_ref_pic_marking_sliding_window(GstVaapiDecoderH264 *decoder)
|
|||
|
||||
GST_DEBUG("reference picture marking process (sliding window)");
|
||||
|
||||
if (!GST_VAAPI_PICTURE_IS_FIRST_FIELD(priv->current_picture))
|
||||
return TRUE;
|
||||
|
||||
max_num_ref_frames = sps->num_ref_frames;
|
||||
if (max_num_ref_frames == 0)
|
||||
max_num_ref_frames = 1;
|
||||
if (!GST_VAAPI_PICTURE_IS_FRAME(priv->current_picture))
|
||||
max_num_ref_frames <<= 1;
|
||||
|
||||
if (priv->short_ref_count + priv->long_ref_count < max_num_ref_frames)
|
||||
return TRUE;
|
||||
|
@ -2018,6 +2202,18 @@ exec_ref_pic_marking_sliding_window(GstVaapiDecoderH264 *decoder)
|
|||
ref_picture = priv->short_ref[m];
|
||||
gst_vaapi_picture_h264_set_reference(ref_picture, 0);
|
||||
ARRAY_REMOVE_INDEX(priv->short_ref, m);
|
||||
|
||||
/* Both fields need to be marked as "unused for reference" */
|
||||
if (ref_picture->other_field)
|
||||
gst_vaapi_picture_h264_set_reference(ref_picture->other_field, 0);
|
||||
if (!GST_VAAPI_PICTURE_IS_FRAME(priv->current_picture)) {
|
||||
for (i = 0; i < priv->short_ref_count; i++) {
|
||||
if (priv->short_ref[i] == ref_picture->other_field) {
|
||||
ARRAY_REMOVE_INDEX(priv->short_ref, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -2455,12 +2651,24 @@ decode_picture(GstVaapiDecoderH264 *decoder, GstH264NalUnit *nalu, GstH264SliceH
|
|||
if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
picture = gst_vaapi_picture_h264_new(decoder);
|
||||
if (!picture) {
|
||||
GST_ERROR("failed to allocate picture");
|
||||
return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
|
||||
if (priv->current_picture) {
|
||||
/* Re-use current picture where the first field was decoded */
|
||||
picture = gst_vaapi_picture_h264_new_field(priv->current_picture);
|
||||
if (!picture) {
|
||||
GST_ERROR("failed to allocate field picture");
|
||||
return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
|
||||
}
|
||||
}
|
||||
priv->current_picture = picture;
|
||||
else {
|
||||
/* Create new picture */
|
||||
picture = gst_vaapi_picture_h264_new(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);
|
||||
|
||||
picture->pps = pps;
|
||||
|
||||
|
@ -2986,6 +3194,7 @@ gst_vaapi_decoder_h264_init(GstVaapiDecoderH264 *decoder)
|
|||
priv->is_opened = FALSE;
|
||||
priv->is_avc = FALSE;
|
||||
priv->has_context = FALSE;
|
||||
priv->progressive_sequence = TRUE;
|
||||
|
||||
memset(priv->dpb, 0, sizeof(priv->dpb));
|
||||
memset(priv->short_ref, 0, sizeof(priv->short_ref));
|
||||
|
|
Loading…
Reference in a new issue