@@ -0,0 +1 @@ | |||||
ffmpeg -i demo.mp4 -c copy -f flv -vcodec h264 -acodec aac demo_flv.flv |
@@ -0,0 +1,20 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netcoreapp3.0</TargetFramework> | |||||
<IsPackable>false</IsPackable> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" /> | |||||
<PackageReference Include="xunit" Version="2.4.0" /> | |||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" /> | |||||
<PackageReference Include="coverlet.collector" Version="1.0.1" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\JT1078.Flv\JT1078.Flv.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -0,0 +1,29 @@ | |||||
using JT1078.Flv.Metadata; | |||||
using JT1078.Flv.Extensions; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Xunit; | |||||
namespace JT1078.Flv.Test.Metadata | |||||
{ | |||||
public class Amf3MetadataTest | |||||
{ | |||||
[Fact] | |||||
public void Amf3Metadata_Duration_Test_1_1() | |||||
{ | |||||
Amf3Metadata_Duration amf3Metadata = new Amf3Metadata_Duration(); | |||||
amf3Metadata.Value = 7.22100; | |||||
var hex=amf3Metadata.ToBuffer().ToArray().ToHexString(); | |||||
Assert.Equal("00086475726174696F6E00401CE24DD2F1A9FC", hex); | |||||
} | |||||
[Fact] | |||||
public void Test1() | |||||
{ | |||||
byte[] d1 = new byte[] { 0xFC, 0xA9, 0xF1, 0xD2, 0x4D, 0xE2, 0x1c, 0x40 }; | |||||
var buffer = BitConverter.GetBytes(7.22100); | |||||
//flv需要倒序的 | |||||
} | |||||
} | |||||
} |
@@ -4,9 +4,9 @@ using System.Text; | |||||
namespace JT1078.Flv.Enums | namespace JT1078.Flv.Enums | ||||
{ | { | ||||
public enum AvcPacketType | |||||
public enum AvcPacketType:byte | |||||
{ | { | ||||
SequenceHeader=1, | |||||
SequenceHeader = 0, | |||||
Raw, | Raw, | ||||
AVCEndSequence | AVCEndSequence | ||||
} | } | ||||
@@ -0,0 +1,28 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Flv.Enums | |||||
{ | |||||
public enum Amf3Type : byte | |||||
{ | |||||
Undefined, | |||||
Null, | |||||
False, | |||||
True, | |||||
Integer, | |||||
Double, | |||||
String, | |||||
XmlDocument, | |||||
Date, | |||||
Array, | |||||
Object, | |||||
Xml, | |||||
ByteArray, | |||||
VectorInt, | |||||
VectorUInt, | |||||
VectorDouble, | |||||
VectorObject, | |||||
Dictionary | |||||
} | |||||
} |
@@ -12,6 +12,6 @@ namespace JT1078.Flv.Enums | |||||
Vp6, | Vp6, | ||||
Vp6WithAlpha, | Vp6WithAlpha, | ||||
ScreenVideo2, | ScreenVideo2, | ||||
Avc | |||||
AvcVideoPacke | |||||
} | } | ||||
} | } |
@@ -4,7 +4,7 @@ using System.Text; | |||||
namespace JT1078.Flv.Enums | namespace JT1078.Flv.Enums | ||||
{ | { | ||||
public enum FrameType | |||||
public enum FrameType:byte | |||||
{ | { | ||||
KeyFrame = 1, | KeyFrame = 1, | ||||
InterFrame, | InterFrame, | ||||
@@ -0,0 +1,19 @@ | |||||
using JT1078.Flv.Metadata; | |||||
using System; | |||||
using System.Buffers.Binary; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Linq; | |||||
namespace JT1078.Flv.Extensions | |||||
{ | |||||
public static class Amf3Extensions | |||||
{ | |||||
public static void WriteDouble(this IAmf3Metadata metadata, Span<byte> value) | |||||
{ | |||||
var flvBuffer = BitConverter.GetBytes((double)metadata.Value).AsSpan(); | |||||
flvBuffer.Reverse(); | |||||
flvBuffer.CopyTo(value); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,129 @@ | |||||
using System; | |||||
namespace JT1078.Flv.Extensions | |||||
{ | |||||
/// <summary> | |||||
/// | |||||
/// ref:"www.codeproject.com/tips/447938/high-performance-csharp-byte-array-to-hex-string-t" | |||||
/// </summary> | |||||
public static partial class HexExtensions | |||||
{ | |||||
public static string ToHexString(this byte[] source) | |||||
{ | |||||
return HexUtil.DoHexDump(source, 0, source.Length).ToUpper(); | |||||
} | |||||
public static int WriteHexStringLittle(byte[] bytes, int offset, string data, int len) | |||||
{ | |||||
if (data == null) data = ""; | |||||
data = data.Replace(" ", ""); | |||||
int startIndex = 0; | |||||
if (data.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) | |||||
{ | |||||
startIndex = 2; | |||||
} | |||||
int length = len; | |||||
if (length == -1) | |||||
{ | |||||
length = (data.Length - startIndex) / 2; | |||||
} | |||||
int noOfZero = length * 2 + startIndex - data.Length; | |||||
if (noOfZero > 0) | |||||
{ | |||||
data = data.Insert(startIndex, new string('0', noOfZero)); | |||||
} | |||||
int byteIndex = 0; | |||||
while (startIndex < data.Length && byteIndex < length) | |||||
{ | |||||
bytes[offset + byteIndex] = Convert.ToByte(data.Substring(startIndex, 2), 16); | |||||
startIndex += 2; | |||||
byteIndex++; | |||||
} | |||||
return length; | |||||
} | |||||
/// <summary> | |||||
/// 16进制字符串转16进制数组 | |||||
/// </summary> | |||||
/// <param name="hexString"></param> | |||||
/// <param name="separator"></param> | |||||
/// <returns></returns> | |||||
public static byte[] ToHexBytes(this string hexString) | |||||
{ | |||||
hexString = hexString.Replace(" ", ""); | |||||
byte[] buf = new byte[hexString.Length / 2]; | |||||
ReadOnlySpan<char> readOnlySpan = hexString.AsSpan(); | |||||
for (int i = 0; i < hexString.Length; i++) | |||||
{ | |||||
if (i % 2 == 0) | |||||
{ | |||||
buf[i / 2] = Convert.ToByte(readOnlySpan.Slice(i, 2).ToString(), 16); | |||||
} | |||||
} | |||||
return buf; | |||||
} | |||||
public static string ReadHexStringLittle(ReadOnlySpan<byte> read, ref int offset, int len) | |||||
{ | |||||
ReadOnlySpan<byte> source = read.Slice(offset, len); | |||||
string hex = HexUtil.DoHexDump(read, offset, len); | |||||
offset += len; | |||||
return hex; | |||||
} | |||||
/// <summary> | |||||
/// Copyright (c) Microsoft. All rights reserved. | |||||
/// Licensed under the MIT license. See LICENSE file in the project root for full license information. | |||||
/// <see cref="DotNetty.Buffers.ByteBufferUtil"/> | |||||
/// </summary> | |||||
} | |||||
public static class HexUtil | |||||
{ | |||||
static readonly char[] HexdumpTable = new char[256 * 4]; | |||||
static HexUtil() | |||||
{ | |||||
char[] digits = "0123456789ABCDEF".ToCharArray(); | |||||
for (int i = 0; i < 256; i++) | |||||
{ | |||||
HexdumpTable[i << 1] = digits[(int)((uint)i >> 4 & 0x0F)]; | |||||
HexdumpTable[(i << 1) + 1] = digits[i & 0x0F]; | |||||
} | |||||
} | |||||
public static string DoHexDump(ReadOnlySpan<byte> buffer, int fromIndex, int length) | |||||
{ | |||||
if (length == 0) | |||||
{ | |||||
return ""; | |||||
} | |||||
int endIndex = fromIndex + length; | |||||
var buf = new char[length << 1]; | |||||
int srcIdx = fromIndex; | |||||
int dstIdx = 0; | |||||
for (; srcIdx < endIndex; srcIdx++, dstIdx += 2) | |||||
{ | |||||
Array.Copy(HexdumpTable, buffer[srcIdx] << 1, buf, dstIdx, 2); | |||||
} | |||||
return new string(buf); | |||||
} | |||||
public static string DoHexDump(byte[] array, int fromIndex, int length) | |||||
{ | |||||
if (length == 0) | |||||
{ | |||||
return ""; | |||||
} | |||||
int endIndex = fromIndex + length; | |||||
var buf = new char[length << 1]; | |||||
int srcIdx = fromIndex; | |||||
int dstIdx = 0; | |||||
for (; srcIdx < endIndex; srcIdx++, dstIdx += 2) | |||||
{ | |||||
Array.Copy(HexdumpTable, (array[srcIdx] & 0xFF) << 1, buf, dstIdx, 2); | |||||
} | |||||
return new string(buf); | |||||
} | |||||
} | |||||
} |
@@ -10,6 +10,6 @@ namespace JT1078.Flv | |||||
/// 前一个tag的长度 | /// 前一个tag的长度 | ||||
/// </summary> | /// </summary> | ||||
public uint PreviousTagSize { get; set; } | public uint PreviousTagSize { get; set; } | ||||
public FlvTag Tag { get; set; } | |||||
public FlvTags Tag { get; set; } | |||||
} | } | ||||
} | } |
@@ -1,8 +1,11 @@ | |||||
namespace JT1078.Flv | |||||
using JT1078.Flv.Enums; | |||||
using JT1078.Flv.Metadata; | |||||
namespace JT1078.Flv | |||||
{ | { | ||||
public class FlvTag | |||||
public class FlvTags | |||||
{ | { | ||||
public byte Type { get; set; } | |||||
public TagType Type { get; set; } | |||||
/// <summary> | /// <summary> | ||||
/// Tag Data部分大小 | /// Tag Data部分大小 | ||||
/// 3个字节 | /// 3个字节 | ||||
@@ -22,6 +25,10 @@ | |||||
/// <summary> | /// <summary> | ||||
/// 根据tag类型 | /// 根据tag类型 | ||||
/// </summary> | /// </summary> | ||||
public byte[] TagData { get; set; } | |||||
public VideoTags VideoTagsData { get; set; } | |||||
/// <summary> | |||||
/// 根据tag类型 | |||||
/// </summary> | |||||
public Amf3 DataTagsData { get; set; } | |||||
} | } | ||||
} | } |
@@ -1,10 +1,12 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <Project Sdk="Microsoft.NET.Sdk"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFramework>netstandard2.0</TargetFramework> | |||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks> | |||||
<LangVersion>8.0</LangVersion> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | |||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> | |||||
<PackageReference Include="System.Memory" Version="4.5.3" /> | <PackageReference Include="System.Memory" Version="4.5.3" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
</Project> | </Project> |
@@ -7,13 +7,14 @@ namespace JT1078.Flv.MessagePack | |||||
ref partial struct FlvMessagePackWriter | ref partial struct FlvMessagePackWriter | ||||
{ | { | ||||
private readonly static byte[] FixedAmf0Data = new byte[] { 0x6F, 0x6E, 0x4D, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61 }; | private readonly static byte[] FixedAmf0Data = new byte[] { 0x6F, 0x6E, 0x4D, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61 }; | ||||
public void WriteAmf0() | |||||
public void WriteAmf1() | |||||
{ | { | ||||
var span = writer.Free; | var span = writer.Free; | ||||
span[0] = 0x02; | span[0] = 0x02; | ||||
span[1] = 0; | span[1] = 0; | ||||
span[2] = 10; | span[2] = 10; | ||||
FixedAmf0Data.CopyTo(span.Slice(3)); | FixedAmf0Data.CopyTo(span.Slice(3)); | ||||
writer.Advance(13); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,4 +1,5 @@ | |||||
using System; | |||||
using JT1078.Flv.Metadata; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | using System.Text; | ||||
@@ -6,7 +7,22 @@ namespace JT1078.Flv.MessagePack | |||||
{ | { | ||||
ref partial struct FlvMessagePackWriter | ref partial struct FlvMessagePackWriter | ||||
{ | { | ||||
public void WriteAmf3(Amf3 amf3) | |||||
{ | |||||
WriteByte(amf3.DataType); | |||||
if(amf3.Amf3Metadatas!=null && amf3.Amf3Metadatas.Count > 0) | |||||
{ | |||||
WriteInt32(amf3.Amf3Metadatas.Count); | |||||
foreach(var item in amf3.Amf3Metadatas) | |||||
{ | |||||
//根据数据类型 | |||||
WriteArray(item.ToBuffer()); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
WriteInt32(0); | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -1,4 +1,5 @@ | |||||
using JT1078.Flv.Enums; | using JT1078.Flv.Enums; | ||||
using JT1078.Flv.Metadata; | |||||
using System; | using System; | ||||
using System.Buffers.Binary; | using System.Buffers.Binary; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
@@ -17,40 +18,84 @@ namespace JT1078.Flv.MessagePack | |||||
} | } | ||||
} | } | ||||
public void WriteFlvTag(FlvTag tag) | |||||
public void WriteFlvTag(FlvTags tag) | |||||
{ | { | ||||
WriteByte(tag.Type); | |||||
WriteByte((byte)tag.Type); | |||||
Skip(3, out int DataSizePosition); | Skip(3, out int DataSizePosition); | ||||
WriteUInt24(tag.Timestamp); | WriteUInt24(tag.Timestamp); | ||||
WriteByte(tag.TimestampExt); | WriteByte(tag.TimestampExt); | ||||
WriteUInt24(tag.StreamId); | WriteUInt24(tag.StreamId); | ||||
switch ((TagType)tag.Type) | |||||
switch (tag.Type) | |||||
{ | { | ||||
case TagType.Video: | case TagType.Video: | ||||
//VideoTag | |||||
WriteVideoTags(tag.VideoTagsData); | |||||
break; | break; | ||||
case TagType.ScriptData: | case TagType.ScriptData: | ||||
//DataTags | |||||
//flv Amf0 | //flv Amf0 | ||||
WriteAmf0(); | |||||
WriteAmf1(); | |||||
//flv Amf3 | //flv Amf3 | ||||
WriteAmf3(tag.DataTagsData); | |||||
break; | break; | ||||
case TagType.Audio: | case TagType.Audio: | ||||
//VIDEODATA | |||||
break; | break; | ||||
} | } | ||||
WriteInt32Return(GetCurrentPosition() - DataSizePosition - 3, DataSizePosition); | |||||
WriteInt24Return(GetCurrentPosition() - DataSizePosition - 3, DataSizePosition); | |||||
} | } | ||||
public void WriteUInt24(uint value) | public void WriteUInt24(uint value) | ||||
{ | { | ||||
BinaryPrimitives.WriteUInt32BigEndian(writer.Free, value); | |||||
var span = writer.Free; | |||||
span[0] = (byte)(value >> 16); | |||||
span[1] = (byte)(value >> 8); | |||||
span[2] = (byte)value; | |||||
writer.Advance(3); | writer.Advance(3); | ||||
} | } | ||||
public void WriteInt32Return(int value, int position) | |||||
public void WriteInt24Return(int value, int position) | |||||
{ | |||||
var span = writer.Written.Slice(position, 3); | |||||
span[0] = (byte)(value >> 16); | |||||
span[1] = (byte)(value >> 8); | |||||
span[2] = (byte)value; | |||||
} | |||||
public void WriteVideoTags(VideoTags videoTags) | |||||
{ | { | ||||
BinaryPrimitives.WriteInt32BigEndian(writer.Written.Slice(position, 3), value); | |||||
WriteByte((byte)((byte)videoTags.FrameType | (byte)videoTags.CodecId)); | |||||
#warning 只处理H.264媒体数据 | |||||
if (videoTags.CodecId== CodecId.AvcVideoPacke) | |||||
{ | |||||
WriteAvcVideoPacke(videoTags.VideoData); | |||||
} | |||||
} | } | ||||
public void WriteAvcVideoPacke(AvcVideoPacke videoPacke) | |||||
{ | |||||
WriteByte((byte)videoPacke.AvcPacketType); | |||||
if (videoPacke.AvcPacketType== AvcPacketType.SequenceHeader) | |||||
{ | |||||
videoPacke.CompositionTime = 0; | |||||
WriteUInt24(videoPacke.CompositionTime); | |||||
//AVCDecoderConfigurationRecord | |||||
#warning AVCDecoderConfigurationRecord | |||||
WriteArray(videoPacke.Data); | |||||
} | |||||
else if(videoPacke.AvcPacketType == AvcPacketType.Raw) | |||||
{ | |||||
WriteUInt24(videoPacke.CompositionTime); | |||||
#warning One or more NALUs | |||||
//One or more NALUs | |||||
WriteArray(videoPacke.Data); | |||||
} | |||||
else | |||||
{ | |||||
videoPacke.CompositionTime = 0; | |||||
WriteUInt24(videoPacke.CompositionTime); | |||||
//Empty | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -0,0 +1,19 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Flv.Metadata | |||||
{ | |||||
public class Amf3 | |||||
{ | |||||
/// <summary> | |||||
/// AMF3数据类型 | |||||
/// </summary> | |||||
public byte DataType { get; set; } = 0x08; | |||||
/// <summary> | |||||
/// 元素个数 | |||||
/// </summary> | |||||
public uint Count { get; set; } | |||||
public List<IAmf3Metadata> Amf3Metadatas { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,27 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Buffers.Binary; | |||||
using JT1078.Flv.Extensions; | |||||
namespace JT1078.Flv.Metadata | |||||
{ | |||||
public class Amf3Metadata_Duration : IAmf3Metadata | |||||
{ | |||||
public ushort FieldNameLength { get; set; } | |||||
public string FieldName { get; set; } = "duration"; | |||||
public byte DataType { get; set; } = 0x00; | |||||
public object Value { get; set; } | |||||
public ReadOnlySpan<byte> ToBuffer() | |||||
{ | |||||
Span<byte> tmp = new byte[2+8+1+8]; | |||||
var b1 = Encoding.ASCII.GetBytes(FieldName); | |||||
BinaryPrimitives.WriteUInt16BigEndian(tmp,(ushort)b1.Length); | |||||
b1.CopyTo(tmp.Slice(2)); | |||||
tmp[10] = DataType; | |||||
this.WriteDouble(tmp.Slice(11)); | |||||
return tmp; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,26 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Buffers.Binary; | |||||
namespace JT1078.Flv.Metadata | |||||
{ | |||||
public class Amf3Metadata_FileSize : IAmf3Metadata | |||||
{ | |||||
public ushort FieldNameLength { get; set; } | |||||
public string FieldName { get; set; } = "filesize"; | |||||
public byte DataType { get; set; } = 0x00; | |||||
public object Value { get; set; } | |||||
public ReadOnlySpan<byte> ToBuffer() | |||||
{ | |||||
Span<byte> tmp = new byte[2+8+1+8]; | |||||
var b1 = Encoding.ASCII.GetBytes(FieldName); | |||||
BinaryPrimitives.WriteUInt16BigEndian(tmp, (ushort)b1.Length); | |||||
b1.CopyTo(tmp.Slice(4)); | |||||
tmp[11] = DataType; | |||||
BinaryPrimitives.WriteInt64BigEndian(tmp.Slice(12), (long)Value); | |||||
return tmp; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,26 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Buffers.Binary; | |||||
namespace JT1078.Flv.Metadata | |||||
{ | |||||
public class Amf3Metadata_FrameRate : IAmf3Metadata | |||||
{ | |||||
public ushort FieldNameLength { get; set; } | |||||
public string FieldName { get; set; } = "framerate"; | |||||
public byte DataType { get; set; } = 0x00; | |||||
public object Value { get; set; } | |||||
public ReadOnlySpan<byte> ToBuffer() | |||||
{ | |||||
Span<byte> tmp = new byte[4+9+1+8]; | |||||
var b1 = Encoding.ASCII.GetBytes(FieldName); | |||||
BinaryPrimitives.WriteUInt16BigEndian(tmp, (ushort)b1.Length); | |||||
b1.CopyTo(tmp.Slice(4)); | |||||
tmp[11] = DataType; | |||||
BinaryPrimitives.WriteInt64BigEndian(tmp.Slice(12), (long)Value); | |||||
return tmp; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,26 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Buffers.Binary; | |||||
namespace JT1078.Flv.Metadata | |||||
{ | |||||
public class Amf3Metadata_Height : IAmf3Metadata | |||||
{ | |||||
public ushort FieldNameLength { get; set; } | |||||
public string FieldName { get; set; } = "height"; | |||||
public byte DataType { get; set; } = 0x00; | |||||
public object Value { get; set; } | |||||
public ReadOnlySpan<byte> ToBuffer() | |||||
{ | |||||
Span<byte> tmp = new byte[4+6+1+8]; | |||||
var b1 = Encoding.ASCII.GetBytes(FieldName); | |||||
BinaryPrimitives.WriteUInt16BigEndian(tmp, (ushort)b1.Length); | |||||
b1.CopyTo(tmp.Slice(3)); | |||||
tmp[11] = DataType; | |||||
BinaryPrimitives.WriteInt64BigEndian(tmp.Slice(12), (long)Value); | |||||
return tmp; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,26 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Buffers.Binary; | |||||
namespace JT1078.Flv.Metadata | |||||
{ | |||||
public class Amf3Metadata_VideoCodecId : IAmf3Metadata | |||||
{ | |||||
public ushort FieldNameLength { get; set; } | |||||
public string FieldName { get; set; } = "videocodecid"; | |||||
public byte DataType { get; set; } = 0x00; | |||||
public object Value { get; set; } | |||||
public ReadOnlySpan<byte> ToBuffer() | |||||
{ | |||||
Span<byte> tmp = new byte[4+12+1+8]; | |||||
var b1 = Encoding.ASCII.GetBytes(FieldName); | |||||
BinaryPrimitives.WriteUInt16BigEndian(tmp, (ushort)b1.Length); | |||||
b1.CopyTo(tmp.Slice(4)); | |||||
tmp[11] = DataType; | |||||
BinaryPrimitives.WriteInt64BigEndian(tmp.Slice(12), (long)Value); | |||||
return tmp; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,26 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Buffers.Binary; | |||||
namespace JT1078.Flv.Metadata | |||||
{ | |||||
public class Amf3Metadata_VideoDataRate : IAmf3Metadata | |||||
{ | |||||
public ushort FieldNameLength { get; set; } | |||||
public string FieldName { get; set; } = "videodatarate"; | |||||
public byte DataType { get; set; } = 0x00; | |||||
public object Value { get; set; } | |||||
public ReadOnlySpan<byte> ToBuffer() | |||||
{ | |||||
Span<byte> tmp = new byte[4+13+1+8]; | |||||
var b1 = Encoding.ASCII.GetBytes(FieldName); | |||||
BinaryPrimitives.WriteUInt16BigEndian(tmp, (ushort)b1.Length); | |||||
b1.CopyTo(tmp.Slice(4)); | |||||
tmp[11] = DataType; | |||||
BinaryPrimitives.WriteInt64BigEndian(tmp.Slice(12), (long)Value); | |||||
return tmp; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,26 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Buffers.Binary; | |||||
namespace JT1078.Flv.Metadata | |||||
{ | |||||
public class Amf3Metadata_Width : IAmf3Metadata | |||||
{ | |||||
public ushort FieldNameLength { get; set; } | |||||
public string FieldName { get; set; } = "width"; | |||||
public byte DataType { get; set; } = 0x00; | |||||
public object Value { get; set; } | |||||
public ReadOnlySpan<byte> ToBuffer() | |||||
{ | |||||
Span<byte> tmp = new byte[4+5+1+8]; | |||||
var b1 = Encoding.ASCII.GetBytes(FieldName); | |||||
BinaryPrimitives.WriteUInt16BigEndian(tmp, (ushort)b1.Length); | |||||
b1.CopyTo(tmp.Slice(3)); | |||||
tmp[11] = DataType; | |||||
BinaryPrimitives.WriteInt64BigEndian(tmp.Slice(12), (long)Value); | |||||
return tmp; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,15 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Flv.Metadata | |||||
{ | |||||
public interface IAmf3Metadata | |||||
{ | |||||
ushort FieldNameLength { get; set; } | |||||
string FieldName { get; set; } | |||||
byte DataType { get; set; } | |||||
object Value { get; set; } | |||||
ReadOnlySpan<byte> ToBuffer(); | |||||
} | |||||
} |
@@ -5,7 +5,7 @@ using System.Text; | |||||
namespace JT1078.Flv.Metadata | namespace JT1078.Flv.Metadata | ||||
{ | { | ||||
public class VideoTag | |||||
public class VideoTags | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// 1: keyframe(for AVC, a seekable frame) —— 即H.264的IDR帧; | /// 1: keyframe(for AVC, a seekable frame) —— 即H.264的IDR帧; | ||||
@@ -15,7 +15,7 @@ namespace JT1078.Flv.Metadata | |||||
/// <summary> | /// <summary> | ||||
/// 当 CodecID 为 7 时,VideoData 为 AVCVIDEOPACKE,也即 H.264媒体数据 | /// 当 CodecID 为 7 时,VideoData 为 AVCVIDEOPACKE,也即 H.264媒体数据 | ||||
/// </summary> | /// </summary> | ||||
public CodecId CodecId { get; set; } | |||||
public byte[] VideoData { get; set; } | |||||
public CodecId CodecId { get; set; } = CodecId.AvcVideoPacke; | |||||
public AvcVideoPacke VideoData { get; set; } | |||||
} | } | ||||
} | } |
@@ -1,8 +1,8 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <Project Sdk="Microsoft.NET.Sdk"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFramework>netstandard2.0</TargetFramework> | |||||
<LangVersion>7.3</LangVersion> | |||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks> | |||||
<LangVersion>8.0</LangVersion> | |||||
<Copyright>Copyright 2019.</Copyright> | <Copyright>Copyright 2019.</Copyright> | ||||
<Authors>SmallChi(Koike)</Authors> | <Authors>SmallChi(Koike)</Authors> | ||||
<PackageId>JT1078</PackageId> | <PackageId>JT1078</PackageId> | ||||
@@ -14,16 +14,17 @@ | |||||
<licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | <licenseUrl>https://github.com/SmallChi/JT1078/blob/master/LICENSE</licenseUrl> | ||||
<license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | <license>https://github.com/SmallChi/JT1078/blob/master/LICENSE</license> | ||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> | <GeneratePackageOnBuild>false</GeneratePackageOnBuild> | ||||
<Version>1.0.0</Version> | |||||
<Version>1.0.1</Version> | |||||
<SignAssembly>false</SignAssembly> | <SignAssembly>false</SignAssembly> | ||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | ||||
<PackageLicenseFile>LICENSE</PackageLicenseFile> | <PackageLicenseFile>LICENSE</PackageLicenseFile> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | |||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> | |||||
<PackageReference Include="System.Memory" Version="4.5.3" /> | <PackageReference Include="System.Memory" Version="4.5.3" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<None Include="..\..\LICENSE"> | <None Include="..\..\LICENSE"> | ||||
<Pack>True</Pack> | <Pack>True</Pack> | ||||
@@ -27,7 +27,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution | |||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.Protocol.Extensions.WebApiTest", "JT808.Protocol.Extensions.WebApiTest\JT808.Protocol.Extensions.WebApiTest.csproj", "{9DB37370-AC73-434B-9CE2-6659321858C8}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT808.Protocol.Extensions.WebApiTest", "JT808.Protocol.Extensions.WebApiTest\JT808.Protocol.Extensions.WebApiTest.csproj", "{9DB37370-AC73-434B-9CE2-6659321858C8}" | ||||
EndProject | EndProject | ||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT1078.Flv", "JT1078.Flv\JT1078.Flv.csproj", "{33E54FFC-7D91-42E5-9DC1-853738AB8980}" | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JT1078.Flv", "JT1078.Flv\JT1078.Flv.csproj", "{33E54FFC-7D91-42E5-9DC1-853738AB8980}" | |||||
EndProject | |||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JT1078.Flv.Test", "JT1078.Flv.Test\JT1078.Flv.Test.csproj", "{D13FE092-1D11-4545-A322-9F06BCDAC0FD}" | |||||
EndProject | EndProject | ||||
Global | Global | ||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
@@ -71,6 +73,10 @@ Global | |||||
{33E54FFC-7D91-42E5-9DC1-853738AB8980}.Debug|Any CPU.Build.0 = Debug|Any CPU | {33E54FFC-7D91-42E5-9DC1-853738AB8980}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{33E54FFC-7D91-42E5-9DC1-853738AB8980}.Release|Any CPU.ActiveCfg = Release|Any CPU | {33E54FFC-7D91-42E5-9DC1-853738AB8980}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
{33E54FFC-7D91-42E5-9DC1-853738AB8980}.Release|Any CPU.Build.0 = Release|Any CPU | {33E54FFC-7D91-42E5-9DC1-853738AB8980}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
{D13FE092-1D11-4545-A322-9F06BCDAC0FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{D13FE092-1D11-4545-A322-9F06BCDAC0FD}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{D13FE092-1D11-4545-A322-9F06BCDAC0FD}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{D13FE092-1D11-4545-A322-9F06BCDAC0FD}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||
@@ -80,6 +86,7 @@ Global | |||||
{E9FF2716-EF30-4180-879B-E8AB979ACFF3} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | {E9FF2716-EF30-4180-879B-E8AB979ACFF3} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | ||||
{9ADD82F9-E0B2-4263-8573-151F673BB33F} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | {9ADD82F9-E0B2-4263-8573-151F673BB33F} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | ||||
{9DB37370-AC73-434B-9CE2-6659321858C8} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | {9DB37370-AC73-434B-9CE2-6659321858C8} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | ||||
{D13FE092-1D11-4545-A322-9F06BCDAC0FD} = {0655AF84-E578-409F-AB0E-B47E0D2F6814} | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(ExtensibilityGlobals) = postSolution | GlobalSection(ExtensibilityGlobals) = postSolution | ||||
SolutionGuid = {FAE1656D-226F-4B4B-8C33-615D7E632B26} | SolutionGuid = {FAE1656D-226F-4B4B-8C33-615D7E632B26} | ||||