mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-12-16 14:03:03 +03:00
Merge pull request #6600 from cvium/keyframe_extraction_v1
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Jellyfin.MediaEncoding.Hls\Jellyfin.MediaEncoding.Hls.csproj" />
|
||||
<ProjectReference Include="..\..\src\Jellyfin.MediaEncoding.Keyframes\Jellyfin.MediaEncoding.Keyframes.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using Jellyfin.MediaEncoding.Hls.Playlist;
|
||||
using Jellyfin.MediaEncoding.Keyframes;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.MediaEncoding.Hls.Tests.Playlist
|
||||
{
|
||||
public class DynamicHlsPlaylistGeneratorTests
|
||||
{
|
||||
[Theory]
|
||||
[MemberData(nameof(ComputeSegments_Valid_Success_Data))]
|
||||
public void ComputeSegments_Valid_Success(KeyframeData keyframeData, int desiredSegmentLengthMs, double[] segments)
|
||||
{
|
||||
Assert.Equal(segments, DynamicHlsPlaylistGenerator.ComputeSegments(keyframeData, desiredSegmentLengthMs));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeSegments_InvalidDuration_ThrowsArgumentException()
|
||||
{
|
||||
var keyframeData = new KeyframeData(0, new[] { MsToTicks(10000) });
|
||||
Assert.Throws<ArgumentException>(() => DynamicHlsPlaylistGenerator.ComputeSegments(keyframeData, 6000));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ComputeEqualLengthSegments_Valid_Success_Data))]
|
||||
public void ComputeEqualLengthSegments_Valid_Success(int desiredSegmentLengthMs, long totalRuntimeTicks, double[] segments)
|
||||
{
|
||||
Assert.Equal(segments, DynamicHlsPlaylistGenerator.ComputeEqualLengthSegments(desiredSegmentLengthMs, totalRuntimeTicks));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 1000000)]
|
||||
[InlineData(1000, 0)]
|
||||
public void ComputeEqualLengthSegments_Invalid_ThrowsInvalidOperationException(int desiredSegmentLengthMs, long totalRuntimeTicks)
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => DynamicHlsPlaylistGenerator.ComputeEqualLengthSegments(desiredSegmentLengthMs, totalRuntimeTicks));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("testfile.mkv", new string[0], false)]
|
||||
[InlineData("testfile.flv", new[] { ".mp4", ".mkv", ".ts" }, false)]
|
||||
[InlineData("testfile.flv", new[] { ".mp4", ".mkv", ".ts", ".flv" }, true)]
|
||||
[InlineData("/some/arbitrarily/long/path/testfile.mkv", new[] { "mkv" }, true)]
|
||||
public void IsExtractionAllowedForFile_Valid_Success(string filePath, string[] allowedExtensions, bool isAllowed)
|
||||
{
|
||||
Assert.Equal(isAllowed, DynamicHlsPlaylistGenerator.IsExtractionAllowedForFile(filePath, allowedExtensions));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("testfile", new[] { ".mp4" })]
|
||||
public void IsExtractionAllowedForFile_Invalid_ReturnsFalse(string filePath, string[] allowedExtensions)
|
||||
{
|
||||
Assert.False(DynamicHlsPlaylistGenerator.IsExtractionAllowedForFile(filePath, allowedExtensions));
|
||||
}
|
||||
|
||||
private static TheoryData<int, long, double[]> ComputeEqualLengthSegments_Valid_Success_Data()
|
||||
{
|
||||
var data = new TheoryData<int, long, double[]>
|
||||
{
|
||||
{ 6000, MsToTicks(13000), new[] { 6.0, 6.0, 1.0 } },
|
||||
{ 3000, MsToTicks(15000), new[] { 3.0, 3.0, 3.0, 3.0, 3.0 } },
|
||||
{ 6000, MsToTicks(25000), new[] { 6.0, 6.0, 6.0, 6.0, 1.0 } },
|
||||
{ 6000, MsToTicks(20123), new[] { 6.0, 6.0, 6.0, 2.123 } },
|
||||
{ 6000, MsToTicks(1234), new[] { 1.234 } }
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static TheoryData<KeyframeData, int, double[]> ComputeSegments_Valid_Success_Data()
|
||||
{
|
||||
var data = new TheoryData<KeyframeData, int, double[]>
|
||||
{
|
||||
{
|
||||
new KeyframeData(MsToTicks(35000), new[] { 0, MsToTicks(10427), MsToTicks(20854), MsToTicks(31240) }),
|
||||
6000,
|
||||
new[] { 10.427, 10.427, 10.386, 3.760 }
|
||||
},
|
||||
{
|
||||
new KeyframeData(MsToTicks(10000), new[] { 0, MsToTicks(1000), MsToTicks(2000), MsToTicks(3000), MsToTicks(4000), MsToTicks(5000) }),
|
||||
2000,
|
||||
new[] { 2.0, 2.0, 6.0 }
|
||||
},
|
||||
{
|
||||
new KeyframeData(MsToTicks(10000), new[] { 0L }),
|
||||
6000,
|
||||
new[] { 10.0 }
|
||||
},
|
||||
{
|
||||
new KeyframeData(MsToTicks(10000), Array.Empty<long>()),
|
||||
6000,
|
||||
new[] { 10.0 }
|
||||
}
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private static long MsToTicks(int value) => TimeSpan.FromMilliseconds(value).Ticks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.MediaEncoding.Keyframes.FfProbe
|
||||
{
|
||||
public class FfProbeKeyframeExtractorTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("keyframes.txt", "keyframes_result.json")]
|
||||
[InlineData("keyframes_streamduration.txt", "keyframes_streamduration_result.json")]
|
||||
public void ParseStream_Valid_Success(string testDataFileName, string resultFileName)
|
||||
{
|
||||
var testDataPath = Path.Combine("FfProbe/Test Data", testDataFileName);
|
||||
var resultPath = Path.Combine("FfProbe/Test Data", resultFileName);
|
||||
using var resultFileStream = File.OpenRead(resultPath);
|
||||
var expectedResult = JsonSerializer.Deserialize<KeyframeData>(resultFileStream)!;
|
||||
|
||||
using var fileStream = File.OpenRead(testDataPath);
|
||||
using var streamReader = new StreamReader(fileStream);
|
||||
|
||||
var result = FfProbeKeyframeExtractor.ParseStream(streamReader);
|
||||
|
||||
Assert.Equal(expectedResult.TotalDuration, result.TotalDuration);
|
||||
Assert.Equal(expectedResult.KeyframeTicks, result.KeyframeTicks);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
{"TotalDuration":7063360000,"KeyframeTicks":[0,103850000,133880000,145150000,165580000,186440000,196450000,209790000,314060000,326990000,396230000,407070000,432520000,476310000,523020000,535540000,550550000,631050000,646480000,665670000,686520000,732400000,772020000,796210000,856690000,887970000,903820000,934270000,983070000,1056060000,1087750000,1187850000,1222050000,1251250000,1265430000,1305470000,1333830000,1345510000,1356770000,1368450000,1427260000,1460630000,1500670000,1540710000,1584500000,1607020000,1627880000,1639550000,1672090000,1685020000,1789290000,1883130000,1909820000,1931510000,1996580000,2017020000,2035370000,2051220000,2065400000,2085000000,2109190000,2120870000,2168420000,2253920000,2295210000,2374460000,2478730000,2582160000,2607190000,2697280000,2783610000,2825320000,2899560000,2929590000,2979230000,3017600000,3048880000,3073490000,3117700000,3141050000,3158160000,3200700000,3279530000,3299960000,3312890000,3332910000,3369200000,3379630000,3438440000,3459290000,3490990000,3533110000,3562730000,3600260000,3624040000,3672000000,3722050000,3753330000,3771270000,3875540000,3957290000,4016100000,4100350000,4114530000,4124540000,4157900000,4180430000,4200450000,4222550000,4252160000,4295960000,4309720000,4328070000,4340590000,4371450000,4400230000,4426920000,4489490000,4512010000,4531190000,4569570000,4599600000,4635460000,4660070000,4680930000,4729310000,4757670000,4777690000,4808550000,4824400000,4851100000,4864440000,4905320000,4955370000,4970380000,5074650000,5095090000,5109270000,5186010000,5204370000,5227720000,5242740000,5266930000,5342000000,5433760000,5447110000,5470470000,5520520000,5550550000,5565140000,5611020000,5642300000,5668160000,5711120000,5743240000,5762420000,5797460000,5817480000,5839170000,5855850000,5870870000,5904230000,5969300000,6056880000,6104850000,6152400000,6256250000,6295870000,6310050000,6325900000,6341750000,6356770000,6385960000,6426840000,6454780000,6469800000,6514420000,6549460000,6574480000,6602010000,6619530000,6654560000,6667080000,6690430000,6724630000,6762170000,6812220000,6849760000,6875200000,6912740000,6983230000,6994900000,7024930000]}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
{"TotalDuration":1000000000,"KeyframeTicks":[0,103850000,133880000,145150000,165580000,186440000,196450000,209790000,314060000,326990000,396230000,407070000,432520000,476310000,523020000,535540000,550550000,631050000,646480000,665670000,686520000,732400000,772020000,796210000,856690000,887970000,903820000,934270000,983070000]}
|
||||
@@ -0,0 +1,49 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
<RootNamespace>Jellyfin.MediaEncoding.Keyframes</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="FfProbe/Test Data/keyframes.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="FfProbe/Test Data/keyframes_result.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="FfProbe/Test Data/keyframes_streamduration.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="FfProbe/Test Data/keyframes_streamduration_result.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user