h264parse: Validate VUI framerate

A few streams contain invalid/wrong framerate in VUI. Do validate
it based on the spec and ignore invalid VUI framerate values.

Fixes: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1753
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4127>
This commit is contained in:
Seungha Yang 2023-03-07 23:46:50 +09:00 committed by GStreamer Marge Bot
parent be72fbc334
commit ad30fb87c2
2 changed files with 107 additions and 0 deletions

View file

@ -276,6 +276,7 @@ gst_h264_parse_reset_stream_info (GstH264Parse * h264parse)
h264parse->packetized = FALSE;
h264parse->push_codec = FALSE;
h264parse->first_frame = TRUE;
h264parse->ignore_vui_fps = FALSE;
gst_buffer_replace (&h264parse->codec_data, NULL);
gst_buffer_replace (&h264parse->codec_data_in, NULL);
@ -2039,6 +2040,82 @@ get_level_string (GstH264SPS * sps)
}
}
typedef enum
{
GST_H264_LEVEL_L1 = 10,
GST_H264_LEVEL_L1B = 9,
GST_H264_LEVEL_L1_1 = 11,
GST_H264_LEVEL_L1_2 = 12,
GST_H264_LEVEL_L1_3 = 13,
GST_H264_LEVEL_L2_0 = 20,
GST_H264_LEVEL_L2_1 = 21,
GST_H264_LEVEL_L2_2 = 22,
GST_H264_LEVEL_L3 = 30,
GST_H264_LEVEL_L3_1 = 31,
GST_H264_LEVEL_L3_2 = 32,
GST_H264_LEVEL_L4 = 40,
GST_H264_LEVEL_L4_1 = 41,
GST_H264_LEVEL_L4_2 = 42,
GST_H264_LEVEL_L5 = 50,
GST_H264_LEVEL_L5_1 = 51,
GST_H264_LEVEL_L5_2 = 52,
GST_H264_LEVEL_L6 = 60,
GST_H264_LEVEL_L6_1 = 61,
GST_H264_LEVEL_L6_2 = 62,
} GstH264Level;
typedef struct
{
GstH264Level level;
guint max_sample_per_sec;
} GstH264LevelLimit;
static const GstH264LevelLimit level_limits_map[] = {
{GST_H264_LEVEL_L1, 380160},
{GST_H264_LEVEL_L1B, 380160},
{GST_H264_LEVEL_L1_1, 768000},
{GST_H264_LEVEL_L1_2, 1536000},
{GST_H264_LEVEL_L1_3, 3041280},
{GST_H264_LEVEL_L2_0, 3041280},
{GST_H264_LEVEL_L2_1, 5068800},
{GST_H264_LEVEL_L2_2, 5184000},
{GST_H264_LEVEL_L3, 10368000},
{GST_H264_LEVEL_L3_1, 27648000},
{GST_H264_LEVEL_L3_2, 55296000},
{GST_H264_LEVEL_L4, 62914560},
{GST_H264_LEVEL_L4_1, 62914560},
{GST_H264_LEVEL_L4_2, 62914560},
{GST_H264_LEVEL_L5, 150994994},
{GST_H264_LEVEL_L5_1, 251658240},
{GST_H264_LEVEL_L5_2, 530841600},
{GST_H264_LEVEL_L6, 1069547520},
{GST_H264_LEVEL_L6_1, 2139095040},
{GST_H264_LEVEL_L6_2, 4278190080},
};
/* A.3.4 Effect of level limits on frame rate (informative) */
static guint
get_max_samples_per_second (const GstH264SPS * sps)
{
guint i;
guint n_levels = G_N_ELEMENTS (level_limits_map);
GstH264Level level = (GstH264Level) sps->level_idc;
if (level == GST_H264_LEVEL_L1_1 &&
(sps->profile_idc == 66 || sps->profile_idc == 77) &&
sps->constraint_set3_flag) {
/* Level 1b */
level = GST_H264_LEVEL_L1B;
}
for (i = 0; i < n_levels; i++) {
if (level == level_limits_map[i].level)
return level_limits_map[i].max_sample_per_sec;
}
return level_limits_map[n_levels - 1].max_sample_per_sec;
}
static void
gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps)
{
@ -2122,6 +2199,32 @@ gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps)
* it in case we have no info */
gst_h264_video_calculate_framerate (sps, h264parse->field_pic_flag,
h264parse->sei_pic_struct, &fps_num, &fps_den);
/* Checks whether given framerate makes sense or not
* See also A.3.4 Effect of level limits on frame rate (informative)
*/
h264parse->ignore_vui_fps = FALSE;
if (fps_num > 0 && fps_den > 0 && sps->width > 0 && sps->height > 0 &&
sps->vui_parameters_present_flag &&
sps->vui_parameters.timing_info_present_flag) {
guint luma_samples = sps->width * sps->height;
guint max_samples = get_max_samples_per_second (sps);
gdouble max_fps, cur_fps;
cur_fps = (gdouble) fps_num / fps_den;
max_fps = (gdouble) max_samples / luma_samples;
/* XXX: allows up to 2x higher framerate */
if (max_fps * 2 < cur_fps) {
GST_WARNING_OBJECT (h264parse,
"VUI framerate %.1f exceeds allowed maximum %.1f",
cur_fps, max_fps);
fps_num = 0;
fps_den = 1;
h264parse->ignore_vui_fps = TRUE;
}
}
if (G_UNLIKELY (h264parse->fps_num != fps_num
|| h264parse->fps_den != fps_den)) {
GST_DEBUG_OBJECT (h264parse, "framerate changed %d/%d", fps_num, fps_den);
@ -2461,6 +2564,9 @@ gst_h264_parse_get_duration (GstH264Parse * h264parse, gboolean frame)
if (!sps) {
GST_DEBUG_OBJECT (h264parse, "referred SPS invalid");
goto fps_duration;
} else if (h264parse->ignore_vui_fps) {
GST_DEBUG_OBJECT (h264parse, "VUI framerate is not reliable");
goto fps_duration;
} else if (!sps->vui_parameters_present_flag) {
GST_DEBUG_OBJECT (h264parse, "unable to compute duration: VUI not present");
goto fps_duration;

View file

@ -117,6 +117,7 @@ struct _GstH264Parse
guint8 sei_pic_struct;
guint8 sei_pic_struct_pres_flag;
guint field_pic_flag;
gboolean ignore_vui_fps;
/* cached timestamps */
/* (trying to) track upstream dts and interpolate */