Refactor FLV processing rule test

This commit is contained in:
Genteure 2021-11-02 21:46:44 +08:00
parent 8e97541d39
commit 2b847c0662
87 changed files with 115512 additions and 568 deletions

2
.gitignore vendored
View File

@ -262,3 +262,5 @@ __pycache__/
*.pyc *.pyc
BililiveRecorder.Cli/Properties/launchSettings.json BililiveRecorder.Cli/Properties/launchSettings.json
**/*.received.*

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<LangVersion>9.0</LangVersion> <LangVersion>9.0</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>

View File

@ -0,0 +1,79 @@
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace BililiveRecorder.Flv.RuleTests
{
public static class AssertTags
{
public static void ShouldHaveLinearTimestamps(List<Tag> tags)
{
Assert.True(tags.Any2((a, b) => (a.Timestamp <= b.Timestamp) && (b.Timestamp - a.Timestamp < 50)));
}
public static void ShouldHaveFullHeaderTags(List<Tag> tags)
{
Assert.Equal(TagType.Script, tags[0].Type);
Assert.Equal(0, tags[0].Timestamp);
Assert.Equal(TagType.Video, tags[1].Type);
Assert.Equal(0, tags[1].Timestamp);
Assert.Equal(TagFlag.Header | TagFlag.Keyframe, tags[1].Flag);
Assert.Equal(TagType.Audio, tags[2].Type);
Assert.Equal(0, tags[2].Timestamp);
Assert.Equal(TagFlag.Header, tags[2].Flag);
Assert.Equal(TagType.Video, tags[3].Type);
Assert.Equal(0, tags[3].Timestamp);
Assert.Equal(TagFlag.Keyframe, tags[3].Flag);
}
public static void ShouldAlmostEqual(List<Tag> expectedTags, List<Tag> actualTags)
{
Assert.Equal(expectedTags.Count, actualTags.Count);
for (var i = 0; i < expectedTags.Count; i++)
{
var expected = expectedTags[i];
var actual = actualTags[i];
Assert.NotSame(expected, actual);
Assert.Equal(expected.Type, actual.Type);
Assert.Equal(expected.Flag, actual.Flag);
if (expected.IsScript())
{
Assert.Equal(0, actual.Timestamp);
}
else if (expected.IsEnd())
{
Assert.True(actual.IsEnd());
}
else if (expected.IsHeader())
{
Assert.Equal(0, actual.Timestamp);
var expectedBinaryData = expected.BinaryDataForSerializationUseOnly;
if (!string.IsNullOrWhiteSpace(expectedBinaryData))
{
var actualBinaryData = actual.BinaryDataForSerializationUseOnly;
Assert.Equal(expectedBinaryData, actualBinaryData);
}
}
else
{
Assert.Equal(expected.Timestamp, actual.Timestamp);
Assert.Equal(expected.Index, actual.Index);
}
}
}
public static void ShouldHaveSingleHeaderTagPerType(List<Tag> tags)
{
Assert.Single(tags.Where(x => x.Type == TagType.Script));
Assert.Single(tags.Where(x => x.Type == TagType.Audio && x.Flag == TagFlag.Header));
Assert.Single(tags.Where(x => x.Type == TagType.Video && x.Flag == (TagFlag.Header | TagFlag.Keyframe)));
}
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
@ -9,6 +9,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="Spectre.Verify.Extensions" Version="0.5.0" />
<PackageReference Include="Verify.Xunit" Version="13.3.1" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@ -25,7 +27,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="samples\**\*"> <None Update="TestData\**\*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,173 @@
{
"TagCounts": [
78
],
"Comments": [
{
"Type": "DecodingHeader",
"Count": 1
}
],
"AlternativeHeaders": [
"170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCBBABABABABABA",
"AF00119056E500FFFFFFFFFFFFFFFF",
"170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCB",
"AF00119056E500AAAAAAAAAAAA",
"AF00119056E500"
]
}
<?xml version="1.0" encoding="utf-16"?>
<BililiveRecorderFlv xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Tags>
<Tag Type="Script" Size="0" Timestamp="0">
<ScriptData>[{"Type":"String","Value":"onMetaData"},{"Type":"EcmaArray","Value":{"duration":{"Type":"Number","Value":1.0},"width":{"Type":"Number","Value":1920.0},"height":{"Type":"Number","Value":1080.0},"videodatarate":{"Type":"Number","Value":1464.84375},"framerate":{"Type":"Number","Value":30.0},"videocodecid":{"Type":"Number","Value":7.0},"audiodatarate":{"Type":"Number","Value":156.25},"audiosamplerate":{"Type":"Number","Value":48000.0},"audiosamplesize":{"Type":"Number","Value":16.0},"stereo":{"Type":"Boolean","Value":true},"audiocodecid":{"Type":"Number","Value":10.0},"encoder":{"Type":"String","Value":"Lavf58.29.100"},"filesize":{"Type":"Number","Value":524554.0}}}]</ScriptData>
</Tag>
<Tag Type="Video" Flag="Header Keyframe" Size="49" Timestamp="0">
<BinaryData>170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCB</BinaryData>
</Tag>
<Tag Type="Audio" Flag="Header" Size="7" Timestamp="0">
<BinaryData>AF00119056E500</BinaryData>
</Tag>
<Tag Type="Video" Flag="Keyframe" Size="1993" Timestamp="0">
<Nalus StartPosition="9" FullSize="753" Type="Sei" />
<Nalus StartPosition="766" FullSize="29" Type="Sps" />
<Nalus StartPosition="799" FullSize="4" Type="Pps" />
<Nalus StartPosition="807" FullSize="753" Type="Sei" />
<Nalus StartPosition="1564" FullSize="429" Type="CodedSliceOfAnIdrPicture" />
</Tag>
<Tag Type="Video" Size="76" Timestamp="34">
<Nalus StartPosition="9" FullSize="67" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Video" Size="73" Timestamp="67">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="405" Timestamp="67" />
<Tag Type="Audio" Size="481" Timestamp="88" />
<Tag Type="Video" Size="73" Timestamp="100">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="444" Timestamp="110" />
<Tag Type="Audio" Size="431" Timestamp="131" />
<Tag Type="Video" Size="73" Timestamp="134">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="329" Timestamp="152" />
<Tag Type="Video" Size="77" Timestamp="167">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="336" Timestamp="174" />
<Tag Type="Audio" Size="339" Timestamp="195" />
<Tag Type="Video" Size="75" Timestamp="200">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="337" Timestamp="216" />
<Tag Type="Video" Size="73" Timestamp="234">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="355" Timestamp="238" />
<Tag Type="Audio" Size="409" Timestamp="259" />
<Tag Type="Video" Size="73" Timestamp="267">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="401" Timestamp="280" />
<Tag Type="Video" Size="77" Timestamp="300">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="409" Timestamp="302" />
<Tag Type="Audio" Size="411" Timestamp="323" />
<Tag Type="Video" Size="75" Timestamp="334">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="443" Timestamp="344" />
<Tag Type="Audio" Size="394" Timestamp="366" />
<Tag Type="Video" Size="73" Timestamp="367">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="425" Timestamp="387" />
<Tag Type="Video" Size="73" Timestamp="400">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="432" Timestamp="408" />
<Tag Type="Audio" Size="424" Timestamp="430" />
<Tag Type="Video" Size="77" Timestamp="434">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="415" Timestamp="451" />
<Tag Type="Video" Size="75" Timestamp="467">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="475" Timestamp="472" />
<Tag Type="Audio" Size="462" Timestamp="494" />
<Tag Type="Video" Size="73" Timestamp="500">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="415" Timestamp="515" />
<Tag Type="Video" Size="73" Timestamp="534">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="425" Timestamp="536" />
<Tag Type="Audio" Size="422" Timestamp="558" />
<Tag Type="Video" Size="77" Timestamp="567">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="418" Timestamp="579" />
<Tag Type="Video" Size="75" Timestamp="600">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="401" Timestamp="600" />
<Tag Type="Audio" Size="415" Timestamp="622" />
<Tag Type="Video" Size="73" Timestamp="634">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="419" Timestamp="643" />
<Tag Type="Audio" Size="412" Timestamp="664" />
<Tag Type="Video" Size="73" Timestamp="667">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="438" Timestamp="686" />
<Tag Type="Video" Size="77" Timestamp="700">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="426" Timestamp="707" />
<Tag Type="Audio" Size="425" Timestamp="728" />
<Tag Type="Video" Size="75" Timestamp="734">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="447" Timestamp="750" />
<Tag Type="Video" Size="73" Timestamp="767">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="404" Timestamp="771" />
<Tag Type="Audio" Size="442" Timestamp="792" />
<Tag Type="Video" Size="73" Timestamp="800">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="440" Timestamp="814" />
<Tag Type="Video" Size="77" Timestamp="834">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="439" Timestamp="835" />
<Tag Type="Audio" Size="425" Timestamp="856" />
<Tag Type="Video" Size="75" Timestamp="867">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="471" Timestamp="878" />
<Tag Type="Audio" Size="466" Timestamp="899" />
<Tag Type="Video" Size="73" Timestamp="900">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="447" Timestamp="920" />
<Tag Type="Video" Size="73" Timestamp="934">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="455" Timestamp="942" />
<Tag Type="Audio" Size="450" Timestamp="963" />
<Tag Type="Video" Size="77" Timestamp="967">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Size="409" Timestamp="984" />
<Tag Type="Video" Size="75" Timestamp="1000">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
</Tags>
</BililiveRecorderFlv>

View File

@ -1,89 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BililiveRecorder.Flv.Grouping;
using BililiveRecorder.Flv.Pipeline;
using BililiveRecorder.Flv.Writer;
using BililiveRecorder.Flv.Xml;
using Newtonsoft.Json;
using Xunit;
namespace BililiveRecorder.Flv.RuleTests.Integrated
{
public class BadTests : TestBase
{
[Theory]
[SampleDirectoryTestData("samples/bad")]
public async Task TestBadSamples(string path)
{
// Arrange
var path_info = Path.Combine(path, "info.json");
var info = JsonConvert.DeserializeObject<Info>(File.ReadAllText(path_info));
var path_input = Path.Combine(path, "input.xml");
var input = this.LoadFile(path_input);
var reader = new TagGroupReader(new FlvTagListReader(input.Tags));
var output = new FlvTagListWriter();
var comments = new List<ProcessingComment>();
// Act
await this.RunPipeline(reader, output, comments).ConfigureAwait(false);
// Assert
Assert.Equal(info.AlternativeHeaderCount, output.AlternativeHeaders.Count);
comments.RemoveAll(x => x.T == CommentType.Logging);
Assert.Equal(info.AllowedComments.Values.Sum(x => x), comments.Count);
Assert.DoesNotContain(comments, x => !info.AllowedComments.ContainsKey(x.T));
Assert.True(info.AllowedComments.All(x => x.Value == comments.Count(c => c.T == x.Key)));
Assert.Equal(info.Files.Length, output.Files.Count);
for (var i = 0; i < info.Files.Length; i++)
{
var expected = info.Files[i];
var actual = output.Files[i];
Assert.Equal(expected.TagCount, actual.Count);
// TODO 重写相关测试的检查,支持只有音频或视频 header 的数据片段
if (!expected.SkipTagCheck)
this.AssertTagsShouldPassBasicChecks(actual);
if (expected.VideoHeaderData is not null)
Assert.Equal(expected.VideoHeaderData, actual[1].BinaryDataForSerializationUseOnly);
if (expected.AudioHeaderData is not null)
Assert.Equal(expected.AudioHeaderData, actual[2].BinaryDataForSerializationUseOnly);
if (!expected.SkipTagCheck)
await this.AssertTagsByRerunPipeline(actual).ConfigureAwait(false);
}
}
public class Info
{
public OutputFile[] Files { get; set; } = Array.Empty<OutputFile>();
public Dictionary<CommentType, int> AllowedComments { get; set; } = new Dictionary<CommentType, int>();
public int AlternativeHeaderCount { get; set; }
}
public class OutputFile
{
public string? VideoHeaderData { get; set; }
public string? AudioHeaderData { get; set; }
public int TagCount { get; set; }
// TODO 采用更合理的检查方法而不是直接跳过
public bool SkipTagCheck { get; set; }
}
}
}

View File

@ -1,77 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BililiveRecorder.Flv.Grouping;
using BililiveRecorder.Flv.Pipeline;
using BililiveRecorder.Flv.Writer;
using BililiveRecorder.Flv.Xml;
using Xunit;
namespace BililiveRecorder.Flv.RuleTests.Integrated
{
public class GoodTests : TestBase
{
[Theory]
[SampleFileTestData("samples/good")]
public async Task StrictTestsAsync(string path)
{
// Arrange
var original = this.LoadFile(path).Tags;
var reader = new TagGroupReader(new FlvTagListReader(this.LoadFile(path).Tags));
var output = new FlvTagListWriter();
var comments = new List<ProcessingComment>();
// Act
await this.RunPipeline(reader, output, comments).ConfigureAwait(false);
// Assert
comments.RemoveAll(x => x.T == CommentType.Logging);
Assert.Empty(comments);
Assert.Empty(output.AlternativeHeaders);
var file = Assert.Single(output.Files);
Assert.Equal(original.Count, file.Count);
this.AssertTagsShouldPassBasicChecks(file);
this.AssertTagsAlmostEqual(original, file);
await this.AssertTagsByRerunPipeline(file).ConfigureAwait(false);
}
[Theory]
[SampleFileTestData("samples/good")]
public async Task StrictWithArtificalOffsetTestsAsync(string path)
{
// Arrange
var original = this.LoadFile(path).Tags;
var random = new System.Random();
var offset = random.Next(51, 9999);
if (random.Next(2) == 1)
offset = -offset;
var inputTags = this.LoadFile(path).Tags;
foreach (var tag in inputTags)
tag.Timestamp += offset;
var reader = new TagGroupReader(new FlvTagListReader(inputTags));
var output = new FlvTagListWriter();
var comments = new List<ProcessingComment>();
// Act
await this.RunPipeline(reader, output, comments).ConfigureAwait(false);
// Assert
comments.RemoveAll(x => x.T == CommentType.Logging);
Assert.Equal(CommentType.TimestampJump, Assert.Single(comments).T);
Assert.Empty(output.AlternativeHeaders);
var file = Assert.Single(output.Files);
Assert.Equal(original.Count, file.Count);
this.AssertTagsShouldPassBasicChecks(file);
this.AssertTagsAlmostEqual(original, file);
await this.AssertTagsByRerunPipeline(file).ConfigureAwait(false);
}
}
}

View File

@ -1,147 +0,0 @@
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using BililiveRecorder.Flv.Grouping;
using BililiveRecorder.Flv.Pipeline;
using BililiveRecorder.Flv.Writer;
using BililiveRecorder.Flv.Xml;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace BililiveRecorder.Flv.RuleTests.Integrated
{
public abstract class TestBase
{
protected XmlFlvFile LoadFile(string path)
{
Stream? stream = null;
try
{
var gz_path = path + ".gz";
if (Path.GetExtension(path) == ".gz")
stream = new GZipStream(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read), CompressionMode.Decompress);
else if (File.Exists(gz_path))
stream = new GZipStream(File.Open(gz_path, FileMode.Open, FileAccess.Read, FileShare.Read), CompressionMode.Decompress);
else
stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
return (XmlFlvFile)XmlFlvFile.Serializer.Deserialize(stream);
}
finally
{
stream?.Dispose();
}
}
protected ProcessingDelegate BuildPipeline() =>
new ProcessingPipelineBuilder(new ServiceCollection().BuildServiceProvider()).AddDefault().AddRemoveFillerData().Build();
protected async Task RunPipeline(ITagGroupReader reader, IFlvTagWriter output, List<ProcessingComment> comments)
{
var writer = new FlvProcessingContextWriter(tagWriter: output, allowMissingHeader: true, disableKeyframes: true);
var session = new Dictionary<object, object?>();
var context = new FlvProcessingContext();
var pipeline = this.BuildPipeline();
while (true)
{
var group = await reader.ReadGroupAsync(default).ConfigureAwait(false);
if (group is null)
break;
context.Reset(group, session);
pipeline(context);
comments.AddRange(context.Comments);
await writer.WriteAsync(context).ConfigureAwait(false);
}
}
protected async Task AssertTagsByRerunPipeline(List<Tag> tags)
{
var reader = new TagGroupReader(new FlvTagListReader(tags.Select(x => x.Clone()).ToList()));
var output = new FlvTagListWriter();
var comments = new List<ProcessingComment>();
await this.RunPipeline(reader, output, comments).ConfigureAwait(false);
comments.RemoveAll(x => x.T == CommentType.Logging);
Assert.Empty(comments);
Assert.Empty(output.AlternativeHeaders);
var file = Assert.Single(output.Files);
Assert.Equal(tags.Count, file.Count);
for (var i = 0; i < tags.Count; i++)
{
var a = tags[i];
var b = file[i];
Assert.NotSame(a, b);
Assert.Equal(a.Type, b.Type);
Assert.Equal(a.Flag, b.Flag);
Assert.Equal(a.Index, b.Index);
Assert.Equal(a.Size, b.Size);
Assert.Equal(a.Timestamp, b.Timestamp);
Assert.Equal(a.BinaryDataForSerializationUseOnly, b.BinaryDataForSerializationUseOnly);
}
}
protected void AssertTagsShouldPassBasicChecks(List<Tag> tags)
{
Assert.True(tags.Any2((a, b) => (a.Timestamp <= b.Timestamp) && (b.Timestamp - a.Timestamp < 50)));
Assert.Equal(TagType.Script, tags[0].Type);
Assert.Equal(0, tags[0].Timestamp);
Assert.Equal(TagType.Video, tags[1].Type);
Assert.Equal(0, tags[1].Timestamp);
Assert.Equal(TagFlag.Header | TagFlag.Keyframe, tags[1].Flag);
Assert.Equal(TagType.Audio, tags[2].Type);
Assert.Equal(0, tags[2].Timestamp);
Assert.Equal(TagFlag.Header, tags[2].Flag);
Assert.InRange(tags[3].Timestamp, 0, 50);
}
protected void AssertTagsAlmostEqual(List<Tag> expected, List<Tag> actual)
{
Assert.Single(actual.Where(x => x.Type == TagType.Script));
Assert.Single(actual.Where(x => x.Type == TagType.Audio && x.Flag == TagFlag.Header));
Assert.Single(actual.Where(x => x.Type == TagType.Video && x.Flag == (TagFlag.Header | TagFlag.Keyframe)));
for (var i = 0; i < expected.Count; i++)
{
var a = expected[i];
var b = actual[i];
Assert.NotSame(a, b);
Assert.Equal(a.Type, b.Type);
Assert.Equal(a.Flag, b.Flag);
if (a.IsScript())
{
Assert.Equal(0, b.Timestamp);
}
else if (a.IsEnd())
{
}
else if (a.IsHeader())
{
Assert.Equal(0, b.Timestamp);
var binaryDataForSerializationUseOnly = a.BinaryDataForSerializationUseOnly;
Assert.False(string.IsNullOrWhiteSpace(binaryDataForSerializationUseOnly));
Assert.Equal(binaryDataForSerializationUseOnly, b.BinaryDataForSerializationUseOnly);
}
else
{
Assert.Equal(a.Timestamp, b.Timestamp);
Assert.Equal(a.Index, b.Index);
}
}
}
}
}

View File

@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Serialization;
using BililiveRecorder.Flv.Grouping;
using BililiveRecorder.Flv.Pipeline;
using BililiveRecorder.Flv.Writer;
using BililiveRecorder.Flv.Xml;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using VerifyTests;
using VerifyXunit;
using Xunit;
namespace BililiveRecorder.Flv.RuleTests.Integrated
{
[UsesVerify]
[ExpectationPath("Bad")]
public class IntegratedBadTests : IntegratedTestBase
{
[Theory]
[Expectation("TestBadSamples")]
//[SampleDirectoryTestData("TestData/Bad")]
[SampleFileTestData("TestData/Bad")]
public async Task TestBadSamples(string path)
{
// Arrange
//var path_info = Path.Combine(path, "info.json");
//var INFO_TO_BE_REMOVED = JsonConvert.DeserializeObject<Info>(File.ReadAllText(path_info));
//var path_input = Path.Combine(path, "input.xml");
var originalTags = SampleFileLoader.Load(path).Tags;
var reader = new TagGroupReader(new FlvTagListReader(originalTags));
var flvTagListWriter = new FlvTagListWriter();
var comments = new List<ProcessingComment>();
// Act
await RunPipeline(reader, flvTagListWriter, comments).ConfigureAwait(false);
// Assert
comments.RemoveAll(x => x.T == CommentType.Logging);
var outputResult = new OutputResult();
//Assert.Equal(INFO_TO_BE_REMOVED.AlternativeHeaderCount, flvTagListWriter.AlternativeHeaders.Count);
outputResult.AlternativeHeaders = flvTagListWriter.AlternativeHeaders.Select(x => x.BinaryDataForSerializationUseOnly).ToArray();
//Assert.Equal(INFO_TO_BE_REMOVED.AllowedComments.Values.Sum(x => x), comments.Count);
//Assert.DoesNotContain(comments, x => !INFO_TO_BE_REMOVED.AllowedComments.ContainsKey(x.T));
//Assert.True(INFO_TO_BE_REMOVED.AllowedComments.All(x => x.Value == comments.Count(c => c.T == x.Key)));
outputResult.Comments = comments.GroupBy(x => x.T).Select(x => new CommentCount(x.Key, x.Count())).ToArray();
//Assert.Equal(INFO_TO_BE_REMOVED.Files.Length, flvTagListWriter.Files.Count);
outputResult.TagCounts = flvTagListWriter.Files.Select(x => x.Count).ToArray();
// outputResult.Tags = flvTagListWriter.Files.ToArray();
using var sw = new StringWriter();
sw.WriteLine(JsonConvert.SerializeObject(outputResult, Formatting.Indented));
// OutputResultSerializer.Serialize(xmlSw, outputResult);
// var xmlStr = xmlSw.ToString();
// await Verifier.Verify(xmlStr).UseParameters(path);
for (var i = 0; i < flvTagListWriter.Files.Count; i++)
{
// var expected = INFO_TO_BE_REMOVED.Files[i];
var outputTags = flvTagListWriter.Files[i];
// Assert.Equal(expected.TagCount, outputTags.Count);
// TODO 重写相关测试的检查,支持只有音频或视频 header 的数据片段
AssertTags.ShouldHaveLinearTimestamps(outputTags);
// if (!expected.SkipTagCheck)
// this.AssertTagsShouldPassBasicChecks(outputTags);
//if (expected.VideoHeaderData is not null)
// Assert.Equal(expected.VideoHeaderData, outputTags[1].BinaryDataForSerializationUseOnly);
//if (expected.AudioHeaderData is not null)
// Assert.Equal(expected.AudioHeaderData, outputTags[2].BinaryDataForSerializationUseOnly);
await AssertTagsByRerunPipeline(outputTags).ConfigureAwait(false);
var xmlStr = SerializeTags(outputTags);
// await Verifier.Verify(xmlStr).UseExtension("xml").UseParameters(path);
sw.WriteLine(xmlStr);
}
await Verifier.Verify(sw.ToString()).UseParameters(path);
}
public static XmlSerializer OutputResultSerializer { get; } = new XmlSerializer(typeof(OutputResult));
public class OutputResult
{
public int[] TagCounts { get; set; } = Array.Empty<int>();
public CommentCount[] Comments { get; set; } = Array.Empty<CommentCount>();
public string?[] AlternativeHeaders { get; set; } = Array.Empty<string>();
}
public class Info
{
public OutputFile[] Files { get; set; } = Array.Empty<OutputFile>();
public Dictionary<CommentType, int> AllowedComments { get; set; } = new Dictionary<CommentType, int>();
public int AlternativeHeaderCount { get; set; }
}
public class OutputFile
{
public string? VideoHeaderData { get; set; }
public string? AudioHeaderData { get; set; }
public int TagCount { get; set; }
}
public struct CommentCount
{
[JsonConverter(typeof(StringEnumConverter))]
public CommentType Type;
public int Count;
public CommentCount(CommentType type, int count)
{
this.Type = type;
this.Count = count;
}
public override bool Equals(object? obj)
{
return obj is CommentCount other &&
this.Type == other.Type &&
this.Count == other.Count;
}
public override int GetHashCode()
{
return HashCode.Combine(this.Type, this.Count);
}
public void Deconstruct(out CommentType type, out int count)
{
type = this.Type;
count = this.Count;
}
public static implicit operator (CommentType, int)(CommentCount value)
{
return (value.Type, value.Count);
}
public static implicit operator CommentCount((CommentType, int) value)
{
return new CommentCount(value.Item1, value.Item2);
}
}
}
}

View File

@ -0,0 +1,103 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BililiveRecorder.Flv.Grouping;
using BililiveRecorder.Flv.Pipeline;
using BililiveRecorder.Flv.Writer;
using BililiveRecorder.Flv.Xml;
using VerifyTests;
using VerifyXunit;
using Xunit;
namespace BililiveRecorder.Flv.RuleTests.Integrated
{
[UsesVerify]
[ExpectationPath("Good")]
public class IntegratedGoodTests : IntegratedTestBase
{
[Theory]
[Expectation("StandardTest")]
[SampleFileTestData("TestData/Good")]
public async Task StrictTestsAsync(string path)
{
// Arrange
var originalTags = SampleFileLoader.Load(path).Tags;
var reader = new TagGroupReader(new FlvTagListReader(SampleFileLoader.Load(path).Tags));
var flvTagListWriter = new FlvTagListWriter();
var comments = new List<ProcessingComment>();
// Act
await RunPipeline(reader, flvTagListWriter, comments).ConfigureAwait(false);
// Assert
comments.RemoveAll(x => x.T == CommentType.Logging);
Assert.Empty(comments);
Assert.Empty(flvTagListWriter.AlternativeHeaders);
var outputTags = Assert.Single(flvTagListWriter.Files);
Assert.Equal(originalTags.Count, outputTags.Count);
AssertTags.ShouldHaveLinearTimestamps(outputTags);
AssertTags.ShouldHaveFullHeaderTags(outputTags);
//this.AssertTagsShouldPassBasicChecks(outputTags);
AssertTags.ShouldHaveSingleHeaderTagPerType(outputTags);
AssertTags.ShouldAlmostEqual(originalTags, outputTags);
//this.AssertTagsAlmostEqual(originalTags, outputTags);
await AssertTagsByRerunPipeline(outputTags).ConfigureAwait(false);
var xmlStr = SerializeTags(outputTags);
await Verifier.Verify(xmlStr).UseExtension("xml").UseParameters(path);
}
[Theory]
[Expectation("WithOffsetTest")]
[SampleFileTestData("TestData/Good")]
public async Task StrictWithArtificalOffsetTestsAsync(string path)
{
// Arrange
var originalTags = SampleFileLoader.Load(path).Tags;
var random = new System.Random();
var offset = random.Next(51, 9999);
if (random.Next(2) == 1)
offset = -offset;
var inputTagsWithOffset = SampleFileLoader.Load(path).Tags;
foreach (var tag in inputTagsWithOffset)
tag.Timestamp += offset;
var reader = new TagGroupReader(new FlvTagListReader(inputTagsWithOffset));
var output = new FlvTagListWriter();
var comments = new List<ProcessingComment>();
// Act
await RunPipeline(reader, output, comments).ConfigureAwait(false);
// Assert
comments.RemoveAll(x => x.T == CommentType.Logging);
Assert.Equal(CommentType.TimestampJump, Assert.Single(comments).T);
Assert.Empty(output.AlternativeHeaders);
var outputTags = Assert.Single(output.Files);
Assert.Equal(originalTags.Count, outputTags.Count);
AssertTags.ShouldHaveLinearTimestamps(outputTags);
AssertTags.ShouldHaveFullHeaderTags(outputTags);
//this.AssertTagsShouldPassBasicChecks(outputTags);
AssertTags.ShouldHaveSingleHeaderTagPerType(outputTags);
AssertTags.ShouldAlmostEqual(originalTags, outputTags);
//this.AssertTagsAlmostEqual(originalTags, outputTags);
await AssertTagsByRerunPipeline(outputTags).ConfigureAwait(false);
var xmlStr = SerializeTags(outputTags);
await Verifier.Verify(xmlStr).UseExtension("xml").UseParameters(path);
}
}
}

View File

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BililiveRecorder.Flv.Grouping;
using BililiveRecorder.Flv.Pipeline;
using BililiveRecorder.Flv.Writer;
using BililiveRecorder.Flv.Xml;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace BililiveRecorder.Flv.RuleTests.Integrated
{
public abstract class IntegratedTestBase
{
protected static string SerializeTags(List<Tag> tags)
{
using var sw = new StringWriter();
XmlFlvFile.Serializer.Serialize(sw, new XmlFlvFile { Tags = tags });
return sw.ToString();
}
protected static async Task RunPipeline(ITagGroupReader reader, IFlvTagWriter output, List<ProcessingComment> comments)
{
var writer = new FlvProcessingContextWriter(tagWriter: output, allowMissingHeader: true, disableKeyframes: true);
var session = new Dictionary<object, object?>();
var context = new FlvProcessingContext();
var pipeline = new ProcessingPipelineBuilder(new ServiceCollection().BuildServiceProvider()).AddDefault().AddRemoveFillerData().Build();
while (true)
{
var group = await reader.ReadGroupAsync(default).ConfigureAwait(false);
if (group is null)
break;
context.Reset(group, session);
pipeline(context);
comments.AddRange(context.Comments);
await writer.WriteAsync(context).ConfigureAwait(false);
}
}
protected static async Task AssertTagsByRerunPipeline(List<Tag> tags)
{
var reader = new TagGroupReader(new FlvTagListReader(tags.Select(x => x.Clone()).ToList()));
var output = new FlvTagListWriter();
var comments = new List<ProcessingComment>();
await RunPipeline(reader, output, comments).ConfigureAwait(false);
// 忽略 ignore Logging
comments.RemoveAll(x => x.T == CommentType.Logging);
// 不应该有任何问题 Shouldn't have any problems
Assert.Empty(comments);
// 不应该有多个 Header Shouldn't have multiple headers
Assert.Empty(output.AlternativeHeaders);
// 只应该有一个文件输出 Should output only a single file
var outputTags = Assert.Single(output.Files);
// Tag count should match
Assert.Equal(tags.Count, outputTags.Count);
for (var i = 0; i < tags.Count; i++)
{
var a = tags[i];
var b = outputTags[i];
Assert.NotSame(a, b);
Assert.Equal(a.Type, b.Type);
Assert.Equal(a.Flag, b.Flag);
Assert.Equal(a.Index, b.Index);
Assert.Equal(a.Size, b.Size);
Assert.Equal(a.Timestamp, b.Timestamp);
Assert.Equal(a.BinaryDataForSerializationUseOnly, b.BinaryDataForSerializationUseOnly);
}
}
}
}

View File

@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Xunit.Sdk;
namespace BililiveRecorder.Flv.RuleTests
{
public class SampleDirectoryTestDataAttribute : DataAttribute
{
public SampleDirectoryTestDataAttribute(string basePath)
{
this.BasePath = basePath;
}
public string BasePath { get; }
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
var fullPath = Path.IsPathRooted(this.BasePath) ? this.BasePath : Path.GetRelativePath(Directory.GetCurrentDirectory(), this.BasePath);
if (!Directory.Exists(fullPath))
throw new ArgumentException($"Could not find directory at path: {fullPath}");
return Directory.GetDirectories(fullPath).Select(x => new object[] { x });
}
}
}

View File

@ -0,0 +1,23 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using BililiveRecorder.Flv.Xml;
namespace BililiveRecorder.Flv.RuleTests
{
public static class SampleFileLoader
{
public static XmlFlvFile Load(string fileName)
{
var stackTrace = new StackTrace();
var frames = stackTrace.GetFrames();
var attr = frames.Select(x => x!.GetMethod()!.GetCustomAttribute<SampleFileTestDataAttribute>()).First(x => x is not null);
var fullPath = Path.Combine(attr!.BasePath, fileName);
using var s = File.Open(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read);
return (XmlFlvFile)XmlFlvFile.Serializer.Deserialize(s)!;
}
}
}

View File

@ -23,7 +23,7 @@ namespace BililiveRecorder.Flv.RuleTests
if (!Directory.Exists(fullPath)) if (!Directory.Exists(fullPath))
throw new ArgumentException($"Could not find directory at path: {fullPath}"); throw new ArgumentException($"Could not find directory at path: {fullPath}");
return new[] { "*.xml", "*.gz" }.SelectMany(x => Directory.GetFiles(fullPath, x)).Select(x => new object[] { x }); return new[] { "*.xml" }.SelectMany(x => Directory.GetFiles(fullPath, x)).Select(x => new object[] { Path.GetFileName(x) });
} }
} }
} }

View File

@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BililiveRecorder.Flv.RuleTests.Standalone
{
public class Test
{
}
}

View File

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
文件连续出现多个不同的 Header
-->
<BililiveRecorderFlv xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Tags>
<Tag Type="Script" Flag="None" Size="293" Timestamp="0">
<ScriptData>[{"Type":"String","Value":"onMetaData"},{"Type":"EcmaArray","Value":{"duration":{"Type":"Number","Value":22.233},"width":{"Type":"Number","Value":1920.0},"height":{"Type":"Number","Value":1080.0},"videodatarate":{"Type":"Number","Value":1464.84375},"framerate":{"Type":"Number","Value":30.0},"videocodecid":{"Type":"Number","Value":7.0},"audiodatarate":{"Type":"Number","Value":156.25},"audiosamplerate":{"Type":"Number","Value":48000.0},"audiosamplesize":{"Type":"Number","Value":16.0},"stereo":{"Type":"Boolean","Value":true},"audiocodecid":{"Type":"Number","Value":10.0},"encoder":{"Type":"String","Value":"Lavf58.29.100"},"filesize":{"Type":"Number","Value":524554.0}}}]</ScriptData>
</Tag>
<Tag Type="Video" Flag="Header Keyframe" Size="49" Timestamp="0">
<BinaryData>170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCBBABABABABABA</BinaryData>
</Tag>
<Tag Type="Audio" Flag="Header" Size="7" Timestamp="0">
<BinaryData>AF00119056E500FFFFFFFFFFFFFFFF</BinaryData>
</Tag>
<Tag Type="Video" Flag="Header Keyframe" Size="49" Timestamp="0">
<BinaryData>170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCB</BinaryData>
</Tag>
<Tag Type="Audio" Flag="Header" Size="7" Timestamp="0">
<BinaryData>AF00119056E500AAAAAAAAAAAA</BinaryData>
</Tag>
<Tag Type="Audio" Flag="Header" Size="7" Timestamp="0">
<BinaryData>AF00119056E500</BinaryData>
</Tag>
<Tag Type="Video" Flag="Keyframe" Size="1993" Timestamp="0">
<Nalus StartPosition="9" FullSize="753" Type="Sei" />
<Nalus StartPosition="766" FullSize="29" Type="Sps" />
<Nalus StartPosition="799" FullSize="4" Type="Pps" />
<Nalus StartPosition="807" FullSize="753" Type="Sei" />
<Nalus StartPosition="1564" FullSize="429" Type="CodedSliceOfAnIdrPicture" />
</Tag>
<Tag Type="Video" Flag="None" Size="76" Timestamp="34">
<Nalus StartPosition="9" FullSize="67" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Video" Flag="None" Size="73" Timestamp="67">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="405" Timestamp="67" />
<Tag Type="Audio" Flag="None" Size="481" Timestamp="88" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="100">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="444" Timestamp="110" />
<Tag Type="Audio" Flag="None" Size="431" Timestamp="131" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="134">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="329" Timestamp="152" />
<Tag Type="Video" Flag="None" Size="77" Timestamp="167">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="336" Timestamp="174" />
<Tag Type="Audio" Flag="None" Size="339" Timestamp="195" />
<Tag Type="Video" Flag="None" Size="75" Timestamp="200">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="337" Timestamp="216" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="234">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="355" Timestamp="238" />
<Tag Type="Audio" Flag="None" Size="409" Timestamp="259" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="267">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="401" Timestamp="280" />
<Tag Type="Video" Flag="None" Size="77" Timestamp="300">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="409" Timestamp="302" />
<Tag Type="Audio" Flag="None" Size="411" Timestamp="323" />
<Tag Type="Video" Flag="None" Size="75" Timestamp="334">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="443" Timestamp="344" />
<Tag Type="Audio" Flag="None" Size="394" Timestamp="366" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="367">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="425" Timestamp="387" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="400">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="432" Timestamp="408" />
<Tag Type="Audio" Flag="None" Size="424" Timestamp="430" />
<Tag Type="Video" Flag="None" Size="77" Timestamp="434">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="415" Timestamp="451" />
<Tag Type="Video" Flag="None" Size="75" Timestamp="467">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="475" Timestamp="472" />
<Tag Type="Audio" Flag="None" Size="462" Timestamp="494" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="500">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="415" Timestamp="515" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="534">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="425" Timestamp="536" />
<Tag Type="Audio" Flag="None" Size="422" Timestamp="558" />
<Tag Type="Video" Flag="None" Size="77" Timestamp="567">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="418" Timestamp="579" />
<Tag Type="Video" Flag="None" Size="75" Timestamp="600">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="401" Timestamp="600" />
<Tag Type="Audio" Flag="None" Size="415" Timestamp="622" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="634">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="419" Timestamp="643" />
<Tag Type="Audio" Flag="None" Size="412" Timestamp="664" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="667">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="438" Timestamp="686" />
<Tag Type="Video" Flag="None" Size="77" Timestamp="700">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="426" Timestamp="707" />
<Tag Type="Audio" Flag="None" Size="425" Timestamp="728" />
<Tag Type="Video" Flag="None" Size="75" Timestamp="734">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="447" Timestamp="750" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="767">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="404" Timestamp="771" />
<Tag Type="Audio" Flag="None" Size="442" Timestamp="792" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="800">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="440" Timestamp="814" />
<Tag Type="Video" Flag="None" Size="77" Timestamp="834">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="439" Timestamp="835" />
<Tag Type="Audio" Flag="None" Size="425" Timestamp="856" />
<Tag Type="Video" Flag="None" Size="75" Timestamp="867">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="471" Timestamp="878" />
<Tag Type="Audio" Flag="None" Size="466" Timestamp="899" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="900">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="447" Timestamp="920" />
<Tag Type="Video" Flag="None" Size="73" Timestamp="934">
<Nalus StartPosition="9" FullSize="64" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="455" Timestamp="942" />
<Tag Type="Audio" Flag="None" Size="450" Timestamp="963" />
<Tag Type="Video" Flag="None" Size="77" Timestamp="967">
<Nalus StartPosition="9" FullSize="68" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="409" Timestamp="984" />
<Tag Type="Video" Flag="None" Size="75" Timestamp="1000">
<Nalus StartPosition="9" FullSize="66" Type="CodedSliceOfANonIdrPicture" />
</Tag>
</Tags>
</BililiveRecorderFlv>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
using System.Runtime.CompilerServices;
using DiffEngine;
using VerifyTests;
namespace BililiveRecorder.Flv.RuleTests
{
public static class VerifyConfig
{
[ModuleInitializer]
public static void Init()
{
VerifierSettings.DerivePathInfo(Expectations.Initialize);
VerifierSettings.UseStrictJson();
DiffRunner.Disabled = true;
}
}
}

View File

@ -1,10 +0,0 @@
两个相同的 Audio Header
```xml
<Tag Type="Audio" Flag="Header" Size="7" Timestamp="0">
<BinaryData>AF00121056E500</BinaryData>
</Tag>
<Tag Type="Audio" Flag="Header" Size="7" Timestamp="0">
<BinaryData>AF00121056E500</BinaryData>
</Tag>
```

View File

@ -1,9 +0,0 @@
{
"Files": [
{
"VideoHeaderData": "17000000000164002AFFE100286764002AAC2CA501E0089F97016A0C0C0C800001F40000EA607000000A7D88000029F630DDE5C14001000468EB8F2C",
"AudioHeaderData": "AF00121056E500",
"TagCount": 862
}
]
}

View File

@ -1 +0,0 @@
文件中途出现了和之前相同的 音频 Header

View File

@ -1,9 +0,0 @@
{
"Files": [
{
"TagCount": 1705,
"AudioHeaderData": "AF00119056E500",
"VideoHeaderData": "170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCB"
}
]
}

View File

@ -1 +0,0 @@
文件中途出现了和之前相同的 音频 和 视频 Header

View File

@ -1,9 +0,0 @@
{
"Files": [
{
"TagCount": 1705,
"AudioHeaderData": "AF00119056E500",
"VideoHeaderData": "170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCB"
}
]
}

View File

@ -1 +0,0 @@
文件中途出现了和之前相同的 视频 Header

View File

@ -1,9 +0,0 @@
{
"Files": [
{
"TagCount": 1705,
"AudioHeaderData": "AF00119056E500",
"VideoHeaderData": "170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCB"
}
]
}

View File

@ -1 +0,0 @@
文件中途出现了和之前不同的 音频 Header

View File

@ -1,18 +0,0 @@
{
"AllowedComments": {
"DecodingHeader": 1,
"TimestampJump": 1
},
"Files": [
{
"TagCount": 641,
"AudioHeaderData": "AF00119056E500",
"VideoHeaderData": "170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCB"
},
{
"TagCount": 1067,
"AudioHeaderData": "AF00119056E500FFFFFFFFFF",
"VideoHeaderData": "170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCB"
}
]
}

View File

@ -1 +0,0 @@
文件中途出现了和之前不同的 音频 和 视频 Header

View File

@ -1,18 +0,0 @@
{
"AllowedComments": {
"DecodingHeader": 1,
"TimestampJump": 1
},
"Files": [
{
"TagCount": 641,
"AudioHeaderData": "AF00119056E500",
"VideoHeaderData": "170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCB"
},
{
"TagCount": 1067,
"AudioHeaderData": "AF00119056E500FFFFFFFFFF",
"VideoHeaderData": "170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCBFFFFFFFFFFFF"
}
]
}

View File

@ -1 +0,0 @@
文件中途出现了和之前不同的 视频 Header

View File

@ -1,18 +0,0 @@
{
"AllowedComments": {
"DecodingHeader": 1,
"TimestampJump": 1
},
"Files": [
{
"TagCount": 641,
"AudioHeaderData": "AF00119056E500",
"VideoHeaderData": "170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCB"
},
{
"TagCount": 1067,
"AudioHeaderData": "AF00119056E500",
"VideoHeaderData": "170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCBFFFFFFFFFFFF"
}
]
}

View File

@ -1 +0,0 @@
文件连续出现多个不同的 Header

View File

@ -1,13 +0,0 @@
{
"AlternativeHeaderCount": 5,
"AllowedComments": {
"DecodingHeader": 1
},
"Files": [
{
"TagCount": 78,
"AudioHeaderData": "AF00119056E500",
"VideoHeaderData": "170000000001640028FFE1001D67640028ACD940780227E59A808080A0000003002000000791E30632C001000468EF8FCB"
}
]
}

View File

@ -1,28 +0,0 @@
PK 直播
音视频顺序有问题
```xml
<Tag Type="Audio" Flag="None" Size="346" Timestamp="2937" />
<Tag Type="Audio" Flag="None" Size="343" Timestamp="2960" />
<Tag Type="Video" Flag="None" Size="9546" Timestamp="2956">
<Nalus StartPosition="9" FullSize="3080" Type="CodedSliceOfANonIdrPicture" />
<Nalus StartPosition="3093" FullSize="6453" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="392" Timestamp="2983" />
<Tag Type="Audio" Flag="None" Size="357" Timestamp="3007" />
<Tag Type="Video" Flag="Keyframe" Size="49883" Timestamp="2997">
<Nalus StartPosition="9" FullSize="23" Type="Sps" />
<Nalus StartPosition="36" FullSize="4" Type="Pps" />
<Nalus StartPosition="44" FullSize="313" Type="Sei" />
<Nalus StartPosition="361" FullSize="25116" Type="CodedSliceOfAnIdrPicture" />
<Nalus StartPosition="25481" FullSize="24402" Type="CodedSliceOfAnIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="350" Timestamp="3030" />
<Tag Type="Audio" Flag="None" Size="364" Timestamp="3053" />
<Tag Type="Video" Flag="None" Size="2544" Timestamp="3038">
<Nalus StartPosition="9" FullSize="567" Type="CodedSliceOfANonIdrPicture" />
<Nalus StartPosition="580" FullSize="1964" Type="CodedSliceOfANonIdrPicture" />
</Tag>
<Tag Type="Audio" Flag="None" Size="367" Timestamp="3077" />
```

View File

@ -1,12 +0,0 @@
{
"AllowedComments": {
"TimestampOffset": 105
},
"Files": [
{
"VideoHeaderData": "17000000000164001F030100176764001FACB402802DD0800000030080000018478C195001000468EF3CB0",
"AudioHeaderData": "AF001210",
"TagCount": 7002
}
]
}

View File

@ -1,26 +0,0 @@
问题: 开头缺一个 header + 一段只有音频的数据
```xml
Script Tag 略
<Tag Type="Audio" Flag="Header" Size="7" Timestamp="0">
<BinaryData>AF00119056E500</BinaryData>
</Tag>
<Tag Type="Audio" Flag="Header" Size="7" Timestamp="0">
<BinaryData>AF00119056E500</BinaryData>
</Tag>
<Tag Type="Audio" Flag="None" Size="8" Timestamp="0" />
...
全 Audio Data
...
<Tag Type="Video" Flag="Header Keyframe" Size="59" Timestamp="8011">
<BinaryData>170000000001640028FFE1002767640028AC2CA501E0089F97016A020202800001F40000753070000016E36000016E360DDE5C1401000468EB8F2C</BinaryData>
</Tag>
<Tag Type="Video" Flag="Keyframe" Size="231930" Timestamp="8045">
<Nalus StartPosition="9" FullSize="12" Type="Sei" />
<Nalus StartPosition="25" FullSize="8" Type="Sei" />
<Nalus StartPosition="37" FullSize="231893" Type="CodedSliceOfAnIdrPicture" />
</Tag>
...
正常数据
```

View File

@ -1,22 +0,0 @@
{
"AlternativeHeaderCount": 0,
"AllowedComments": {
"DecodingHeader": 1,
"TimestampOffset": 1,
"TimestampJump": 1
},
"Files": [
{
"TagCount": 375,
"VideoHeaderData": "AF00119056E500",
"AudioHeaderData": null,
"___备注": "这部分测试逻辑还需要再修改",
"SkipTagCheck": true
},
{
"TagCount": 366,
"VideoHeaderData": "170000000001640028FFE1002767640028AC2CA501E0089F97016A020202800001F40000753070000016E36000016E360DDE5C1401000468EB8F2C",
"AudioHeaderData": "AF00119056E500"
}
]
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>