mirror of
https://github.com/BililiveRecorder/BililiveRecorder.git
synced 2024-11-15 19:22:19 +08:00
Preliminary work on calculating composition time offset
This commit is contained in:
parent
2225ca2dd3
commit
018390a065
66
BililiveRecorder.Flv/BinaryConvertUtilities.cs
Normal file
66
BililiveRecorder.Flv/BinaryConvertUtilities.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace BililiveRecorder.Flv
|
||||
{
|
||||
internal static class BinaryConvertUtilities
|
||||
{
|
||||
private static readonly uint[] _lookup32 = CreateLookup32();
|
||||
|
||||
private static uint[] CreateLookup32()
|
||||
{
|
||||
var result = new uint[256];
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
var s = i.ToString("X2");
|
||||
result[i] = s[0] + ((uint)s[1] << 16);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static string ByteArrayToHexString(byte[] bytes) => ByteArrayToHexString(bytes, 0, bytes.Length);
|
||||
|
||||
internal static string ByteArrayToHexString(byte[] bytes, int start, int length)
|
||||
{
|
||||
var lookup32 = _lookup32;
|
||||
var result = new char[length * 2];
|
||||
for (var i = start; i < length; i++)
|
||||
{
|
||||
var val = lookup32[bytes[i]];
|
||||
result[2 * i] = (char)val;
|
||||
result[2 * i + 1] = (char)(val >> 16);
|
||||
}
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
internal static byte[] HexStringToByteArray(string hex)
|
||||
{
|
||||
var bytes = new byte[hex.Length / 2];
|
||||
for (var i = 0; i < hex.Length; i += 2)
|
||||
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
internal static string StreamToHexString(Stream stream)
|
||||
{
|
||||
var lookup32 = _lookup32;
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
var result = new char[stream.Length * 2];
|
||||
for (var i = 0; i < stream.Length; i++)
|
||||
{
|
||||
var val = lookup32[stream.ReadByte()];
|
||||
result[2 * i] = (char)val;
|
||||
result[2 * i + 1] = (char)(val >> 16);
|
||||
}
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
internal static MemoryStream HexStringToMemoryStream(string hex)
|
||||
{
|
||||
var stream = new MemoryStream(hex.Length / 2);
|
||||
for (var i = 0; i < hex.Length; i += 2)
|
||||
stream.WriteByte(Convert.ToByte(hex.Substring(i, 2), 16));
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
}
|
58
BililiveRecorder.Flv/Int24.cs
Normal file
58
BililiveRecorder.Flv/Int24.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace BililiveRecorder.Flv
|
||||
{
|
||||
public class Int24
|
||||
{
|
||||
public static int ReadInt24(ReadOnlySpan<byte> source)
|
||||
{
|
||||
if (source.Length < 3)
|
||||
throw new ArgumentException("source must longer than 3 bytes", nameof(source));
|
||||
|
||||
const int mask = -16777216;
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(4);
|
||||
try
|
||||
{
|
||||
buffer[0] = 0;
|
||||
buffer[1] = source[0];
|
||||
buffer[2] = source[1];
|
||||
buffer[3] = source[2];
|
||||
|
||||
var value = BinaryPrimitives.ReadInt32BigEndian(buffer);
|
||||
|
||||
if ((value & 0x00800000) > 0)
|
||||
value |= mask;
|
||||
else
|
||||
value &= ~mask;
|
||||
|
||||
return value;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteInt24(Span<byte> destination, int value)
|
||||
{
|
||||
if (value is > 8388607 or < -8388608)
|
||||
throw new ArgumentOutOfRangeException(nameof(value), "int24 should be between -8388608 and 8388607");
|
||||
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(4);
|
||||
try
|
||||
{
|
||||
BinaryPrimitives.WriteInt32BigEndian(buffer, value);
|
||||
|
||||
destination[0] = buffer[1];
|
||||
destination[1] = buffer[2];
|
||||
destination[2] = buffer[3];
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -57,7 +57,7 @@ namespace BililiveRecorder.Flv.Parser
|
|||
/// 实现二进制数据的解析
|
||||
/// </summary>
|
||||
/// <returns>解析出的 Flv Tag</returns>
|
||||
private async Task<Tag?> ReadNextTagAsync(CancellationToken cancellationToken = default)
|
||||
private async Task<Tag?> ReadNextTagAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
|
@ -291,11 +291,15 @@ namespace BililiveRecorder.Flv.Parser
|
|||
tag.Nalus = nalus;
|
||||
}
|
||||
|
||||
tag.BinaryData = tagBodyStream;
|
||||
tag.UpdateExtraData();
|
||||
|
||||
// Dispose Stream If Not Needed
|
||||
if (!this.skipData || tag.ShouldSerializeBinaryDataForSerializationUseOnly())
|
||||
tag.BinaryData = tagBodyStream;
|
||||
else
|
||||
if (this.skipData && !tag.ShouldSerializeBinaryDataForSerializationUseOnly())
|
||||
{
|
||||
tag.BinaryData = null;
|
||||
tagBodyStream.Dispose();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace BililiveRecorder.Flv.Pipeline
|
|||
builder
|
||||
.Add<HandleEndTagRule>()
|
||||
.Add<HandleDelayedAudioHeaderRule>()
|
||||
//.Add<UpdateCompositionTimeRule>()
|
||||
.Add<UpdateCompositionTimeRule>()
|
||||
.Add<UpdateTimestampOffsetRule>()
|
||||
.Add<UpdateTimestampJumpRule>()
|
||||
.Add<HandleNewScriptRule>()
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BililiveRecorder.Flv.Pipeline.Actions;
|
||||
|
||||
namespace BililiveRecorder.Flv.Pipeline.Rules
|
||||
{
|
||||
|
@ -6,7 +9,55 @@ namespace BililiveRecorder.Flv.Pipeline.Rules
|
|||
{
|
||||
public void Run(FlvProcessingContext context, Action next)
|
||||
{
|
||||
context.PerActionRun(this.RunPerAction);
|
||||
next();
|
||||
}
|
||||
|
||||
private IEnumerable<PipelineAction?> RunPerAction(FlvProcessingContext context, PipelineAction action)
|
||||
{
|
||||
if (action is not PipelineDataAction data)
|
||||
{
|
||||
yield return action;
|
||||
yield break;
|
||||
}
|
||||
|
||||
var videoTags = data.Tags.Where(x => x.Type == TagType.Video);
|
||||
|
||||
if (!videoTags.Any())
|
||||
{
|
||||
// skip
|
||||
yield return data;
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (videoTags.Any(x => x.ExtraData is null))
|
||||
{
|
||||
context.AddComment(new ProcessingComment(CommentType.Unrepairable, "有 Tag 的 ExtraData 为 null,请检查文件或联系开发者"));
|
||||
yield break;
|
||||
}
|
||||
|
||||
var compositionOffset = videoTags.Min(x => x.ExtraData!.CompositionTime);
|
||||
|
||||
if (compositionOffset is <= 0 or >= int.MaxValue)
|
||||
{
|
||||
// skip
|
||||
yield return data;
|
||||
yield break;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var tag in data.Tags)
|
||||
{
|
||||
if (tag.Type != TagType.Video)
|
||||
continue;
|
||||
|
||||
System.Diagnostics.Debug.WriteLine("CompositionOffset: " + compositionOffset);
|
||||
tag.ExtraData!.CompositionTime -= compositionOffset;
|
||||
tag.Timestamp += compositionOffset;
|
||||
}
|
||||
yield return data;
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
@ -8,7 +6,6 @@ using System.IO;
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Xml.Serialization;
|
||||
using BililiveRecorder.Flv.Amf;
|
||||
using FastHashes;
|
||||
|
||||
namespace BililiveRecorder.Flv
|
||||
{
|
||||
|
@ -108,79 +105,6 @@ namespace BililiveRecorder.Flv
|
|||
};
|
||||
}
|
||||
|
||||
private static readonly FarmHash64 farmHash64 = new();
|
||||
|
||||
public TagExtraData? UpdateExtraData()
|
||||
{
|
||||
if (this.BinaryData is not { } binaryData || binaryData.Length < 5)
|
||||
{
|
||||
this.ExtraData = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var old_position = binaryData.Position;
|
||||
var extra = new TagExtraData();
|
||||
|
||||
binaryData.Position = 0;
|
||||
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(5);
|
||||
try
|
||||
{
|
||||
binaryData.Read(buffer, 0, 5);
|
||||
extra.FirstBytes = BinaryConvertUtilities.ByteArrayToHexString(buffer, 0, 2);
|
||||
|
||||
if (this.Type == TagType.Video)
|
||||
{
|
||||
buffer[1] = 0;
|
||||
|
||||
const int mask = -16777216;
|
||||
var value = BinaryPrimitives.ReadInt32BigEndian(buffer.AsSpan(1));
|
||||
if ((value & 0x00800000) > 0)
|
||||
value |= mask;
|
||||
else
|
||||
value &= ~mask;
|
||||
|
||||
extra.CompositionTime = value;
|
||||
extra.FinalTime = this.Timestamp + value;
|
||||
}
|
||||
else
|
||||
{
|
||||
extra.CompositionTime = int.MinValue;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
|
||||
binaryData.Position = old_position;
|
||||
this.ExtraData = extra;
|
||||
}
|
||||
return this.ExtraData;
|
||||
}
|
||||
|
||||
public string? UpdateDataHash()
|
||||
{
|
||||
if (this.BinaryData is null)
|
||||
{
|
||||
this.DataHash = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var buffer = this.BinaryData.GetBuffer();
|
||||
this.DataHash = BinaryConvertUtilities.ByteArrayToHexString(farmHash64.ComputeHash(buffer, (int)this.BinaryData.Length));
|
||||
|
||||
if (this.Nalus?.Count > 0)
|
||||
{
|
||||
foreach (var nalu in this.Nalus)
|
||||
{
|
||||
nalu.NaluHash = BinaryConvertUtilities.ByteArrayToHexString(farmHash64.ComputeHash(buffer, nalu.StartPosition, (int)nalu.FullSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.DataHash;
|
||||
}
|
||||
|
||||
private string DebuggerDisplay => string.Format("{0}, {1}{2}{3}, TS={4}, Size={5}",
|
||||
this.Type switch
|
||||
{
|
||||
|
@ -194,66 +118,5 @@ namespace BililiveRecorder.Flv
|
|||
this.Flag.HasFlag(TagFlag.End) ? "E" : "-",
|
||||
this.Timestamp,
|
||||
this.Size);
|
||||
|
||||
private static class BinaryConvertUtilities
|
||||
{
|
||||
private static readonly uint[] _lookup32 = CreateLookup32();
|
||||
|
||||
private static uint[] CreateLookup32()
|
||||
{
|
||||
var result = new uint[256];
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
var s = i.ToString("X2");
|
||||
result[i] = s[0] + ((uint)s[1] << 16);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static string ByteArrayToHexString(byte[] bytes) => ByteArrayToHexString(bytes, 0, bytes.Length);
|
||||
|
||||
internal static string ByteArrayToHexString(byte[] bytes, int start, int length)
|
||||
{
|
||||
var lookup32 = _lookup32;
|
||||
var result = new char[length * 2];
|
||||
for (var i = start; i < length; i++)
|
||||
{
|
||||
var val = lookup32[bytes[i]];
|
||||
result[2 * i] = (char)val;
|
||||
result[2 * i + 1] = (char)(val >> 16);
|
||||
}
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
internal static byte[] HexStringToByteArray(string hex)
|
||||
{
|
||||
var bytes = new byte[hex.Length / 2];
|
||||
for (var i = 0; i < hex.Length; i += 2)
|
||||
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
internal static string StreamToHexString(Stream stream)
|
||||
{
|
||||
var lookup32 = _lookup32;
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
var result = new char[stream.Length * 2];
|
||||
for (var i = 0; i < stream.Length; i++)
|
||||
{
|
||||
var val = lookup32[stream.ReadByte()];
|
||||
result[2 * i] = (char)val;
|
||||
result[2 * i + 1] = (char)(val >> 16);
|
||||
}
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
internal static MemoryStream HexStringToMemoryStream(string hex)
|
||||
{
|
||||
var stream = new MemoryStream(hex.Length / 2);
|
||||
for (var i = 0; i < hex.Length; i += 2)
|
||||
stream.WriteByte(Convert.ToByte(hex.Substring(i, 2), 16));
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Buffers.Binary;
|
|||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using FastHashes;
|
||||
|
||||
namespace BililiveRecorder.Flv
|
||||
{
|
||||
|
@ -43,24 +44,34 @@ namespace BililiveRecorder.Flv
|
|||
Stream? data = null;
|
||||
try
|
||||
{
|
||||
// 先准备 Tag 的 body 部分
|
||||
if (tag.IsScript())
|
||||
{
|
||||
// Script Tag 写入时使用 Script Tag 序列化
|
||||
if (tag.ScriptData is null)
|
||||
throw new Exception("BinaryData is null");
|
||||
throw new Exception("ScriptData is null");
|
||||
|
||||
data = memoryStreamProvider?.CreateMemoryStream(nameof(TagExtentions) + ":" + nameof(WriteTo) + ":TagBodyTemp") ?? new MemoryStream();
|
||||
tag.ScriptData.WriteTo(data);
|
||||
}
|
||||
else if (tag.Nalus != null)
|
||||
{
|
||||
// 如果 Tag 有 Nalu 信息,则按照 Nalus 里面的指示分段复制
|
||||
// 这个 Tag 一定是 Video
|
||||
if (tag.BinaryData is null)
|
||||
throw new Exception("BinaryData is null");
|
||||
|
||||
data = memoryStreamProvider?.CreateMemoryStream(nameof(TagExtentions) + ":" + nameof(WriteTo) + ":TagBodyTemp") ?? new MemoryStream();
|
||||
|
||||
tag.BinaryData.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
tag.BinaryData.Read(buffer, 0, 5);
|
||||
|
||||
if (tag.ExtraData is not null)
|
||||
{
|
||||
// 如果有 ExtraData 则以这里面的 composition time 为准
|
||||
Int24.WriteInt24(buffer.AsSpan(2, 3), tag.ExtraData.CompositionTime);
|
||||
}
|
||||
|
||||
data.Write(buffer, 0, 5);
|
||||
|
||||
foreach (var nalu in tag.Nalus)
|
||||
|
@ -77,12 +88,27 @@ namespace BililiveRecorder.Flv
|
|||
if (tag.BinaryData is null)
|
||||
throw new Exception("BinaryData is null");
|
||||
|
||||
dispose = false;
|
||||
data = tag.BinaryData;
|
||||
if (tag.Type == TagType.Video && tag.ExtraData is not null)
|
||||
{
|
||||
// 复制并修改 composition time
|
||||
data = memoryStreamProvider?.CreateMemoryStream(nameof(TagExtentions) + ":" + nameof(WriteTo) + ":TagBodyTemp") ?? new MemoryStream();
|
||||
tag.BinaryData.CopyTo(data);
|
||||
|
||||
Int24.WriteInt24(buffer.AsSpan(0, 3), tag.ExtraData.CompositionTime);
|
||||
data.Seek(2, SeekOrigin.Begin);
|
||||
data.Read(buffer, 0, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 直接复用原数据
|
||||
dispose = false;
|
||||
data = tag.BinaryData;
|
||||
}
|
||||
}
|
||||
|
||||
data.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// 序列号 Tag 的 header 部分
|
||||
BinaryPrimitives.WriteUInt32BigEndian(new Span<byte>(buffer, 0, 4), (uint)data.Length);
|
||||
buffer[0] = (byte)tag.Type;
|
||||
|
||||
|
@ -100,6 +126,7 @@ namespace BililiveRecorder.Flv
|
|||
buffer[9] = 0;
|
||||
buffer[10] = 0;
|
||||
|
||||
// 写入
|
||||
await target.WriteAsync(buffer, 0, 11).ConfigureAwait(false);
|
||||
await data.CopyToAsync(target).ConfigureAwait(false);
|
||||
|
||||
|
@ -113,5 +140,69 @@ namespace BililiveRecorder.Flv
|
|||
data?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static TagExtraData? UpdateExtraData(this Tag tag)
|
||||
{
|
||||
if (tag.BinaryData is not { } binaryData || binaryData.Length < 5)
|
||||
{
|
||||
tag.ExtraData = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var old_position = binaryData.Position;
|
||||
var extra = new TagExtraData();
|
||||
|
||||
binaryData.Position = 0;
|
||||
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(5);
|
||||
try
|
||||
{
|
||||
binaryData.Read(buffer, 0, 5);
|
||||
extra.FirstBytes = BinaryConvertUtilities.ByteArrayToHexString(buffer, 0, 2);
|
||||
|
||||
if (tag.Type == TagType.Video)
|
||||
{
|
||||
extra.CompositionTime = Int24.ReadInt24(buffer.AsSpan(2, 3));
|
||||
extra.FinalTime = tag.Timestamp + extra.CompositionTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
extra.CompositionTime = int.MinValue;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
|
||||
binaryData.Position = old_position;
|
||||
tag.ExtraData = extra;
|
||||
}
|
||||
return tag.ExtraData;
|
||||
}
|
||||
|
||||
private static readonly FarmHash64 farmHash64 = new();
|
||||
|
||||
public static string? UpdateDataHash(this Tag tag)
|
||||
{
|
||||
if (tag.BinaryData is null)
|
||||
{
|
||||
tag.DataHash = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var buffer = tag.BinaryData.GetBuffer();
|
||||
tag.DataHash = BinaryConvertUtilities.ByteArrayToHexString(farmHash64.ComputeHash(buffer, (int)tag.BinaryData.Length));
|
||||
|
||||
if (tag.Nalus?.Count > 0)
|
||||
{
|
||||
foreach (var nalu in tag.Nalus)
|
||||
{
|
||||
nalu.NaluHash = BinaryConvertUtilities.ByteArrayToHexString(farmHash64.ComputeHash(buffer, nalu.StartPosition, (int)nalu.FullSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
return tag.DataHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,7 @@ namespace BililiveRecorder.ToolBox.ProcessingRules
|
|||
|
||||
if (tag.ExtraData is { } extra)
|
||||
this.frameComposition.Add(extra.CompositionTime);
|
||||
else if (tag.UpdateExtraData() is { } extra2)
|
||||
this.frameComposition.Add(extra2.CompositionTime);
|
||||
|
||||
}
|
||||
else if (tag.Type == TagType.Audio && tag.Flag == TagFlag.None)
|
||||
{
|
||||
|
|
|
@ -73,7 +73,6 @@ namespace BililiveRecorder.ToolBox.Tool.Export
|
|||
if (tag is null)
|
||||
break;
|
||||
|
||||
tag.UpdateExtraData();
|
||||
tag.UpdateDataHash();
|
||||
if (!tag.ShouldSerializeBinaryDataForSerializationUseOnly())
|
||||
tag.BinaryData?.Dispose();
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
46
test/BililiveRecorder.Flv.Tests/FlvTests/Int24Tests.cs
Normal file
46
test/BililiveRecorder.Flv.Tests/FlvTests/Int24Tests.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
namespace BililiveRecorder.Flv.Tests.FlvTests
|
||||
{
|
||||
public class Int24Tests
|
||||
{
|
||||
private static IEnumerable<object[]> TestData()
|
||||
{
|
||||
yield return new object[] { 0, new byte[] { 0, 0, 0 } };
|
||||
yield return new object[] { 1, new byte[] { 0, 0, 1 } };
|
||||
yield return new object[] { -1, new byte[] { 0xFF, 0xFF, 0xFF } };
|
||||
yield return new object[] { -8388608, new byte[] { 0x80, 0, 0 } };
|
||||
yield return new object[] { 8388607, new byte[] { 0x7F, 0xFF, 0xFF } };
|
||||
yield return new object[] { -5517841, new byte[] { 0xAB, 0xCD, 0xEF } };
|
||||
}
|
||||
|
||||
[Theory, MemberData(nameof(TestData))]
|
||||
public void Int24SerializeCorrectly(int number, byte[] bytes)
|
||||
{
|
||||
var result = new byte[3];
|
||||
Int24.WriteInt24(result, number);
|
||||
Assert.Equal(bytes, result);
|
||||
}
|
||||
|
||||
[Theory, MemberData(nameof(TestData))]
|
||||
public void Int24DeserializeCorrectly(int number, byte[] bytes)
|
||||
{
|
||||
var result = Int24.ReadInt24(bytes);
|
||||
Assert.Equal(number, result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(8388608)]
|
||||
[InlineData(-8388609)]
|
||||
public void Int24ThrowOnOutOfRange(int number)
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
var result = new byte[3];
|
||||
Int24.WriteInt24(result, number);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ namespace BililiveRecorder.Flv.Tests.FlvTests
|
|||
{
|
||||
[UsesVerify]
|
||||
[ExpectationPath("FlvParser")]
|
||||
public class ParserTest
|
||||
public class ParserTests
|
||||
{
|
||||
[Theory]
|
||||
[Expectation("XmlOutput")]
|
Loading…
Reference in New Issue
Block a user