avviddec, video.c, h265parse: Workaround for broken field-based interlaced encoders

Some encoders (e.g. Makito) have H265 field-based interlacing, but then
also specify an 1:2 pixel aspect ratio. That makes it kind-of work with
decoders that don't properly support field-based decoding, but makes us
end up with the wrong aspect ratio if we implement everything properly.
As a workaround, detect 1:2 pixel aspect ratio for field-based
interlacing, and check if making that 1:1 would make the new display
aspect ratio common. In that case, we override it with 1:1.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2577>
This commit is contained in:
Vivia Nikolaidou 2022-06-08 19:18:48 +03:00 committed by GStreamer Marge Bot
parent 936143599c
commit b90d02741e
4 changed files with 85 additions and 5 deletions

View file

@ -1101,6 +1101,8 @@ gst_ffmpegviddec_update_par (GstFFMpegVidDec * ffmpegdec,
gboolean decoder_par_set = FALSE;
gint demuxer_num = 1, demuxer_denom = 1;
gint decoder_num = 1, decoder_denom = 1;
GstVideoCodecFrame *out_frame;
GstFFMpegVidDecVideoFrame *out_dframe;
if (in_info->par_n && in_info->par_d) {
demuxer_num = in_info->par_n;
@ -1127,14 +1129,38 @@ gst_ffmpegviddec_update_par (GstFFMpegVidDec * ffmpegdec,
if (decoder_par_set && !demuxer_par_set)
goto use_decoder_par;
/* Special case for some encoders which provide an 1:2 pixel aspect ratio
* for HEVC interlaced content, possibly to work around decoders that don't
* support field-based interlacing. Add some defensive checks to check for
* a "common" aspect ratio. */
out_dframe = ffmpegdec->picture->opaque;
out_frame = out_dframe->frame;
if (demuxer_num == 1 && demuxer_denom == 1 &&
decoder_num == 1 && decoder_denom == 2 &&
GST_BUFFER_FLAG_IS_SET (out_frame->input_buffer,
GST_VIDEO_BUFFER_FLAG_ONEFIELD) &&
gst_video_is_common_aspect_ratio (ffmpegdec->pic_width,
ffmpegdec->pic_height, 1, 2) &&
!gst_video_is_common_aspect_ratio (ffmpegdec->pic_width,
ffmpegdec->pic_height, 1, 1)) {
GST_WARNING_OBJECT (ffmpegdec,
"PAR 1/2 makes the aspect ratio of "
"a %d x %d frame uncommon. Switching to 1/1",
ffmpegdec->pic_width, ffmpegdec->pic_height);
goto use_demuxer_par;
}
/* Both the demuxer and the decoder provide a PAR. If one of
* the two PARs is 1:1 and the other one is not, use the one
* that is not 1:1. */
if (demuxer_num == demuxer_denom && decoder_num != decoder_denom)
if (demuxer_num == demuxer_denom && decoder_num != decoder_denom) {
goto use_decoder_par;
}
if (decoder_num == decoder_denom && demuxer_num != demuxer_denom)
if (decoder_num == decoder_denom && demuxer_num != demuxer_denom) {
goto use_demuxer_par;
}
/* Both PARs are non-1:1, so use the PAR provided by the demuxer */
goto use_demuxer_par;

View file

@ -2086,6 +2086,7 @@ gst_h265_parse_update_src_caps (GstH265Parse * h265parse, GstCaps * caps)
gboolean modified = FALSE;
GstBuffer *buf = NULL;
GstStructure *s = NULL;
gint width, height;
if (G_UNLIKELY (!gst_pad_has_current_caps (GST_BASE_PARSE_SRC_PAD
(h265parse))))
@ -2242,7 +2243,6 @@ gst_h265_parse_update_src_caps (GstH265Parse * h265parse, GstCaps * caps)
if (G_UNLIKELY (modified || h265parse->update_caps)) {
gint fps_num = h265parse->fps_num;
gint fps_den = h265parse->fps_den;
gint width, height;
GstClockTime latency = 0;
caps = gst_caps_copy (sink_caps);
@ -2345,9 +2345,22 @@ gst_h265_parse_update_src_caps (GstH265Parse * h265parse, GstCaps * caps)
gst_h265_parse_get_par (h265parse, &par_n, &par_d);
if (par_n != 0 && par_d != 0 &&
(!s || !gst_structure_has_field (s, "pixel-aspect-ratio"))) {
GST_INFO_OBJECT (h265parse, "PAR %d/%d", par_n, par_d);
gint new_par_d = par_d;
/* Special case for some encoders which provide an 1:2 pixel aspect ratio
* for HEVC interlaced content, possibly to work around decoders that don't
* support field-based interlacing. Add some defensive checks to check for
* a "common" aspect ratio. */
if (par_n == 1 && par_d == 2
&& gst_h265_parse_is_field_interlaced (h265parse)
&& !gst_video_is_common_aspect_ratio (width, height, par_n, par_d)
&& gst_video_is_common_aspect_ratio (width, height, 1, 1)) {
GST_WARNING_OBJECT (h265parse, "PAR 1/2 makes the aspect ratio of "
"a %d x %d frame uncommon. Switching to 1/1", width, height);
new_par_d = 1;
}
GST_INFO_OBJECT (h265parse, "PAR %d/%d", par_n, new_par_d);
gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
par_n, par_d, NULL);
par_n, new_par_d, NULL);
}
/* set profile and level in caps */

View file

@ -183,6 +183,44 @@ gst_video_guess_framerate (GstClockTime duration, gint * dest_n, gint * dest_d)
return (best_error != G_MAXUINT64);
}
/**
* gst_video_is_common_aspect_ratio:
* @width: Width of the video frame
* @height: Height of the video frame
* @par_n: Pixel aspect ratio numerator
* @par_d: Pixel aspect ratio denominator
*
* Given a frame's dimensions and pixel aspect ratio, this function will
* calculate the frame's aspect ratio and compare it against a set of
* common well-known "standard" aspect ratios.
*
* Returns: %TRUE if a known "standard" aspect ratio was
* recognised, and %FALSE otherwise.
*
* Since: 1.22
*/
gboolean
gst_video_is_common_aspect_ratio (gint width, gint height, gint par_n,
gint par_d)
{
gint dar_n, dar_d;
gst_util_fraction_multiply (width, height, par_n, par_d, &dar_n, &dar_d);
if (dar_n == 16 && dar_d == 9)
return TRUE;
if (dar_n == 4 && dar_d == 3)
return TRUE;
if (dar_n == 14 && dar_d == 9)
return TRUE;
if (dar_n == 8 && dar_d == 5)
return TRUE;
if (dar_n == 21 && dar_d == 11)
return TRUE;
return FALSE;
}
/**
* gst_video_alignment_reset:

View file

@ -149,6 +149,9 @@ GST_VIDEO_API
gboolean gst_video_guess_framerate (GstClockTime duration,
gint * dest_n, gint * dest_d);
GST_VIDEO_API
gboolean gst_video_is_common_aspect_ratio (gint width, gint height, gint par_n, gint par_d);
/* convert/encode video sample from one format to another */
typedef void (*GstVideoConvertSampleCallback) (GstSample * sample, GError *error, gpointer user_data);