diff --git a/BililiveRecorder.Flv/Tag.cs b/BililiveRecorder.Flv/Tag.cs index 400c574..5b673cc 100644 --- a/BililiveRecorder.Flv/Tag.cs +++ b/BililiveRecorder.Flv/Tag.cs @@ -1,4 +1,6 @@ using System; +using System.Buffers; +using System.Buffers.Binary; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; @@ -37,6 +39,9 @@ namespace BililiveRecorder.Flv [XmlElement] public ScriptTagBody? ScriptData { get; set; } + [XmlElement] + public TagExtraData? ExtraData { get; set; } + [XmlElement] public List? Nalus { get; set; } @@ -103,7 +108,56 @@ namespace BililiveRecorder.Flv }; } - private static readonly FarmHash64 farmHash64 = new FarmHash64(); + 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.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.Shared.Return(buffer); + } + + binaryData.Position = old_position; + this.ExtraData = extra; + } + return this.ExtraData; + } public string? UpdateDataHash() { @@ -156,11 +210,13 @@ namespace BililiveRecorder.Flv return result; } - internal static string ByteArrayToHexString(byte[] bytes) + 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[bytes.Length * 2]; - for (var i = 0; i < bytes.Length; i++) + var result = new char[length * 2]; + for (var i = start; i < length; i++) { var val = lookup32[bytes[i]]; result[2 * i] = (char)val; diff --git a/BililiveRecorder.Flv/TagExtraData.cs b/BililiveRecorder.Flv/TagExtraData.cs new file mode 100644 index 0000000..8861744 --- /dev/null +++ b/BililiveRecorder.Flv/TagExtraData.cs @@ -0,0 +1,24 @@ +using System.Xml.Serialization; + +namespace BililiveRecorder.Flv +{ + public sealed class TagExtraData + { + public TagExtraData() + { + } + + [XmlAttribute] + public string FirstBytes { get; set; } = string.Empty; + + [XmlAttribute] + public int CompositionTime { get; set; } + + [XmlAttribute] + public int FinalTime { get; set; } + + public bool ShouldSerializeCompositionTime() => this.CompositionTime != int.MinValue; + + public bool ShouldSerializeFinalTime() => this.CompositionTime != int.MinValue; + } +} diff --git a/BililiveRecorder.ToolBox/Tool/Export/ExportHandler.cs b/BililiveRecorder.ToolBox/Tool/Export/ExportHandler.cs index aed043e..ad29d24 100644 --- a/BililiveRecorder.ToolBox/Tool/Export/ExportHandler.cs +++ b/BililiveRecorder.ToolBox/Tool/Export/ExportHandler.cs @@ -73,6 +73,7 @@ namespace BililiveRecorder.ToolBox.Tool.Export if (tag is null) break; + tag.UpdateExtraData(); tag.UpdateDataHash(); if (!tag.ShouldSerializeBinaryDataForSerializationUseOnly()) tag.BinaryData?.Dispose();