@@ -1,21 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Flv | |||||
{ | |||||
public static class Amf0 | |||||
{ | |||||
public readonly static byte[] Buffer; | |||||
public readonly static byte[] FixedData = new byte[] { 0x6F, 0x6E, 0x4D, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61 }; | |||||
static Amf0() | |||||
{ | |||||
Buffer = new byte[13]; | |||||
Buffer[0] = 0x02; | |||||
Buffer[1] = 0; | |||||
Buffer[2] = 10; | |||||
Array.Copy(FixedData, 0, Buffer, 3, 10); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,24 @@ | |||||
using System; | |||||
namespace JT1078.Flv | |||||
{ | |||||
/// <summary> | |||||
/// <see cref="System.Buffers.Writer"/> | |||||
/// </summary> | |||||
ref partial struct FlvBufferWriter | |||||
{ | |||||
private Span<byte> _buffer; | |||||
public FlvBufferWriter(Span<byte> buffer) | |||||
{ | |||||
_buffer = buffer; | |||||
WrittenCount = 0; | |||||
} | |||||
public Span<byte> Free => _buffer.Slice(WrittenCount); | |||||
public Span<byte> Written => _buffer.Slice(0, WrittenCount); | |||||
public int WrittenCount { get; private set; } | |||||
public void Advance(int count) | |||||
{ | |||||
WrittenCount += count; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,13 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Flv.Enums | |||||
{ | |||||
public enum AvcPacketType | |||||
{ | |||||
SequenceHeader=1, | |||||
Raw, | |||||
AVCEndSequence | |||||
} | |||||
} |
@@ -1,12 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Flv | |||||
{ | |||||
public enum AacPacketType | |||||
{ | |||||
SequenceHeader, | |||||
Raw | |||||
} | |||||
} |
@@ -2,7 +2,7 @@ | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | using System.Text; | ||||
namespace JT1078.Flv | namespace JT1078.Flv.Enums | ||||
{ | { | ||||
public enum CodecId | public enum CodecId | ||||
{ | { | ||||
@@ -2,7 +2,7 @@ | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | using System.Text; | ||||
namespace JT1078.Flv | namespace JT1078.Flv.Enums | ||||
{ | { | ||||
public enum FrameType | public enum FrameType | ||||
{ | { | ||||
@@ -0,0 +1,13 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Flv.Enums | |||||
{ | |||||
public enum TagType:byte | |||||
{ | |||||
Audio= 0x08, | |||||
Video= 0x09, | |||||
ScriptData= 0x12, | |||||
} | |||||
} |
@@ -1,4 +1,5 @@ | |||||
using System; | using JT1078.Flv.MessagePack; | ||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | using System.Text; | ||||
@@ -7,9 +8,29 @@ namespace JT1078.Flv | |||||
public class FlvMuxer | public class FlvMuxer | ||||
{ | { | ||||
private readonly FlvHeader VideoFlvHeader = new FlvHeader(true, false); | private readonly FlvHeader VideoFlvHeader = new FlvHeader(true, false); | ||||
public void FlvVideoMuxer() | public byte[] FlvFirstFrame() | ||||
{ | { | ||||
byte[] buffer = FlvArrayPool.Rent(10240); | |||||
try | |||||
{ | |||||
FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer); | |||||
//flv header | |||||
flvMessagePackWriter.WriteArray(VideoFlvHeader.ToArray()); | |||||
//flv body | |||||
//flv body PreviousTagSize | |||||
flvMessagePackWriter.WriteUInt32(0); | |||||
//flv body tag | |||||
//flv body tag header | |||||
//flv body tag body | |||||
return flvMessagePackWriter.FlushAndGetArray(); | |||||
} | |||||
finally | |||||
{ | |||||
FlvArrayPool.Return(buffer); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -2,7 +2,23 @@ | |||||
{ | { | ||||
public class FlvTag | public class FlvTag | ||||
{ | { | ||||
public FlvTagHeader TagHeader { get; set; } | public byte Type { get; set; } | ||||
/// <summary> | |||||
/// Tag Data部分大小 | |||||
/// 3个字节 | |||||
/// </summary> | |||||
public uint DataSize { get; set; } | |||||
/// <summary> | |||||
/// Tag时间戳 | |||||
/// 3个字节 | |||||
/// </summary> | |||||
public uint Timestamp { get; set; } = 0; | |||||
public byte TimestampExt { get; set; } = 0; | |||||
/// <summary> | |||||
/// stream id 总是0 | |||||
/// 3个字节 | |||||
/// </summary> | |||||
public uint StreamId { get; set; } = 0; | |||||
/// <summary> | /// <summary> | ||||
/// 根据tag类型 | /// 根据tag类型 | ||||
/// </summary> | /// </summary> | ||||
@@ -1,27 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Flv | |||||
{ | |||||
public class FlvTagHeader | |||||
{ | |||||
public byte Type { get; set; } | |||||
/// <summary> | |||||
/// Tag Data部分大小 | |||||
/// 3个字节 | |||||
/// </summary> | |||||
public uint DataSize { get; set; } | |||||
/// <summary> | |||||
/// Tag时间戳 | |||||
/// 3个字节 | |||||
/// </summary> | |||||
public uint Timestamp { get; set; } | |||||
public byte TimestampExt { get; set; } = 0; | |||||
/// <summary> | |||||
/// stream id 总是0 | |||||
/// 3个字节 | |||||
/// </summary> | |||||
public uint StreamId { get; set; } = 0; | |||||
} | |||||
} |
@@ -0,0 +1,60 @@ | |||||
using System; | |||||
using System.Buffers.Binary; | |||||
namespace JT1078.Flv.MessagePack | |||||
{ | |||||
ref partial struct FlvMessagePackWriter | |||||
{ | |||||
private FlvBufferWriter writer; | |||||
public FlvMessagePackWriter(Span<byte> buffer) | |||||
{ | |||||
this.writer = new FlvBufferWriter(buffer); | |||||
} | |||||
public byte[] FlushAndGetArray() | |||||
{ | |||||
return writer.Written.ToArray(); | |||||
} | |||||
public void WriteByte(byte value) | |||||
{ | |||||
var span = writer.Free; | |||||
span[0] = value; | |||||
writer.Advance(1); | |||||
} | |||||
public void WriteUInt16(ushort value) | |||||
{ | |||||
BinaryPrimitives.WriteUInt16BigEndian(writer.Free, value); | |||||
writer.Advance(2); | |||||
} | |||||
public void WriteInt32(int value) | |||||
{ | |||||
BinaryPrimitives.WriteInt32BigEndian(writer.Free, value); | |||||
writer.Advance(4); | |||||
} | |||||
public void WriteUInt64(ulong value) | |||||
{ | |||||
BinaryPrimitives.WriteUInt64BigEndian(writer.Free, value); | |||||
writer.Advance(8); | |||||
} | |||||
public void WriteUInt32(uint value) | |||||
{ | |||||
BinaryPrimitives.WriteUInt32BigEndian(writer.Free, value); | |||||
writer.Advance(4); | |||||
} | |||||
public void WriteArray(ReadOnlySpan<byte> src) | |||||
{ | |||||
src.CopyTo(writer.Free); | |||||
writer.Advance(src.Length); | |||||
} | |||||
public void Skip(int count, out int position) | |||||
{ | |||||
position = writer.WrittenCount; | |||||
byte[] tmp = new byte[count]; | |||||
tmp.CopyTo(writer.Free); | |||||
writer.Advance(count); | |||||
} | |||||
public int GetCurrentPosition() | |||||
{ | |||||
return writer.WrittenCount; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,19 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Flv.MessagePack | |||||
{ | |||||
ref partial struct FlvMessagePackWriter | |||||
{ | |||||
private readonly static byte[] FixedAmf0Data = new byte[] { 0x6F, 0x6E, 0x4D, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61 }; | |||||
public void WriteAmf0() | |||||
{ | |||||
var span = writer.Free; | |||||
span[0] = 0x02; | |||||
span[1] = 0; | |||||
span[2] = 10; | |||||
FixedAmf0Data.CopyTo(span.Slice(3)); | |||||
} | |||||
} | |||||
} |
@@ -2,10 +2,11 @@ | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | using System.Text; | ||||
namespace JT1078.Flv | namespace JT1078.Flv.MessagePack | ||||
{ | { | ||||
public static class Amf3 | ref partial struct FlvMessagePackWriter | ||||
{ | { | ||||
} | } | ||||
} | } |
@@ -0,0 +1,56 @@ | |||||
using JT1078.Flv.Enums; | |||||
using System; | |||||
using System.Buffers.Binary; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Flv.MessagePack | |||||
{ | |||||
ref partial struct FlvMessagePackWriter | |||||
{ | |||||
public void WriteFlvBody(FlvBody body) | |||||
{ | |||||
WriteUInt32(body.PreviousTagSize); | |||||
if (body.Tag != null) | |||||
{ | |||||
WriteFlvTag(body.Tag); | |||||
} | |||||
} | |||||
public void WriteFlvTag(FlvTag tag) | |||||
{ | |||||
WriteByte(tag.Type); | |||||
Skip(3, out int DataSizePosition); | |||||
WriteUInt24(tag.Timestamp); | |||||
WriteByte(tag.TimestampExt); | |||||
WriteUInt24(tag.StreamId); | |||||
switch ((TagType)tag.Type) | |||||
{ | |||||
case TagType.Video: | |||||
break; | |||||
case TagType.ScriptData: | |||||
//flv Amf0 | |||||
WriteAmf0(); | |||||
//flv Amf3 | |||||
break; | |||||
case TagType.Audio: | |||||
break; | |||||
} | |||||
WriteInt32Return(GetCurrentPosition() - DataSizePosition - 3, DataSizePosition); | |||||
} | |||||
public void WriteUInt24(uint value) | |||||
{ | |||||
BinaryPrimitives.WriteUInt32BigEndian(writer.Free, value); | |||||
writer.Advance(3); | |||||
} | |||||
public void WriteInt32Return(int value, int position) | |||||
{ | |||||
BinaryPrimitives.WriteInt32BigEndian(writer.Written.Slice(position, 3), value); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,16 @@ | |||||
using JT1078.Flv.Enums; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Flv.Metadata | |||||
{ | |||||
public class AvcVideoPacke | |||||
{ | |||||
public AvcPacketType AvcPacketType { get; set; } | |||||
public uint CompositionTime { get; set; } | |||||
public byte[] Data { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,21 @@ | |||||
using JT1078.Flv.Enums; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace JT1078.Flv.Metadata | |||||
{ | |||||
public class VideoTag | |||||
{ | |||||
/// <summary> | |||||
/// 1: keyframe(for AVC, a seekable frame) —— 即H.264的IDR帧; | |||||
/// 2: inter frame(for AVC, a non- seekable frame) —— H.264的普通I帧; | |||||
/// </summary> | |||||
public FrameType FrameType { get; set; } | |||||
/// <summary> | |||||
/// 当 CodecID 为 7 时,VideoData 为 AVCVIDEOPACKE,也即 H.264媒体数据 | |||||
/// </summary> | |||||
public CodecId CodecId { get; set; } | |||||
public byte[] VideoData { get; set; } | |||||
} | |||||
} |