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;
};
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)
{
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)
{
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;
guint is_opened : 1;
guint is_avc : 1;
guint has_context : 1;
+ guint progressive_sequence : 1;
};
static gboolean
{
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));
}
}
- // 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) {
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,
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;
}
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,
GstVaapiPictureH264 *picture,
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];
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];
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
+ );
}
}
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 */
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;
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];
+ }
}
}
/* 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 */
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;
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;
}
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;
+ }
+ }
+ 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;
+ }
}
- priv->current_picture = picture;
+ gst_vaapi_picture_replace(&priv->current_picture, picture);
+ gst_vaapi_picture_unref(picture);
picture->pps = pps;
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));