Revert 'Fix interlace check for H.264 MBAFF coded MP4 files' (#6222)

This commit is contained in:
Bond_009 2023-01-19 14:19:56 +01:00
parent d57dcf2245
commit 75fe640f2b
5 changed files with 380 additions and 35 deletions

View File

@ -625,17 +625,6 @@ namespace MediaBrowser.MediaEncoding.Probing
return attachment; return attachment;
} }
/// <summary>
/// Determines whether a stream code time base is double the frame rate.
/// </summary>
/// <param name="averageFrameRate">average frame rate.</param>
/// <param name="codecTimeBase">codec time base string.</param>
/// <returns>true if the codec time base is double the frame rate.</returns>
internal static bool IsCodecTimeBaseDoubleTheFrameRate(float? averageFrameRate, string codecTimeBase)
{
return MathF.Abs(((averageFrameRate ?? 0) * (GetFrameRate(codecTimeBase) ?? 0)) - 0.5f) <= float.Epsilon;
}
/// <summary> /// <summary>
/// Converts ffprobe stream info to our MediaStream class. /// Converts ffprobe stream info to our MediaStream class.
/// </summary> /// </summary>
@ -748,22 +737,9 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.AverageFrameRate = GetFrameRate(streamInfo.AverageFrameRate); stream.AverageFrameRate = GetFrameRate(streamInfo.AverageFrameRate);
stream.RealFrameRate = GetFrameRate(streamInfo.RFrameRate); stream.RealFrameRate = GetFrameRate(streamInfo.RFrameRate);
bool videoInterlaced = !string.IsNullOrWhiteSpace(streamInfo.FieldOrder) stream.IsInterlaced = !string.IsNullOrWhiteSpace(streamInfo.FieldOrder)
&& !string.Equals(streamInfo.FieldOrder, "progressive", StringComparison.OrdinalIgnoreCase); && !string.Equals(streamInfo.FieldOrder, "progressive", StringComparison.OrdinalIgnoreCase);
// Some interlaced H.264 files in mp4 containers using MBAFF coding aren't flagged as being interlaced by FFprobe,
// so for H.264 files we also calculate the frame rate from the codec time base and check if it is double the reported
// frame rate to determine if the file is interlaced
bool h264MbaffCoded = string.Equals(stream.Codec, "h264", StringComparison.OrdinalIgnoreCase)
&& string.IsNullOrWhiteSpace(streamInfo.FieldOrder)
&& IsCodecTimeBaseDoubleTheFrameRate(stream.AverageFrameRate, stream.CodecTimeBase);
if (videoInterlaced || h264MbaffCoded)
{
stream.IsInterlaced = true;
}
if (isAudio if (isAudio
|| string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase) || string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)
|| string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase) || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase)

View File

