mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-19 23:06:49 +00:00
decoder: h264: add support for MVC interlaced streams.
Fix support for MVC Stereo High profile streams with interlaced frames. Also improve the detection logic of the first field.
This commit is contained in:
parent
5d76afb40d
commit
abcc8bb7a2
1 changed files with 90 additions and 11 deletions
|
@ -450,7 +450,8 @@ struct _GstVaapiDecoderH264Private {
|
|||
GstVaapiParserInfoH264 *active_pps;
|
||||
GstVaapiParserInfoH264 *prev_pi;
|
||||
GstVaapiParserInfoH264 *prev_slice_pi;
|
||||
GstVaapiFrameStore *prev_frame;
|
||||
GstVaapiFrameStore **prev_frames;
|
||||
guint prev_frames_alloc;
|
||||
GstVaapiFrameStore **dpb;
|
||||
guint dpb_count;
|
||||
guint dpb_size;
|
||||
|
@ -519,6 +520,13 @@ is_mvc_profile(GstH264Profile profile)
|
|||
profile == GST_H264_PROFILE_STEREO_HIGH;
|
||||
}
|
||||
|
||||
/* Determines the view_id from the supplied NAL unit */
|
||||
static inline guint
|
||||
get_view_id(GstH264NalUnit *nalu)
|
||||
{
|
||||
return GST_H264_IS_MVC_NALU(nalu) ? nalu->extension.mvc.view_id : 0;
|
||||
}
|
||||
|
||||
/* Determines the view order index (VOIdx) from the supplied view_id */
|
||||
static gint
|
||||
get_view_order_index(GstH264SPS *sps, guint16 view_id)
|
||||
|
@ -685,6 +693,23 @@ dpb_evict(GstVaapiDecoderH264 *decoder, GstVaapiPictureH264 *picture, guint i)
|
|||
dpb_remove_index(decoder, i);
|
||||
}
|
||||
|
||||
/* Finds the frame store holding the supplied picture */
|
||||
static gint
|
||||
dpb_find_picture(GstVaapiDecoderH264 *decoder, GstVaapiPictureH264 *picture)
|
||||
{
|
||||
GstVaapiDecoderH264Private * const priv = &decoder->priv;
|
||||
gint i, j;
|
||||
|
||||
for (i = 0; i < priv->dpb_count; i++) {
|
||||
GstVaapiFrameStore * const fs = priv->dpb[i];
|
||||
for (j = 0; j < fs->num_buffers; j++) {
|
||||
if (fs->buffers[j] == picture)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Finds the picture with the lowest POC that needs to be output */
|
||||
static gint
|
||||
dpb_find_lowest_poc(GstVaapiDecoderH264 *decoder, GstVaapiPictureH264 *picture,
|
||||
|
@ -791,7 +816,13 @@ dpb_clear(GstVaapiDecoderH264 *decoder, GstVaapiPictureH264 *picture)
|
|||
}
|
||||
priv->dpb_count = n;
|
||||
|
||||
gst_vaapi_frame_store_replace(&priv->prev_frame, NULL);
|
||||
/* Clear previous frame buffers only if this is a "flush-all" operation,
|
||||
or if the picture is the first one in the access unit */
|
||||
if (!picture || GST_VAAPI_PICTURE_FLAG_IS_SET(picture,
|
||||
GST_VAAPI_PICTURE_FLAG_AU_START)) {
|
||||
for (i = 0; i < priv->max_views; i++)
|
||||
gst_vaapi_picture_replace(&priv->prev_frames[i], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -846,15 +877,19 @@ dpb_add(GstVaapiDecoderH264 *decoder, GstVaapiPictureH264 *picture)
|
|||
}
|
||||
|
||||
// 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))
|
||||
return gst_vaapi_frame_store_add(fs, picture);
|
||||
if (GST_VAAPI_PICTURE_IS_INTERLACED(picture) &&
|
||||
!GST_VAAPI_PICTURE_IS_FIRST_FIELD(picture)) {
|
||||
const gint found_index = dpb_find_picture(decoder,
|
||||
GST_VAAPI_PICTURE_H264(picture->base.parent_picture));
|
||||
if (found_index >= 0)
|
||||
return gst_vaapi_frame_store_add(priv->dpb[found_index], 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_replace(&priv->prev_frames[picture->base.voc], fs);
|
||||
gst_vaapi_frame_store_unref(fs);
|
||||
|
||||
if (!priv->progressive_sequence && gst_vaapi_frame_store_has_frame(fs)) {
|
||||
|
@ -937,6 +972,7 @@ static gboolean
|
|||
mvc_reset(GstVaapiDecoderH264 *decoder)
|
||||
{
|
||||
GstVaapiDecoderH264Private * const priv = &decoder->priv;
|
||||
guint i;
|
||||
|
||||
// Resize array of inter-view references
|
||||
if (priv->inter_views)
|
||||
|
@ -947,6 +983,20 @@ mvc_reset(GstVaapiDecoderH264 *decoder)
|
|||
if (!priv->inter_views)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Resize array of previous frame buffers
|
||||
for (i = priv->max_views; i < priv->prev_frames_alloc; i++)
|
||||
gst_vaapi_picture_replace(&priv->prev_frames[i], NULL);
|
||||
|
||||
priv->prev_frames = g_try_realloc_n(priv->prev_frames, priv->max_views,
|
||||
sizeof(*priv->prev_frames));
|
||||
if (!priv->prev_frames) {
|
||||
priv->prev_frames_alloc = 0;
|
||||
return FALSE;
|
||||
}
|
||||
for (i = priv->prev_frames_alloc; i < priv->max_views; i++)
|
||||
priv->prev_frames[i] = NULL;
|
||||
priv->prev_frames_alloc = priv->max_views;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -1021,6 +1071,10 @@ gst_vaapi_decoder_h264_destroy(GstVaapiDecoder *base_decoder)
|
|||
priv->dpb = NULL;
|
||||
priv->dpb_size = 0;
|
||||
|
||||
g_free(priv->prev_frames);
|
||||
priv->prev_frames = NULL;
|
||||
priv->prev_frames_alloc = 0;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS(priv->pps); i++)
|
||||
gst_vaapi_parser_info_h264_replace(&priv->pps[i], NULL);
|
||||
gst_vaapi_parser_info_h264_replace(&priv->active_pps, NULL);
|
||||
|
@ -1365,8 +1419,7 @@ decode_current_picture(GstVaapiDecoderH264 *decoder)
|
|||
goto error;
|
||||
if (!gst_vaapi_picture_decode(GST_VAAPI_PICTURE_CAST(picture)))
|
||||
goto error;
|
||||
if (priv->prev_frame && gst_vaapi_frame_store_has_frame(priv->prev_frame))
|
||||
gst_vaapi_picture_replace(&priv->current_picture, NULL);
|
||||
gst_vaapi_picture_replace(&priv->current_picture, NULL);
|
||||
return GST_VAAPI_DECODER_STATUS_SUCCESS;
|
||||
|
||||
error:
|
||||
|
@ -3227,6 +3280,31 @@ is_new_access_unit(GstVaapiParserInfoH264 *pi, GstVaapiParserInfoH264 *prev_pi)
|
|||
return voc < prev_voc;
|
||||
}
|
||||
|
||||
/* Finds the first field picture corresponding to the supplied picture */
|
||||
static GstVaapiPictureH264 *
|
||||
find_first_field(GstVaapiDecoderH264 *decoder, GstH264NalUnit *nalu,
|
||||
GstH264SliceHdr *slice_hdr)
|
||||
{
|
||||
GstVaapiDecoderH264Private * const priv = &decoder->priv;
|
||||
GstVaapiFrameStore *fs;
|
||||
gint voc;
|
||||
|
||||
if (!slice_hdr->field_pic_flag)
|
||||
return NULL;
|
||||
|
||||
voc = get_view_order_index(get_sps(decoder), get_view_id(nalu));
|
||||
if (voc < 0)
|
||||
return NULL;
|
||||
|
||||
fs = priv->prev_frames[voc];
|
||||
if (!fs || gst_vaapi_frame_store_has_frame(fs))
|
||||
return NULL;
|
||||
|
||||
if (fs->buffers[0]->frame_num == slice_hdr->frame_num)
|
||||
return fs->buffers[0];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GstVaapiDecoderStatus
|
||||
decode_picture(GstVaapiDecoderH264 *decoder, GstVaapiDecoderUnit *unit)
|
||||
{
|
||||
|
@ -3235,7 +3313,7 @@ decode_picture(GstVaapiDecoderH264 *decoder, GstVaapiDecoderUnit *unit)
|
|||
GstH264SliceHdr * const slice_hdr = &pi->data.slice_hdr;
|
||||
GstH264PPS * const pps = ensure_pps(decoder, slice_hdr->pps);
|
||||
GstH264SPS * const sps = ensure_sps(decoder, slice_hdr->pps->sequence);
|
||||
GstVaapiPictureH264 *picture;
|
||||
GstVaapiPictureH264 *picture, *first_field;
|
||||
GstVaapiDecoderStatus status;
|
||||
|
||||
g_return_val_if_fail(pps != NULL, GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN);
|
||||
|
@ -3258,9 +3336,10 @@ decode_picture(GstVaapiDecoderH264 *decoder, GstVaapiDecoderUnit *unit)
|
|||
|
||||
priv->decoder_state = 0;
|
||||
|
||||
if (priv->current_picture) {
|
||||
first_field = find_first_field(decoder, &pi->nalu, slice_hdr);
|
||||
if (first_field) {
|
||||
/* Re-use current picture where the first field was decoded */
|
||||
picture = gst_vaapi_picture_h264_new_field(priv->current_picture);
|
||||
picture = gst_vaapi_picture_h264_new_field(first_field);
|
||||
if (!picture) {
|
||||
GST_ERROR("failed to allocate field picture");
|
||||
return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED;
|
||||
|
|
Loading…
Reference in a new issue