codecs: h264decoder: Add support for interlaced stream

Initial support for interlaced stream. Subclass should implement
new_field_picture() vfunc. Otherwise, baseclass will assume that
subclass doesn't support interlaced stream.

Restrictions:
* Reference picture modification process for interlaced stream
  is not implemented yet
* PAFF (Picture Adaptive Frame Field) is not properly implemented.
* Field display ordering (e.g., top-field-first) decision should
  be enhanced via picture timing SEI parsing
* Gap in field picture should be handled

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1534>
This commit is contained in:
Seungha Yang 2020-11-10 01:28:03 +09:00 committed by GStreamer Merge Bot
parent 52a8864076
commit 0b9f975b36
3 changed files with 342 additions and 50 deletions

View file

@ -181,7 +181,8 @@ static void gst_h264_decoder_prepare_ref_pic_lists (GstH264Decoder * self);
static void gst_h264_decoder_clear_ref_pic_lists (GstH264Decoder * self); static void gst_h264_decoder_clear_ref_pic_lists (GstH264Decoder * self);
static gboolean gst_h264_decoder_modify_ref_pic_lists (GstH264Decoder * self); static gboolean gst_h264_decoder_modify_ref_pic_lists (GstH264Decoder * self);
static gboolean static gboolean
gst_h264_decoder_sliding_window_picture_marking (GstH264Decoder * self); gst_h264_decoder_sliding_window_picture_marking (GstH264Decoder * self,
GstH264Picture * picture);
static void gst_h264_decoder_do_output_picture (GstH264Decoder * self, static void gst_h264_decoder_do_output_picture (GstH264Decoder * self,
GstH264Picture * picture); GstH264Picture * picture);
@ -581,14 +582,24 @@ gst_h264_decoder_update_pic_nums (GstH264Decoder * self,
continue; continue;
if (GST_H264_PICTURE_IS_LONG_TERM_REF (picture)) { if (GST_H264_PICTURE_IS_LONG_TERM_REF (picture)) {
picture->long_term_pic_num = picture->long_term_frame_idx; if (current_picture->field == GST_H264_PICTURE_FIELD_FRAME)
picture->long_term_pic_num = picture->long_term_frame_idx;
else if (current_picture->field == picture->field)
picture->long_term_pic_num = 2 * picture->long_term_frame_idx + 1;
else
picture->long_term_pic_num = 2 * picture->long_term_frame_idx;
} else { } else {
if (picture->frame_num > frame_num) if (picture->frame_num > frame_num)
picture->frame_num_wrap = picture->frame_num - priv->max_frame_num; picture->frame_num_wrap = picture->frame_num - priv->max_frame_num;
else else
picture->frame_num_wrap = picture->frame_num; picture->frame_num_wrap = picture->frame_num;
picture->pic_num = picture->frame_num_wrap; if (current_picture->field == GST_H264_PICTURE_FIELD_FRAME)
picture->pic_num = picture->frame_num_wrap;
else if (picture->field == current_picture->field)
picture->pic_num = 2 * picture->frame_num_wrap + 1;
else
picture->pic_num = 2 * picture->frame_num_wrap;
} }
} }
@ -651,7 +662,7 @@ gst_h264_decoder_handle_frame_num_gap (GstH264Decoder * self, gint frame_num)
unused_short_term_frame_num); unused_short_term_frame_num);
/* C.2.1 */ /* C.2.1 */
if (!gst_h264_decoder_sliding_window_picture_marking (self)) { if (!gst_h264_decoder_sliding_window_picture_marking (self, picture)) {
GST_ERROR_OBJECT (self, GST_ERROR_OBJECT (self,
"Couldn't perform sliding window picture marking"); "Couldn't perform sliding window picture marking");
return FALSE; return FALSE;
@ -769,6 +780,95 @@ gst_h264_decoder_start_current_picture (GstH264Decoder * self)
return TRUE; return TRUE;
} }
static GstH264Picture *
gst_h264_decoder_new_field_picture (GstH264Decoder * self,
GstH264Picture * picture)
{
GstH264DecoderClass *klass = GST_H264_DECODER_GET_CLASS (self);
GstH264Picture *new_picture;
/* Should not happen */
g_assert (picture->field != GST_H264_PICTURE_FIELD_FRAME);
if (!klass->new_field_picture) {
GST_WARNING_OBJECT (self, "Subclass does not support interlaced stream");
return NULL;
}
new_picture = gst_h264_picture_new ();
if (!klass->new_field_picture (self, picture, new_picture)) {
GST_ERROR_OBJECT (self, "Subclass couldn't handle new field picture");
gst_h264_picture_unref (new_picture);
return NULL;
}
new_picture->other_field = picture;
new_picture->second_field = TRUE;
new_picture->field = picture->field == GST_H264_PICTURE_FIELD_TOP_FIELD ?
GST_H264_PICTURE_FIELD_BOTTOM_FIELD : GST_H264_PICTURE_FIELD_TOP_FIELD;
return new_picture;
}
static gboolean
gst_h264_decoder_find_first_field_picture (GstH264Decoder * self,
GstH264Slice * slice, GstH264Picture ** first_field)
{
GstH264DecoderPrivate *priv = self->priv;
const GstH264SliceHdr *slice_hdr = &slice->header;
GstH264Picture *prev_picture;
GArray *pictures;
*first_field = NULL;
/* DPB is empty, must be the first field */
if (gst_h264_dpb_get_size (priv->dpb) == 0)
return TRUE;
pictures = gst_h264_dpb_get_pictures_all (priv->dpb);
prev_picture = g_array_index (pictures, GstH264Picture *, pictures->len - 1);
/* This is not a field picture */
if (!slice_hdr->field_pic_flag) {
/* Check whether the last picture is complete or not */
if (prev_picture->field != GST_H264_PICTURE_FIELD_FRAME &&
!prev_picture->other_field) {
GST_WARNING_OBJECT (self, "Previous picture %p (poc %d) is not complete",
prev_picture, prev_picture->pic_order_cnt);
/* FIXME: implement fill gap field picture */
return FALSE;
}
return TRUE;
}
/* Previous picture was not a field picture or complete already */
if (prev_picture->field == GST_H264_PICTURE_FIELD_FRAME ||
prev_picture->other_field)
return TRUE;
if (prev_picture->frame_num == slice_hdr->frame_num) {
GstH264PictureField current_field = slice_hdr->bottom_field_flag ?
GST_H264_PICTURE_FIELD_BOTTOM_FIELD : GST_H264_PICTURE_FIELD_TOP_FIELD;
if (current_field == prev_picture->field) {
GST_WARNING_OBJECT (self,
"Currnet picture and previous picture have identical field %d",
current_field);
/* FIXME: implement fill gap field picture */
return FALSE;
}
*first_field = prev_picture;
return TRUE;
}
return TRUE;
}
static gboolean static gboolean
gst_h264_decoder_parse_slice (GstH264Decoder * self, GstH264NalUnit * nalu) gst_h264_decoder_parse_slice (GstH264Decoder * self, GstH264NalUnit * nalu)
{ {
@ -797,26 +897,42 @@ gst_h264_decoder_parse_slice (GstH264Decoder * self, GstH264NalUnit * nalu)
if (!priv->current_picture) { if (!priv->current_picture) {
GstH264DecoderClass *klass = GST_H264_DECODER_GET_CLASS (self); GstH264DecoderClass *klass = GST_H264_DECODER_GET_CLASS (self);
GstH264Picture *picture; GstH264Picture *picture = NULL;
GstH264Picture *first_field = NULL;
gboolean ret = TRUE; gboolean ret = TRUE;
picture = gst_h264_picture_new ();
/* This allows accessing the frame from the picture. */
picture->system_frame_number = priv->current_frame->system_frame_number;
priv->current_picture = picture;
g_assert (priv->current_frame); g_assert (priv->current_frame);
if (klass->new_picture) if (!gst_h264_decoder_find_first_field_picture (self,
ret = klass->new_picture (self, priv->current_frame, picture); &priv->current_slice, &first_field)) {
GST_ERROR_OBJECT (self, "Couldn't find or determine first picture");
if (!ret) {
GST_ERROR_OBJECT (self, "subclass does not want accept new picture");
priv->current_picture = NULL;
gst_h264_picture_unref (picture);
return FALSE; return FALSE;
} }
if (first_field) {
picture = gst_h264_decoder_new_field_picture (self, first_field);
if (!picture) {
GST_ERROR_OBJECT (self, "Couldn't duplicate the first field picture");
return FALSE;
}
} else {
picture = gst_h264_picture_new ();
if (klass->new_picture)
ret = klass->new_picture (self, priv->current_frame, picture);
if (!ret) {
GST_ERROR_OBJECT (self, "subclass does not want accept new picture");
gst_h264_picture_unref (picture);
return FALSE;
}
}
/* This allows accessing the frame from the picture. */
picture->system_frame_number = priv->current_frame->system_frame_number;
priv->current_picture = picture;
if (!gst_h264_decoder_start_current_picture (self)) { if (!gst_h264_decoder_start_current_picture (self)) {
GST_ERROR_OBJECT (self, "start picture failed"); GST_ERROR_OBJECT (self, "start picture failed");
return FALSE; return FALSE;
@ -1034,10 +1150,16 @@ gst_h264_decoder_fill_picture_from_slice (GstH264Decoder * self,
picture->nal_ref_idc = slice->nalu.ref_idc; picture->nal_ref_idc = slice->nalu.ref_idc;
if (slice->nalu.ref_idc != 0) if (slice->nalu.ref_idc != 0)
gst_h264_picture_set_reference (picture, GST_H264_PICTURE_REF_SHORT_TERM); gst_h264_picture_set_reference (picture,
GST_H264_PICTURE_REF_SHORT_TERM, FALSE);
/* This assumes non-interlaced stream */ picture->frame_num = slice_hdr->frame_num;
picture->frame_num = picture->pic_num = slice_hdr->frame_num;
/* 7.4.3 */
if (!slice_hdr->field_pic_flag)
picture->pic_num = slice_hdr->frame_num;
else
picture->pic_num = 2 * slice_hdr->frame_num + 1;
picture->pic_order_cnt_type = sps->pic_order_cnt_type; picture->pic_order_cnt_type = sps->pic_order_cnt_type;
switch (picture->pic_order_cnt_type) { switch (picture->pic_order_cnt_type) {
@ -1411,13 +1533,18 @@ gst_h264_decoder_handle_memory_management_opt (GstH264Decoder * self,
} }
static gboolean static gboolean
gst_h264_decoder_sliding_window_picture_marking (GstH264Decoder * self) gst_h264_decoder_sliding_window_picture_marking (GstH264Decoder * self,
GstH264Picture * picture)
{ {
GstH264DecoderPrivate *priv = self->priv; GstH264DecoderPrivate *priv = self->priv;
const GstH264SPS *sps = priv->active_sps; const GstH264SPS *sps = priv->active_sps;
gint num_ref_pics; gint num_ref_pics;
gint max_num_ref_frames; gint max_num_ref_frames;
/* Skip this for the second field */
if (picture->second_field)
return TRUE;
if (!sps) { if (!sps) {
GST_ERROR_OBJECT (self, "No active sps"); GST_ERROR_OBJECT (self, "No active sps");
return FALSE; return FALSE;
@ -1457,7 +1584,7 @@ gst_h264_decoder_sliding_window_picture_marking (GstH264Decoder * self)
"Unmark reference flag of picture %p (frame_num %d, poc %d)", "Unmark reference flag of picture %p (frame_num %d, poc %d)",
to_unmark, to_unmark->frame_num, to_unmark->pic_order_cnt); to_unmark, to_unmark->frame_num, to_unmark->pic_order_cnt);
gst_h264_picture_set_reference (to_unmark, GST_H264_PICTURE_REF_NONE); gst_h264_picture_set_reference (to_unmark, GST_H264_PICTURE_REF_NONE, TRUE);
gst_h264_picture_unref (to_unmark); gst_h264_picture_unref (to_unmark);
num_ref_pics--; num_ref_pics--;
@ -1482,11 +1609,13 @@ gst_h264_decoder_reference_picture_marking (GstH264Decoder * self,
gst_h264_dpb_mark_all_non_ref (priv->dpb); gst_h264_dpb_mark_all_non_ref (priv->dpb);
if (picture->dec_ref_pic_marking.long_term_reference_flag) { if (picture->dec_ref_pic_marking.long_term_reference_flag) {
gst_h264_picture_set_reference (picture, GST_H264_PICTURE_REF_LONG_TERM); gst_h264_picture_set_reference (picture,
GST_H264_PICTURE_REF_LONG_TERM, FALSE);
picture->long_term_frame_idx = 0; picture->long_term_frame_idx = 0;
priv->max_long_term_frame_idx = 0; priv->max_long_term_frame_idx = 0;
} else { } else {
gst_h264_picture_set_reference (picture, GST_H264_PICTURE_REF_SHORT_TERM); gst_h264_picture_set_reference (picture,
GST_H264_PICTURE_REF_SHORT_TERM, FALSE);
priv->max_long_term_frame_idx = -1; priv->max_long_term_frame_idx = -1;
} }
@ -1507,13 +1636,14 @@ gst_h264_decoder_reference_picture_marking (GstH264Decoder * self,
return gst_h264_decoder_handle_memory_management_opt (self, picture); return gst_h264_decoder_handle_memory_management_opt (self, picture);
} }
return gst_h264_decoder_sliding_window_picture_marking (self); return gst_h264_decoder_sliding_window_picture_marking (self, picture);
} }
static gboolean static gboolean
gst_h264_decoder_finish_picture (GstH264Decoder * self, gst_h264_decoder_finish_picture (GstH264Decoder * self,
GstH264Picture * picture) GstH264Picture * picture)
{ {
GstVideoDecoder *decoder = GST_VIDEO_DECODER (self);
GstH264DecoderPrivate *priv = self->priv; GstH264DecoderPrivate *priv = self->priv;
/* Finish processing the picture. /* Finish processing the picture.
@ -1535,6 +1665,15 @@ gst_h264_decoder_finish_picture (GstH264Decoder * self,
/* Remove unused (for reference or later output) pictures from DPB, marking /* Remove unused (for reference or later output) pictures from DPB, marking
* them as such */ * them as such */
gst_h264_dpb_delete_unused (priv->dpb); gst_h264_dpb_delete_unused (priv->dpb);
/* If this is the second field, drop corresponding frame */
if (picture->second_field) {
GstVideoCodecFrame *frame = gst_video_decoder_get_frame (decoder,
picture->system_frame_number);
gst_video_decoder_release_frame (decoder, frame);
}
gst_h264_dpb_add (priv->dpb, picture); gst_h264_dpb_add (priv->dpb, picture);
GST_LOG_OBJECT (self, GST_LOG_OBJECT (self,
@ -1733,13 +1872,26 @@ gst_h264_decoder_process_sps (GstH264Decoder * self, GstH264SPS * sps)
gint max_dpb_frames; gint max_dpb_frames;
gint max_dpb_size; gint max_dpb_size;
gint prev_max_dpb_size; gint prev_max_dpb_size;
gboolean prev_interlaced;
gboolean interlaced;
if (sps->frame_mbs_only_flag == 0 && !klass->new_field_picture) { if (sps->frame_mbs_only_flag == 0) {
GST_FIXME_OBJECT (self, if (!klass->new_field_picture) {
"frame_mbs_only_flag != 1 not supported by subclass"); GST_FIXME_OBJECT (self,
return FALSE; "frame_mbs_only_flag != 1 not supported by subclass");
return FALSE;
}
if (sps->mb_adaptive_frame_field_flag) {
GST_LOG_OBJECT (self,
"mb_adaptive_frame_field_flag == 1, MBAFF sequence");
} else {
GST_LOG_OBJECT (self, "mb_adaptive_frame_field_flag == 0, PAFF sequence");
}
} }
interlaced = !sps->frame_mbs_only_flag;
/* Spec A.3.1 and A.3.2 /* Spec A.3.1 and A.3.2
* For Baseline, Constrained Baseline and Main profile, the indicated level is * For Baseline, Constrained Baseline and Main profile, the indicated level is
* Level 1b if level_idc is equal to 11 and constraint_set3_flag is equal to 1 * Level 1b if level_idc is equal to 11 and constraint_set3_flag is equal to 1
@ -1782,14 +1934,16 @@ gst_h264_decoder_process_sps (GstH264Decoder * self, GstH264SPS * sps)
g_return_val_if_fail (max_dpb_size <= GST_H264_DPB_MAX_SIZE, FALSE); g_return_val_if_fail (max_dpb_size <= GST_H264_DPB_MAX_SIZE, FALSE);
prev_max_dpb_size = gst_h264_dpb_get_max_num_frames (priv->dpb); prev_max_dpb_size = gst_h264_dpb_get_max_num_frames (priv->dpb);
prev_interlaced = gst_h264_dpb_get_interlaced (priv->dpb);
if (priv->width != sps->width || priv->height != sps->height || if (priv->width != sps->width || priv->height != sps->height ||
prev_max_dpb_size != max_dpb_size) { prev_max_dpb_size != max_dpb_size || prev_interlaced != interlaced) {
GstH264DecoderClass *klass = GST_H264_DECODER_GET_CLASS (self); GstH264DecoderClass *klass = GST_H264_DECODER_GET_CLASS (self);
GST_DEBUG_OBJECT (self, GST_DEBUG_OBJECT (self,
"SPS updated, resolution: %dx%d -> %dx%d, dpb size: %d -> %d", "SPS updated, resolution: %dx%d -> %dx%d, dpb size: %d -> %d, "
"interlaced %d -> %d",
priv->width, priv->height, sps->width, sps->height, priv->width, priv->height, sps->width, sps->height,
prev_max_dpb_size, max_dpb_size); prev_max_dpb_size, max_dpb_size, prev_interlaced, interlaced);
if (gst_h264_decoder_drain (GST_VIDEO_DECODER (self)) != GST_FLOW_OK) if (gst_h264_decoder_drain (GST_VIDEO_DECODER (self)) != GST_FLOW_OK)
return FALSE; return FALSE;
@ -1806,6 +1960,7 @@ gst_h264_decoder_process_sps (GstH264Decoder * self, GstH264SPS * sps)
gst_h264_decoder_set_latency (self, sps, max_dpb_size); gst_h264_decoder_set_latency (self, sps, max_dpb_size);
gst_h264_dpb_set_max_num_frames (priv->dpb, max_dpb_size); gst_h264_dpb_set_max_num_frames (priv->dpb, max_dpb_size);
gst_h264_dpb_set_interlaced (priv->dpb, interlaced);
} }
return gst_h264_decoder_update_max_num_reorder_frames (self, sps); return gst_h264_decoder_update_max_num_reorder_frames (self, sps);

View file

@ -109,6 +109,8 @@ struct _GstH264Dpb
gint max_num_frames; gint max_num_frames;
gint num_output_needed; gint num_output_needed;
gint32 last_output_poc; gint32 last_output_poc;
gboolean interlaced;
}; };
static void static void
@ -175,6 +177,37 @@ gst_h264_dpb_get_max_num_frames (GstH264Dpb * dpb)
return dpb->max_num_frames; return dpb->max_num_frames;
} }
/**
* gst_h264_dpb_set_interlaced:
* @dpb: a #GstH264Dpb
* @interlaced: %TRUE if interlaced
*
* Since: 1.20
*/
void
gst_h264_dpb_set_interlaced (GstH264Dpb * dpb, gboolean interlaced)
{
g_return_if_fail (dpb != NULL);
dpb->interlaced = interlaced;
}
/**
* gst_h264_dpb_get_interlaced:
* @dpb: a #GstH264Dpb
*
* Returns: %TRUE if @dpb is configured for interlaced stream
*
* Since: 1.20
*/
gboolean
gst_h264_dpb_get_interlaced (GstH264Dpb * dpb)
{
g_return_val_if_fail (dpb != NULL, FALSE);
return dpb->interlaced;
}
/** /**
* gst_h264_dpb_free: * gst_h264_dpb_free:
* @dpb: a #GstH264Dpb to free * @dpb: a #GstH264Dpb to free
@ -225,7 +258,19 @@ gst_h264_dpb_add (GstH264Dpb * dpb, GstH264Picture * picture)
* as "not needed for output", and the DPB fullness is incremented by one */ * as "not needed for output", and the DPB fullness is incremented by one */
if (!picture->nonexisting) { if (!picture->nonexisting) {
picture->needed_for_output = TRUE; picture->needed_for_output = TRUE;
dpb->num_output_needed++;
if (picture->field == GST_H264_PICTURE_FIELD_FRAME) {
dpb->num_output_needed++;
} else {
/* We can do output only when field pair are complete */
if (picture->second_field) {
dpb->num_output_needed++;
/* And link each field */
if (picture->other_field)
picture->other_field->other_field = picture;
}
}
} else { } else {
picture->needed_for_output = FALSE; picture->needed_for_output = FALSE;
} }
@ -253,8 +298,9 @@ gst_h264_dpb_delete_unused (GstH264Dpb * dpb)
/* NOTE: don't use g_array_remove_index_fast here since the last picture /* NOTE: don't use g_array_remove_index_fast here since the last picture
* need to be referenced for bumping decision */ * need to be referenced for bumping decision */
if (!picture->needed_for_output && !GST_H264_PICTURE_IS_REF (picture)) { if (!picture->needed_for_output && !GST_H264_PICTURE_IS_REF (picture)) {
GST_TRACE ("remove picture %p (frame num %d) from dpb", GST_TRACE
picture, picture->frame_num); ("remove picture %p (frame num: %d, poc: %d, field: %d) from dpb",
picture, picture->frame_num, picture->pic_order_cnt, picture->field);
g_array_remove_index (dpb->pic_list, i); g_array_remove_index (dpb->pic_list, i);
i--; i--;
} }
@ -281,6 +327,10 @@ gst_h264_dpb_num_ref_frames (GstH264Dpb * dpb)
GstH264Picture *picture = GstH264Picture *picture =
g_array_index (dpb->pic_list, GstH264Picture *, i); g_array_index (dpb->pic_list, GstH264Picture *, i);
/* Count frame, not field picture */
if (picture->second_field)
continue;
if (GST_H264_PICTURE_IS_REF (picture)) if (GST_H264_PICTURE_IS_REF (picture))
ret++; ret++;
} }
@ -305,7 +355,7 @@ gst_h264_dpb_mark_all_non_ref (GstH264Dpb * dpb)
GstH264Picture *picture = GstH264Picture *picture =
g_array_index (dpb->pic_list, GstH264Picture *, i); g_array_index (dpb->pic_list, GstH264Picture *, i);
gst_h264_picture_set_reference (picture, GST_H264_PICTURE_REF_NONE); gst_h264_picture_set_reference (picture, GST_H264_PICTURE_REF_NONE, FALSE);
} }
} }
@ -522,8 +572,26 @@ gst_h264_dpb_get_picture (GstH264Dpb * dpb, guint32 system_frame_number)
static gboolean static gboolean
gst_h264_dpb_has_empty_frame_buffer (GstH264Dpb * dpb) gst_h264_dpb_has_empty_frame_buffer (GstH264Dpb * dpb)
{ {
if (dpb->pic_list->len <= dpb->max_num_frames) if (!dpb->interlaced) {
return TRUE; if (dpb->pic_list->len <= dpb->max_num_frames)
return TRUE;
} else {
gint i;
gint count = 0;
/* Count pictures without second fields */
for (i = 0; i < dpb->pic_list->len; i++) {
GstH264Picture *picture =
g_array_index (dpb->pic_list, GstH264Picture *, i);
if (picture->second_field)
continue;
count++;
}
if (count <= dpb->max_num_frames)
return TRUE;
}
return FALSE; return FALSE;
} }
@ -545,6 +613,10 @@ gst_h264_dpb_get_lowest_output_needed_picture (GstH264Dpb * dpb,
if (!picture->needed_for_output) if (!picture->needed_for_output)
continue; continue;
if (picture->field != GST_H264_PICTURE_FIELD_FRAME &&
(!picture->other_field || picture->second_field))
continue;
if (!lowest) { if (!lowest) {
lowest = picture; lowest = picture;
index = i; index = i;
@ -666,6 +738,8 @@ GstH264Picture *
gst_h264_dpb_bump (GstH264Dpb * dpb, gboolean drain) gst_h264_dpb_bump (GstH264Dpb * dpb, gboolean drain)
{ {
GstH264Picture *picture; GstH264Picture *picture;
GstH264Picture *other_picture;
gint i;
gint index; gint index;
g_return_val_if_fail (dpb != NULL, NULL); g_return_val_if_fail (dpb != NULL, NULL);
@ -685,6 +759,32 @@ gst_h264_dpb_bump (GstH264Dpb * dpb, gboolean drain)
if (!GST_H264_PICTURE_IS_REF (picture) || drain) if (!GST_H264_PICTURE_IS_REF (picture) || drain)
g_array_remove_index (dpb->pic_list, index); g_array_remove_index (dpb->pic_list, index);
other_picture = picture->other_field;
if (other_picture) {
other_picture->needed_for_output = FALSE;
/* At this moment, this picture should be interlaced */
picture->buffer_flags |= GST_VIDEO_BUFFER_FLAG_INTERLACED;
/* FIXME: need to check picture timing SEI for the case where top/bottom poc
* are identical */
if (picture->pic_order_cnt < other_picture->pic_order_cnt)
picture->buffer_flags |= GST_VIDEO_BUFFER_FLAG_TFF;
if (!other_picture->ref) {
for (i = 0; i < dpb->pic_list->len; i++) {
GstH264Picture *tmp =
g_array_index (dpb->pic_list, GstH264Picture *, i);
if (tmp == other_picture) {
g_array_remove_index (dpb->pic_list, i);
break;
}
}
}
/* Now other field may or may not exist */
}
dpb->last_output_poc = picture->pic_order_cnt; dpb->last_output_poc = picture->pic_order_cnt;
return picture; return picture;
@ -693,7 +793,6 @@ gst_h264_dpb_bump (GstH264Dpb * dpb, gboolean drain)
static gint static gint
get_picNumX (GstH264Picture * picture, GstH264RefPicMarking * ref_pic_marking) get_picNumX (GstH264Picture * picture, GstH264RefPicMarking * ref_pic_marking)
{ {
/* FIXME: support interlaced */
return picture->pic_num - return picture->pic_num -
(ref_pic_marking->difference_of_pic_nums_minus1 + 1); (ref_pic_marking->difference_of_pic_nums_minus1 + 1);
} }
@ -736,7 +835,8 @@ gst_h264_dpb_perform_memory_management_control_operation (GstH264Dpb * dpb,
pic_num_x = get_picNumX (picture, ref_pic_marking); pic_num_x = get_picNumX (picture, ref_pic_marking);
other = gst_h264_dpb_get_short_ref_by_pic_num (dpb, pic_num_x); other = gst_h264_dpb_get_short_ref_by_pic_num (dpb, pic_num_x);
if (other) { if (other) {
gst_h264_picture_set_reference (other, GST_H264_PICTURE_REF_NONE); gst_h264_picture_set_reference (other,
GST_H264_PICTURE_REF_NONE, FALSE);
GST_TRACE ("MMCO-1: unmark short-term ref picture %p, (poc %d)", GST_TRACE ("MMCO-1: unmark short-term ref picture %p, (poc %d)",
other, other->pic_order_cnt); other, other->pic_order_cnt);
} else { } else {
@ -750,7 +850,8 @@ gst_h264_dpb_perform_memory_management_control_operation (GstH264Dpb * dpb,
other = gst_h264_dpb_get_long_ref_by_long_term_pic_num (dpb, other = gst_h264_dpb_get_long_ref_by_long_term_pic_num (dpb,
ref_pic_marking->long_term_pic_num); ref_pic_marking->long_term_pic_num);
if (other) { if (other) {
gst_h264_picture_set_reference (other, GST_H264_PICTURE_REF_NONE); gst_h264_picture_set_reference (other,
GST_H264_PICTURE_REF_NONE, FALSE);
GST_TRACE ("MMCO-2: unmark long-term ref picture %p, (poc %d)", GST_TRACE ("MMCO-2: unmark long-term ref picture %p, (poc %d)",
other, other->pic_order_cnt); other, other->pic_order_cnt);
} else { } else {
@ -770,7 +871,8 @@ gst_h264_dpb_perform_memory_management_control_operation (GstH264Dpb * dpb,
if (GST_H264_PICTURE_IS_LONG_TERM_REF (other) if (GST_H264_PICTURE_IS_LONG_TERM_REF (other)
&& other->long_term_frame_idx == && other->long_term_frame_idx ==
ref_pic_marking->long_term_frame_idx) { ref_pic_marking->long_term_frame_idx) {
gst_h264_picture_set_reference (other, GST_H264_PICTURE_REF_NONE); gst_h264_picture_set_reference (other,
GST_H264_PICTURE_REF_NONE, TRUE);
GST_TRACE ("MMCO-3: unmark old long-term ref pic %p (poc %d)", GST_TRACE ("MMCO-3: unmark old long-term ref pic %p (poc %d)",
other, other->pic_order_cnt); other, other->pic_order_cnt);
break; break;
@ -780,10 +882,17 @@ gst_h264_dpb_perform_memory_management_control_operation (GstH264Dpb * dpb,
pic_num_x = get_picNumX (picture, ref_pic_marking); pic_num_x = get_picNumX (picture, ref_pic_marking);
other = gst_h264_dpb_get_short_ref_by_pic_num (dpb, pic_num_x); other = gst_h264_dpb_get_short_ref_by_pic_num (dpb, pic_num_x);
if (other) { if (other) {
gst_h264_picture_set_reference (other, GST_H264_PICTURE_REF_LONG_TERM); gst_h264_picture_set_reference (other,
GST_H264_PICTURE_REF_LONG_TERM, picture->second_field);
other->long_term_frame_idx = ref_pic_marking->long_term_frame_idx; other->long_term_frame_idx = ref_pic_marking->long_term_frame_idx;
GST_TRACE ("MMCO-3: mark long-term ref pic %p, index %d, (poc %d)", GST_TRACE ("MMCO-3: mark long-term ref pic %p, index %d, (poc %d)",
other, other->long_term_frame_idx, other->pic_order_cnt); other, other->long_term_frame_idx, other->pic_order_cnt);
if (picture->other_field &&
GST_H264_PICTURE_IS_LONG_TERM_REF (picture->other_field)) {
picture->other_field->long_term_frame_idx =
ref_pic_marking->long_term_frame_idx;
}
} else { } else {
GST_WARNING ("Invalid picNumX %d for operation type 3", pic_num_x); GST_WARNING ("Invalid picNumX %d for operation type 3", pic_num_x);
return FALSE; return FALSE;
@ -803,7 +912,8 @@ gst_h264_dpb_perform_memory_management_control_operation (GstH264Dpb * dpb,
if (GST_H264_PICTURE_IS_LONG_TERM_REF (other) && if (GST_H264_PICTURE_IS_LONG_TERM_REF (other) &&
other->long_term_frame_idx > max_long_term_frame_idx) { other->long_term_frame_idx > max_long_term_frame_idx) {
gst_h264_picture_set_reference (other, GST_H264_PICTURE_REF_NONE); gst_h264_picture_set_reference (other,
GST_H264_PICTURE_REF_NONE, FALSE);
GST_TRACE ("MMCO-4: unmark long-term ref pic %p, index %d, (poc %d)", GST_TRACE ("MMCO-4: unmark long-term ref pic %p, index %d, (poc %d)",
other, other->long_term_frame_idx, other->pic_order_cnt); other, other->long_term_frame_idx, other->pic_order_cnt);
} }
@ -813,7 +923,8 @@ gst_h264_dpb_perform_memory_management_control_operation (GstH264Dpb * dpb,
/* 8.2.5.4.5 Unmark all reference pictures */ /* 8.2.5.4.5 Unmark all reference pictures */
for (i = 0; i < dpb->pic_list->len; i++) { for (i = 0; i < dpb->pic_list->len; i++) {
other = g_array_index (dpb->pic_list, GstH264Picture *, i); other = g_array_index (dpb->pic_list, GstH264Picture *, i);
gst_h264_picture_set_reference (other, GST_H264_PICTURE_REF_NONE); gst_h264_picture_set_reference (other,
GST_H264_PICTURE_REF_NONE, FALSE);
} }
picture->mem_mgmt_5 = TRUE; picture->mem_mgmt_5 = TRUE;
picture->frame_num = 0; picture->frame_num = 0;
@ -832,13 +943,20 @@ gst_h264_dpb_perform_memory_management_control_operation (GstH264Dpb * dpb,
ref_pic_marking->long_term_frame_idx) { ref_pic_marking->long_term_frame_idx) {
GST_TRACE ("MMCO-6: unmark old long-term ref pic %p (poc %d)", GST_TRACE ("MMCO-6: unmark old long-term ref pic %p (poc %d)",
other, other->pic_order_cnt); other, other->pic_order_cnt);
gst_h264_picture_set_reference (other, GST_H264_PICTURE_REF_NONE); gst_h264_picture_set_reference (other,
GST_H264_PICTURE_REF_NONE, TRUE);
break; break;
} }
} }
gst_h264_picture_set_reference (picture, GST_H264_PICTURE_REF_LONG_TERM); gst_h264_picture_set_reference (picture,
GST_H264_PICTURE_REF_LONG_TERM, picture->second_field);
picture->long_term_frame_idx = ref_pic_marking->long_term_frame_idx; picture->long_term_frame_idx = ref_pic_marking->long_term_frame_idx;
if (picture->other_field &&
GST_H264_PICTURE_IS_LONG_TERM_REF (picture->other_field)) {
picture->other_field->long_term_frame_idx =
ref_pic_marking->long_term_frame_idx;
}
break; break;
default: default:
g_assert_not_reached (); g_assert_not_reached ();
@ -852,6 +970,8 @@ gst_h264_dpb_perform_memory_management_control_operation (GstH264Dpb * dpb,
* gst_h264_picture_set_reference: * gst_h264_picture_set_reference:
* @picture: a #GstH264Picture * @picture: a #GstH264Picture
* @reference: a GstH264PictureReference * @reference: a GstH264PictureReference
* @other_field: %TRUE if @reference needs to be applied to the
* other field if any
* *
* Update reference picture type of @picture with @reference * Update reference picture type of @picture with @reference
* *
@ -859,9 +979,12 @@ gst_h264_dpb_perform_memory_management_control_operation (GstH264Dpb * dpb,
*/ */
void void
gst_h264_picture_set_reference (GstH264Picture * picture, gst_h264_picture_set_reference (GstH264Picture * picture,
GstH264PictureReference reference) GstH264PictureReference reference, gboolean other_field)
{ {
g_return_if_fail (picture != NULL); g_return_if_fail (picture != NULL);
picture->ref = reference; picture->ref = reference;
if (other_field && picture->other_field)
picture->other_field->ref = reference;
} }

View file

@ -21,8 +21,8 @@
#define __GST_H264_PICTURE_H__ #define __GST_H264_PICTURE_H__
#include <gst/codecs/codecs-prelude.h> #include <gst/codecs/codecs-prelude.h>
#include <gst/codecparsers/gsth264parser.h> #include <gst/codecparsers/gsth264parser.h>
#include <gst/video/video.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -141,6 +141,12 @@ struct _GstH264Picture
GstH264DecRefPicMarking dec_ref_pic_marking; GstH264DecRefPicMarking dec_ref_pic_marking;
/* For interlaced decoding */
gboolean second_field;
GstH264Picture * other_field;
GstVideoBufferFlags buffer_flags;
gpointer user_data; gpointer user_data;
GDestroyNotify notify; GDestroyNotify notify;
}; };
@ -203,6 +209,13 @@ void gst_h264_dpb_set_max_num_frames (GstH264Dpb * dpb,
GST_CODECS_API GST_CODECS_API
gint gst_h264_dpb_get_max_num_frames (GstH264Dpb * dpb); gint gst_h264_dpb_get_max_num_frames (GstH264Dpb * dpb);
GST_CODECS_API
void gst_h264_dpb_set_interlaced (GstH264Dpb * dpb,
gboolean interlaced);
GST_CODECS_API
gboolean gst_h264_dpb_get_interlaced (GstH264Dpb * dpb);
GST_CODECS_API GST_CODECS_API
void gst_h264_dpb_free (GstH264Dpb * dpb); void gst_h264_dpb_free (GstH264Dpb * dpb);
@ -267,7 +280,8 @@ gboolean gst_h264_dpb_perform_memory_management_control_operation (GstH2
/* Internal methods */ /* Internal methods */
void gst_h264_picture_set_reference (GstH264Picture * picture, void gst_h264_picture_set_reference (GstH264Picture * picture,
GstH264PictureReference reference); GstH264PictureReference reference,
gboolean other_field);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstH264Picture, gst_h264_picture_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstH264Picture, gst_h264_picture_unref)