@ -31,16 +31,6 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
public void GetFrameRate_Success(string value, float? expected) public void GetFrameRate_Success(string value, float? expected)
=> Assert.Equal(expected, ProbeResultNormalizer.GetFrameRate(value)); => Assert.Equal(expected, ProbeResultNormalizer.GetFrameRate(value));
[Theory]
[InlineData(0.5f, "0/1", false)]
[InlineData(24.5f, "8/196", false)]
[InlineData(63.5f, "1/127", true)]
[InlineData(null, "1/60", false)]
[InlineData(30f, "2/120", true)]
[InlineData(59.999996f, "1563/187560", true)]
public void IsCodecTimeBaseDoubleTheFrameRate_Success(float? frameRate, string codecTimeBase, bool expected)
=> Assert.Equal(expected, ProbeResultNormalizer.IsCodecTimeBaseDoubleTheFrameRate(frameRate, codecTimeBase));
[Fact] [Fact]
public void GetMediaInfo_MetaData_Success() public void GetMediaInfo_MetaData_Success()
{ {
@ -158,6 +148,99 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
Assert.False(res.MediaStreams[5].IsHearingImpaired); Assert.False(res.MediaStreams[5].IsHearingImpaired);
} }
[Fact]
public void GetMediaInfo_ProgressiveVideoNoFieldOrder_Success()
{
var bytes = File.ReadAllBytes("Test Data/Probing/video_progressive_no_field_order.json");
var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_progressive_no_field_order.mp4", MediaProtocol.File);
Assert.Equal(2, res.MediaStreams.Count);
Assert.NotNull(res.VideoStream);
Assert.Equal(res.MediaStreams[0], res.VideoStream);
Assert.Equal(0, res.VideoStream.Index);
Assert.Equal("h264", res.VideoStream.Codec);
Assert.Equal("Main", res.VideoStream.Profile);
Assert.Equal(MediaStreamType.Video, res.VideoStream.Type);
Assert.Equal(1080, res.VideoStream.Height);
Assert.Equal(1920, res.VideoStream.Width);
Assert.False(res.VideoStream.IsInterlaced);
Assert.Equal("16:9", res.VideoStream.AspectRatio);
Assert.Equal("yuv420p", res.VideoStream.PixelFormat);
Assert.Equal(41d, res.VideoStream.Level);
Assert.Equal(1, res.VideoStream.RefFrames);
Assert.True(res.VideoStream.IsAVC);
Assert.Equal(23.9760246f, res.VideoStream.RealFrameRate);
Assert.Equal("1/24000", res.VideoStream.TimeBase);
Assert.Equal(3948341, res.VideoStream.BitRate);
Assert.Equal(8, res.VideoStream.BitDepth);
Assert.True(res.VideoStream.IsDefault);
}
[Fact]
public void GetMediaInfo_ProgressiveVideoNoFieldOrder2_Success()
{
var bytes = File.ReadAllBytes("Test Data/Probing/video_progressive_no_field_order2.json");
var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_progressive_no_field_order2.mp4", MediaProtocol.File);
Assert.Single(res.MediaStreams);
Assert.NotNull(res.VideoStream);
Assert.Equal(res.MediaStreams[0], res.VideoStream);
Assert.Equal(0, res.VideoStream.Index);
Assert.Equal("h264", res.VideoStream.Codec);
Assert.Equal("High", res.VideoStream.Profile);
Assert.Equal(MediaStreamType.Video, res.VideoStream.Type);
Assert.Equal(720, res.VideoStream.Height);
Assert.Equal(1280, res.VideoStream.Width);
Assert.False(res.VideoStream.IsInterlaced);
Assert.Equal("16:9", res.VideoStream.AspectRatio);
Assert.Equal("yuv420p", res.VideoStream.PixelFormat);
Assert.Equal(31d, res.VideoStream.Level);
Assert.Equal(1, res.VideoStream.RefFrames);
Assert.True(res.VideoStream.IsAVC);
Assert.Equal(25f, res.VideoStream.RealFrameRate);
Assert.Equal("1/12800", res.VideoStream.TimeBase);
Assert.Equal(53288, res.VideoStream.BitRate);
Assert.Equal(8, res.VideoStream.BitDepth);
Assert.True(res.VideoStream.IsDefault);
}
[Fact]
public void GetMediaInfo_InterlacedVideo_Success()
{
var bytes = File.ReadAllBytes("Test Data/Probing/video_interlaced.json");
var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_interlaced.mp4", MediaProtocol.File);
Assert.Single(res.MediaStreams);
Assert.NotNull(res.VideoStream);
Assert.Equal(res.MediaStreams[0], res.VideoStream);
Assert.Equal(0, res.VideoStream.Index);
Assert.Equal("h264", res.VideoStream.Codec);
Assert.Equal("High", res.VideoStream.Profile);
Assert.Equal(MediaStreamType.Video, res.VideoStream.Type);
Assert.Equal(720, res.VideoStream.Height);
Assert.Equal(1280, res.VideoStream.Width);
Assert.True(res.VideoStream.IsInterlaced);
Assert.Equal("16:9", res.VideoStream.AspectRatio);
Assert.Equal("yuv420p", res.VideoStream.PixelFormat);
Assert.Equal(40d, res.VideoStream.Level);
Assert.Equal(1, res.VideoStream.RefFrames);
Assert.True(res.VideoStream.IsAVC);
Assert.Equal(25f, res.VideoStream.RealFrameRate);
Assert.Equal("1/12800", res.VideoStream.TimeBase);
Assert.Equal(56945, res.VideoStream.BitRate);
Assert.Equal(8, res.VideoStream.BitDepth);
Assert.True(res.VideoStream.IsDefault);
}
[Fact] [Fact]
public void GetMediaInfo_MusicVideo_Success() public void GetMediaInfo_MusicVideo_Success()
{ {

View File

@ -0,0 +1,81 @@
{
"streams": [
{
"index": 0,
"codec_name": "h264",
"codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
"profile": "High",
"codec_type": "video",
"codec_tag_string": "avc1",
"codec_tag": "0x31637661",
"width": 1280,
"height": 720,
"coded_width": 1280,
"coded_height": 720,
"closed_captions": 0,
"film_grain": 0,
"has_b_frames": 2,
"pix_fmt": "yuv420p",
"level": 40,
"chroma_location": "left",
"field_order": "tt",
"refs": 1,
"is_avc": "true",
"nal_length_size": "4",
"id": "0x1",
"r_frame_rate": "25/1",
"avg_frame_rate": "25/1",
"time_base": "1/12800",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 3840000,
"duration": "300.000000",
"bit_rate": "56945",
"bits_per_raw_sample": "8",
"nb_frames": "7500",
"extradata_size": 42,
"disposition": {
"default": 1,
"dub": 0,
"original": 0,
"comment": 0,
"lyrics": 0,
"karaoke": 0,
"forced": 0,
"hearing_impaired": 0,
"visual_impaired": 0,
"clean_effects": 0,
"attached_pic": 0,
"timed_thumbnails": 0,
"captions": 0,
"descriptions": 0,
"metadata": 0,
"dependent": 0,
"still_image": 0
},
"tags": {
"language": "und",
"handler_name": "VideoHandler",
"vendor_id": "[0][0][0][0]"
}
}
],
"format": {
"filename": "test-gray.720i.mp4",
"nb_streams": 1,
"nb_programs": 0,
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
"format_long_name": "QuickTime / MOV",
"start_time": "0.000000",
"duration": "300.000000",
"size": "2223957",
"bit_rate": "59305",
"probe_score": 100,
"tags": {
"major_brand": "isom",
"minor_version": "512",
"compatible_brands": "isomiso2avc1mp41",
"encoder": "Lavf58.20.100"
}
}
}

View File

@ -0,0 +1,133 @@
{
"streams": [
{
"index": 0,
"codec_name": "h264",
"codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
"profile": "Main",
"codec_type": "video",
"codec_time_base": "1001/48000",
"codec_tag_string": "avc1",
"codec_tag": "0x31637661",
"width": 1920,
"height": 1080,
"coded_width": 1920,
"coded_height": 1088,
"closed_captions": 0,
"has_b_frames": 1,
"sample_aspect_ratio": "1:1",
"display_aspect_ratio": "16:9",
"pix_fmt": "yuv420p",
"level": 41,
"chroma_location": "left",
"refs": 1,
"is_avc": "true",
"nal_length_size": "4",
"r_frame_rate": "24000/1001",
"avg_frame_rate": "24000/1001",
"time_base": "1/24000",
"start_pts": 1000,
"start_time": "0.041667",
"duration_ts": 29095066,
"duration": "1212.294417",
"bit_rate": "3948341",
"bits_per_raw_sample": "8",
"nb_frames": "29066",
"disposition": {
"default": 1,
"dub": 0,
"original": 0,
"comment": 0,
"lyrics": 0,
"karaoke": 0,
"forced": 0,
"hearing_impaired": 0,
"visual_impaired": 0,
"clean_effects": 0,
"attached_pic": 0,
"timed_thumbnails": 0
},
"tags": {
"creation_time": "2020-01-20T13:56:34.000000Z",
"language": "eng",
"handler_name": "\fVideoHandler",
"encoder": "h264"
}
},
{
"index": 1,
"codec_name": "ac3",
"codec_long_name": "ATSC A/52A (AC-3)",
"codec_type": "audio",
"codec_time_base": "1/48000",
"codec_tag_string": "ac-3",
"codec_tag": "0x332d6361",
"sample_fmt": "fltp",
"sample_rate": "48000",
"channels": 2,
"channel_layout": "stereo",
"bits_per_sample": 0,
"dmix_mode": "-1",
"ltrt_cmixlev": "-1.000000",
"ltrt_surmixlev": "-1.000000",
"loro_cmixlev": "-1.000000",
"loro_surmixlev": "-1.000000",
"r_frame_rate": "0/0",
"avg_frame_rate": "0/0",
"time_base": "1/48000",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 58232832,
"duration": "1213.184000",
"bit_rate": "224000",
"nb_frames": "37912",
"disposition": {
"default": 1,
"dub": 0,
"original": 0,
"comment": 0,
"lyrics": 0,
"karaoke": 0,
"forced": 0,
"hearing_impaired": 0,
"visual_impaired": 0,
"clean_effects": 0,
"attached_pic": 0,
"timed_thumbnails": 0
},
"tags": {
"creation_time": "2020-01-20T13:56:34.000000Z",
"language": "eng",
"handler_name": "\fSoundHandler"
},
"side_data_list": [
{
"side_data_type": "Audio Service Type"
}
]
}
],
"format": {
"filename": "The Big Bang Theory - S01E17.mp4",
"nb_streams": 2,
"nb_programs": 0,
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
"format_long_name": "QuickTime / MOV",
"start_time": "0.000000",
"duration": "1213.184000",
"size": "633084606",
"bit_rate": "4174698",
"probe_score": 100,
"tags": {
"major_brand": "mp42",
"minor_version": "512",
"compatible_brands": "mp42",
"creation_time": "2020-01-20T13:56:34.000000Z",
"media_type": "9",
"season_number": "0",
"episode_sort": "0",
"hd_video": "0",
"iTunMOVI": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"><plist version=\"1.0\"><dict><key>studio</key><string>studio</string><key>cast</key><array><dict><key>name</key><string></string></dict></array><key>directors</key><array><dict><key>name</key><string></string></dict></array><key>producers</key><array><dict><key>name</key><string></string></dict></array><key>codirectors</key><array><dict><key>name</key><string>codirector</string></dict></array><key>screenwriters</key><array><dict><key>name</key><string></string></dict></array></dict></plist>"
}
}
}

View File

@ -0,0 +1,72 @@
{
"streams": [
{
"index": 0,
"codec_name": "h264",
"codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
"profile": "High",
"codec_type": "video",
"codec_time_base": "1/50",
"codec_tag_string": "avc1",
"codec_tag": "0x31637661",
"width": 1280,
"height": 720,
"coded_width": 1280,
"coded_height": 720,
"closed_captions": 0,
"has_b_frames": 2,
"pix_fmt": "yuv420p",
"level": 31,
"chroma_location": "left",
"refs": 1,
"is_avc": "true",
"nal_length_size": "4",
"r_frame_rate": "25/1",
"avg_frame_rate": "25/1",
"time_base": "1/12800",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 3840000,
"duration": "300.000000",
"bit_rate": "53288",
"bits_per_raw_sample": "8",
"nb_frames": "7500",
"disposition": {
"default": 1,
"dub": 0,
"original": 0,
"comment": 0,
"lyrics": 0,
"karaoke": 0,
"forced": 0,
"hearing_impaired": 0,
"visual_impaired": 0,
"clean_effects": 0,
"attached_pic": 0,
"timed_thumbnails": 0
},
"tags": {
"language": "und",
"handler_name": "VideoHandler"
}
}
],
"format": {
"filename": "test-gray.720p.mp4",
"nb_streams": 1,
"nb_programs": 0,
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
"format_long_name": "QuickTime / MOV",
"start_time": "0.000000",
"duration": "300.000000",
"size": "2086818",
"bit_rate": "55648",
"probe_score": 100,
"tags": {
"major_brand": "isom",
"minor_version": "512",
"compatible_brands": "isomiso2avc1mp41",
"encoder": "Lavf58.20.100"
}
}
}