From 12456e8e5390f5d83e3d370b69de650746110e42 Mon Sep 17 00:00:00 2001
From: waterliu99 <qqcc2012game@163.com>
Date: Wed, 15 Jan 2020 10:25:02 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96flv=20=E7=BC=96=E7=A0=81?=
 =?UTF-8?q?=EF=BC=8C=E5=AE=8C=E5=96=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/JT1078.Flv.Test/FlvEncoderTest.cs         | 407 +++++-------------
 src/JT1078.Flv.Test/H264ToFlvTest.cs          |   3 +
 src/JT1078.Flv/FlvEncoder.cs                  | 114 +++--
 src/JT1078.Flv/FlvTags.cs                     |   2 +-
 src/JT1078.Flv/JT1078.Flv.xml                 |  51 ++-
 .../MessagePack/FlvMessagePackWriter_Flv.cs   |   3 +-
 6 files changed, 201 insertions(+), 379 deletions(-)

diff --git a/src/JT1078.Flv.Test/FlvEncoderTest.cs b/src/JT1078.Flv.Test/FlvEncoderTest.cs
index ea95ae0..dc0547f 100644
--- a/src/JT1078.Flv.Test/FlvEncoderTest.cs
+++ b/src/JT1078.Flv.Test/FlvEncoderTest.cs
@@ -20,306 +20,133 @@ namespace JT1078.Flv.Test
         [Fact]
         public void 测试第一帧的数据()
         {
-            JT1078Package Package = null;
-            var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.txt"));
-            int mergeBodyLength = 0;
-            foreach (var line in lines)
+            FileStream fileStream = null;
+            try
             {
-                var data = line.Split(',');
-                var bytes = data[6].ToHexBytes();
-                JT1078Package package = JT1078Serializer.Deserialize(bytes);
-                mergeBodyLength += package.DataBodyLength;
-                Package = JT1078Serializer.Merge(package);
+                var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.txt"));
+                var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.flv");
+                fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
+
+                bool isNeedFirstHeadler = true;
+                FlvEncoder encoder = new FlvEncoder();
+                foreach (var line in lines)
+                {
+                    var data = line.Split(',');
+                    var bytes = data[6].ToHexBytes();
+                    JT1078Package package = JT1078Serializer.Deserialize(bytes);
+                    JT1078Package fullpackage = JT1078Serializer.Merge(package);
+                    if (fullpackage != null)
+                    {
+                        var videoTag = encoder.EncoderVideoTag(fullpackage, isNeedFirstHeadler);
+                        fileStream.Write(videoTag);
+                        isNeedFirstHeadler = false;
+                    }
+                }
             }
-            H264Decoder decoder = new H264Decoder();
-            //7 8 6 5 1 1 1 1 1 7 8 6 5 1 1 1 1
-            var nalus = decoder.ParseNALU(Package);
-            Assert.Equal(4, nalus.Count);
+            catch (Exception ex)
+            {
 
-            FlvEncoder encoder = new FlvEncoder();
-            var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_1.flv");
-            if (File.Exists(filepath))
+            }
+            finally
             {
-                File.Delete(filepath);
+                fileStream?.Close();
+                fileStream?.Dispose();
             }
-            FileStream fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
-            fileStream.Write(encoder.EncoderFlvHeader());
-            fileStream.Write(encoder.EncoderScriptTag());
-            fileStream.Write(encoder.EncoderVideoTag(Package, true));
-            fileStream.Close();
         }
 
-        //[Fact]
-        //public void 测试前几帧的数据()
-        //{
-        //    FileStream fileStream = null;
-        //    try
-        //    {
-        //        JT1078Package Package = null;
-        //        List<H264NALU> h264NALULs = new List<H264NALU>();
-        //        H264Decoder decoder = new H264Decoder();
-        //        FlvEncoder encoder = new FlvEncoder();
-        //        var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_2.txt"));
-        //        foreach (var line in lines)
-        //        {
-        //            var data = line.Split(',');
-        //            var bytes = data[6].ToHexBytes();
-        //            JT1078Package package = JT1078Serializer.Deserialize(bytes);
-        //            Package = JT1078Serializer.Merge(package);
-        //            if (Package != null)
-        //            {
-        //                var tmp = decoder.ParseNALU(Package);
-        //                if (tmp != null && tmp.Count > 0)
-        //                {
-        //                    h264NALULs = h264NALULs.Concat(tmp).ToList();
-        //                }
-        //            }
-        //        }
-
-        //        var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_2.flv");
-        //        if (File.Exists(filepath))
-        //        {
-        //            File.Delete(filepath);
-        //        }
-        //        fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
-        //        fileStream.Write(FlvEncoder.VideoFlvHeaderBuffer);
-        //        var totalPage = (h264NALULs.Count + 10 - 1) / 10;
-        //        for (var i = 0; i < totalPage; i++)
-        //        {
-        //            var flv2 = encoder.CreateFlvFrame(h264NALULs.Skip(i * 10).Take(10).ToList());
-        //            if (flv2.Length != 0)
-        //            {
-        //                fileStream.Write(flv2);
-        //            }
-        //        }
-        //    }
-        //    catch (Exception ex)
-        //    {
-
-        //    }
-        //    finally
-        //    {
-        //        fileStream?.Close();
-        //        fileStream?.Dispose();
-        //    }
-        //}
-
-        //[Fact]
-        //public void 测试可以播放的Flv1()
-        //{
-        //    FileStream fileStream = null;
-        //    H264Decoder decoder = new H264Decoder();
-        //    List<H264NALU> h264NALULs = new List<H264NALU>();
-        //    FlvEncoder encoder = new FlvEncoder();
-        //    try
-        //    {
-        //        var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_3.flv");
-        //        var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_3.txt"));
-        //        if (File.Exists(filepath))
-        //        {
-        //            File.Delete(filepath);
-        //        }
-
-        //        JT1078Package Package = null;
-
-        //        foreach (var line in lines)
-        //        {
-        //            var data = line.Split(',');
-        //            var bytes = data[6].ToHexBytes();
-        //            JT1078Package package = JT1078Serializer.Deserialize(bytes);
-        //            Package = JT1078Serializer.Merge(package);
-        //            if (Package != null)
-        //            {
-        //                var tmp = decoder.ParseNALU(Package);
-        //                if (tmp != null && tmp.Count > 0)
-        //                {
-        //                    h264NALULs = h264NALULs.Concat(tmp).ToList();
-        //                }
-        //            }
-        //        }
-
-        //        fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
-        //        fileStream.Write(FlvEncoder.VideoFlvHeaderBuffer);
-        //        var totalPage = (h264NALULs.Count + 10 - 1) / 10;
-        //        for (var i = 0; i < totalPage; i++)
-        //        {
-        //            var flv2 = encoder.CreateFlvFrame(h264NALULs.Skip(i * 10).Take(10).ToList());
-        //            if (flv2.Length != 0)
-        //            {
-        //                fileStream.Write(flv2);
-        //            }
-        //        }
-        //    }
-        //    catch (Exception ex)
-        //    {
-        //        Assert.Throws<Exception>(() => { });
-        //    }
-        //    finally
-        //    {
-        //        fileStream?.Close();
-        //        fileStream?.Dispose();
-        //    }
-        //}
-
-        //[Fact]
-        //public void 测试可以播放的Flv2()
-        //{
-        //    FileStream fileStream = null;
-        //    H264Decoder decoder = new H264Decoder();
-        //    List<H264NALU> h264NALULs = new List<H264NALU>();
-        //    FlvEncoder encoder = new FlvEncoder();
-        //    try
-        //    {
-        //        var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_4.flv");
-        //        var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_4.txt"));
-        //        if (File.Exists(filepath))
-        //        {
-        //            File.Delete(filepath);
-        //        }
-
-        //        JT1078Package Package = null;
-
-        //        foreach (var line in lines)
-        //        {
-        //            var data = line.Split(',');
-        //            var bytes = data[6].ToHexBytes();
-        //            JT1078Package package = JT1078Serializer.Deserialize(bytes);
-        //            Package = JT1078Serializer.Merge(package);
-        //            if (Package != null)
-        //            {
-        //                var tmp = decoder.ParseNALU(Package);
-        //                if (tmp != null && tmp.Count > 0)
-        //                {
-        //                    h264NALULs = h264NALULs.Concat(tmp).ToList();
-        //                }
-        //            }
-        //        }
-
-        //        fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
-        //        fileStream.Write(FlvEncoder.VideoFlvHeaderBuffer);
-        //        var totalPage = (h264NALULs.Count + 10 - 1) / 10;
-        //        for (var i = 0; i < totalPage; i++)
-        //        {
-        //            var flv2 = encoder.CreateFlvFrame(h264NALULs.Skip(i * 10).Take(10).ToList());
-        //            if (flv2.Length != 0)
-        //            {
-        //                fileStream.Write(flv2);
-        //            }
-        //        }
-        //    }
-        //    catch (Exception ex)
-        //    {
-        //        Assert.Throws<Exception>(() => { });
-        //    }
-        //    finally
-        //    {
-        //        fileStream?.Close();
-        //        fileStream?.Dispose();
-        //    }
-        //}
-
-        //[Fact]
-        //public void 测试主次码流切换()
-        //{
-        //    FileStream fileStream = null;
-        //    H264Decoder decoder = new H264Decoder();
-        //    List<H264NALU> h264NALULs = new List<H264NALU>();
-        //    FlvEncoder encoder = new FlvEncoder();
-        //    try
-        //    {
-        //        var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_5.flv");
-        //        var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_5.txt"));
-        //        if (File.Exists(filepath))
-        //        {
-        //            File.Delete(filepath);
-        //        }
-
-        //        JT1078Package Package = null;
+        [Fact]
+        public void 测试前几帧的数据()
+        {
+            FileStream fileStream = null;
+            try
+            {
+                var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_2.txt"));
+                var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_2.flv");
+                fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
+
+                bool isNeedFirstHeadler = true;
+                FlvEncoder encoder = new FlvEncoder();
+                foreach (var line in lines)
+                {
+                    var data = line.Split(',');
+                    var bytes = data[6].ToHexBytes();
+                    JT1078Package package = JT1078Serializer.Deserialize(bytes);
+                    JT1078Package fullpackage = JT1078Serializer.Merge(package);
+                    if (fullpackage != null)
+                    {
+                        var videoTag = encoder.EncoderVideoTag(fullpackage, isNeedFirstHeadler);
+                        fileStream.Write(videoTag);
+                        isNeedFirstHeadler = false;
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
 
-        //        foreach (var line in lines)
-        //        {
-        //            var data = line.Split(',');
-        //            var bytes = data[6].ToHexBytes();
-        //            JT1078Package package = JT1078Serializer.Deserialize(bytes);
-        //            Package = JT1078Serializer.Merge(package);
-        //            if (Package != null)
-        //            {
-        //                var tmp = decoder.ParseNALU(Package);
-        //                if (tmp != null && tmp.Count > 0)
-        //                {
-        //                    h264NALULs = h264NALULs.Concat(tmp).ToList();
-        //                }
-        //            }
-        //        }
-        //        var tmp1 = h264NALULs.Where(w => w.NALUHeader.NalUnitType == 7).ToList();
-        //        List<SPSInfo> tmpSpss = new List<SPSInfo>();
-        //        List<ulong> times = new List<ulong>();
-        //        List<ushort> lastIFrameIntervals = new List<ushort>();
-        //        List<ushort> lastFrameIntervals = new List<ushort>();
-        //        List<int> type = new List<int>();
-        //        foreach (var item in h264NALULs)
-        //        {
-        //            //type.Add(item.NALUHeader.NalUnitType);
-        //            times.Add(item.Timestamp);
-        //            lastFrameIntervals.Add(item.LastFrameInterval);
-        //            lastIFrameIntervals.Add(item.LastIFrameInterval);
-        //            if (item.NALUHeader.NalUnitType == 7)
-        //            {
-        //                ExpGolombReader expGolombReader = new ExpGolombReader(item.RawData);
-        //                tmpSpss.Add(expGolombReader.ReadSPS());
-        //            }
-        //        }
-        //        fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
-        //        var totalPage = (h264NALULs.Count + 10 - 1) / 10;
-        //        for (var i = 0; i < totalPage; i++)
-        //        {
-        //            var flv2 = encoder.CreateFlvFrame(h264NALULs.Skip(i * 10).Take(10).ToList());
-        //            if (flv2.Length != 0)
-        //            {
-        //                //fileStream.Write(flv2);
-        //            }
-        //        }
-        //    }
-        //    catch (Exception ex)
-        //    {
-        //        Assert.Throws<Exception>(() => { });
-        //    }
-        //    finally
-        //    {
-        //        fileStream?.Close();
-        //        fileStream?.Dispose();
-        //    }
-        //}
+            }
+            finally
+            {
+                fileStream?.Close();
+                fileStream?.Dispose();
+            }
+        }
 
-        //[Fact]
-        //public void CreateScriptTagFrameTest()
-        //{
-        //    FlvEncoder flvEncoder = new FlvEncoder();
-        //    var hexData = flvEncoder.CreateScriptTagFrame(288, 352);
-        //    Assert.Equal(151, hexData.Length);
-        //}
+        [Fact]
+        public void 测试可以播放的Flv3()
+        {
+            FileStream fileStream = null;
+            try
+            {
+                var filepath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_3.flv");
+                var lines = File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "H264", "JT1078_3.txt"));
+                fileStream = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write);
+
+                bool isNeedFirstHeadler = true;
+                FlvEncoder encoder = new FlvEncoder();
+                foreach (var line in lines)
+                {
+                    var data = line.Split(',');
+                    var bytes = data[6].ToHexBytes();
+                    JT1078Package package = JT1078Serializer.Deserialize(bytes);
+                    JT1078Package fullpackage = JT1078Serializer.Merge(package);
+                    if (fullpackage != null)
+                    {
+                        var videoTag = encoder.EncoderVideoTag(fullpackage, isNeedFirstHeadler);
+                        fileStream.Write(videoTag);
+                        isNeedFirstHeadler = false;
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                Assert.Throws<Exception>(() => { });
+            }
+            finally
+            {
+                fileStream?.Close();
+                fileStream?.Dispose();
+            }
+        }
 
-        //[Fact]
-        //public void CreateVideoTag0FrameTest()
-        //{
-        //    FlvEncoder flvEncoder = new FlvEncoder();
-        //    var hexData = flvEncoder.CreateVideoTag0Frame(
-        //        new byte[] { 0x67, 0x4D, 0, 0x14, 0x95, 0xA8, 0x58, 0x25, 0x90 },
-        //        new byte[] { 0x68, 0xEE, 0x3C, 0x80 },
-        //        new SPSInfo { levelIdc = 0x14, profileIdc = 0x4d, profileCompat = 0 });
-        //    Assert.Equal(40, hexData.Length);
-        //}
+        [Fact]
+        public void EncoderScriptTag()
+        {
+            FlvEncoder flvEncoder = new FlvEncoder();
+            var hexData = flvEncoder.EncoderScriptTag(new SPSInfo { width = 288, height = 352 });
+            Assert.Equal(155, hexData.Length);
+        }
 
-        //[Fact]
-        //public void GetFirstFlvFrameTest()
-        //{
-        //    FlvEncoder flvEncoder = new FlvEncoder();
-        //    string key = "test";
-        //    var bufferFlvFrame = new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };
-        //    FlvEncoder.FirstFlvFrameCache.TryAdd(key, (2, new byte[] { 1, 2, 3, 4, 5, 6 }, true));
-        //    var buffer = flvEncoder.GetFirstFlvFrame(key, bufferFlvFrame);
-        //    //替换PreviousTagSize 4位的长度为首帧的 PreviousTagSize
-        //    Assert.Equal(new byte[] { 1, 2, 3, 4, 5, 6, 0, 0, 0, 2, 0xE, 0xF }, buffer);
-        //}
+        [Fact]
+        public void EncoderFirstVideoTag()
+        {
+            FlvEncoder flvEncoder = new FlvEncoder();
+            var hexData = flvEncoder.EncoderFirstVideoTag(
+                new SPSInfo { levelIdc = 0x14, profileIdc = 0x4d, profileCompat = 0 },
+                new H264NALU { RawData = new byte[] { 0x67, 0x4D, 0, 0x14, 0x95, 0xA8, 0x58, 0x25, 0x90 } },
+                new H264NALU { RawData = new byte[] { 0x68, 0xEE, 0x3C, 0x80 } },
+                new H264NALU()
+           );
+            Assert.Equal(44, hexData.Length);
+        }
     }
 }
diff --git a/src/JT1078.Flv.Test/H264ToFlvTest.cs b/src/JT1078.Flv.Test/H264ToFlvTest.cs
index 7cb0605..94e3c46 100644
--- a/src/JT1078.Flv.Test/H264ToFlvTest.cs
+++ b/src/JT1078.Flv.Test/H264ToFlvTest.cs
@@ -12,6 +12,9 @@ namespace JT1078.Flv.Test
 {
     public class H264ToFlvTest
     {
+        /// <summary>
+        /// jt1078 转成 H264数据包
+        /// </summary>
         [Fact]
         public void Test_1()
         {
diff --git a/src/JT1078.Flv/FlvEncoder.cs b/src/JT1078.Flv/FlvEncoder.cs
index 20c9a37..a2f56f9 100644
--- a/src/JT1078.Flv/FlvEncoder.cs
+++ b/src/JT1078.Flv/FlvEncoder.cs
@@ -22,7 +22,8 @@ namespace JT1078.Flv
     /// </para>
     /// 
     /// 手动编码
-    /// 1、<see cref="EncoderFlvHeader"/>
+    /// 0、<see cref="EncoderFlvHeader"/>
+    /// 1、插入  PriviousTagSize =0  always equal 0
     /// 2、<see cref="EncoderScriptTag"/>
     /// 3、<see cref="EncoderFirstVideoTag"/>
     /// 4、<see cref="EncoderFirstAudioTag"/>
@@ -36,11 +37,8 @@ namespace JT1078.Flv
     /// </summary>
     public class FlvEncoder : IDisposable
     {
-        uint previousTagSize;
-        FlvHeader flvHeader = new FlvHeader(true, true);
         readonly FaacEncoder faacEncoder;
         readonly H264Decoder h264Decoder = new H264Decoder();
-
         public FlvEncoder(int sampleRate = 8000, int channels = 1, int sampleBit = 16, bool adts = false)
         {
             faacEncoder = new FaacEncoder(sampleRate, channels, sampleBit, adts);
@@ -48,32 +46,24 @@ namespace JT1078.Flv
 
         /// <summary>
         /// 编码flv头
-        /// <para>
-        /// 注意:本方法已写入<see cref="previousTagSize"/>
-        /// </para>
         /// </summary>
-        /// <param name="hasVideo"></param>
-        /// <param name="hasAudio"></param>
+        /// <param name="hasVideo">是否有视频</param>
+        /// <param name="hasAudio">是否有音频</param>
         /// <returns></returns>
         public byte[] EncoderFlvHeader(bool hasVideo = true, bool hasAudio = false)
         {
-            previousTagSize = 0;
-            flvHeader = new FlvHeader(hasVideo, hasAudio);
+            var flvHeader = new FlvHeader(hasVideo, hasAudio);
             return flvHeader.ToArray().ToArray();
         }
 
         /// <summary>
         /// 编码脚本Tag
-        /// <para>
-        /// 注意:本方法已写入<see cref="previousTagSize"/>
-        /// </para>
         /// </summary>
-        /// <param name="width">视频宽度</param>
-        /// <param name="height">视频高度</param>
-        /// <param name="hasAudio">是否含有音频,如果有,则写入音频配置,后来发现即便是有音频,这里给<c>false</c>也没关系</param>
-        /// <param name="frameRate">帧率</param>
+        /// <param name="spsInfo">解析后的sps信息</param>
+        /// <param name="hasAudio">是否有音频</param>
+        /// <param name="frameRate">帧率 默认25d 即每秒25帧</param>
         /// <returns></returns>
-        public byte[] EncoderScriptTag(bool hasAudio = false, double frameRate = 25d)
+        public byte[] EncoderScriptTag(SPSInfo spsInfo, bool hasAudio = false, double frameRate = 25d)
         {
             byte[] buffer = FlvArrayPool.Rent(1024);
             try
@@ -93,8 +83,12 @@ namespace JT1078.Flv
                             new Amf3Metadata_VideoDataRate{Value = 0d},
                             new Amf3Metadata_VideoCodecId{Value = 7d},
                             new Amf3Metadata_FrameRate{Value = frameRate},
-                            new Amf3Metadata_Width(),
-                            new Amf3Metadata_Height(),
+                            new Amf3Metadata_Width(){
+                             Value=spsInfo.width
+                            },
+                            new Amf3Metadata_Height(){
+                             Value=spsInfo.height
+                            },
                         }
                     }
                 };
@@ -105,11 +99,9 @@ namespace JT1078.Flv
                     flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioSampleSize());
                     flvTags.DataTagsData.Amf3Metadatas.Add(new Amf3Metadata_AudioStereo());
                 }
-                flvMessagePackWriter.WriteUInt32(previousTagSize);
                 flvMessagePackWriter.WriteFlvTag(flvTags);
-                var data = flvMessagePackWriter.FlushAndGetArray();
-                previousTagSize = (uint)(flvTags.DataSize + 11);
-                return data;
+                flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
+                return flvMessagePackWriter.FlushAndGetArray();
             }
             finally
             {
@@ -118,24 +110,19 @@ namespace JT1078.Flv
         }
 
         /// <summary>
-        /// 编码首帧视频,即videoTag[0]
-        /// <para>
-        /// 注意:本方法已写入<see cref="previousTagSize"/>
-        /// </para>
+        ///  编码首帧视频,即videoTag[0]
         /// </summary>
+        /// <param name="spsInfo">sps 解析后的数据</param>
         /// <param name="sps"></param>
         /// <param name="pps"></param>
         /// <param name="sei"></param>
         /// <returns></returns>
-        public byte[] EncoderFirstVideoTag(H264NALU sps, H264NALU pps, H264NALU sei)
+        public byte[] EncoderFirstVideoTag(SPSInfo spsInfo, H264NALU sps, H264NALU pps, H264NALU sei)
         {
             byte[] buffer = FlvArrayPool.Rent(2048);
             try
             {
                 FlvMessagePackWriter flvMessagePackWriter = new FlvMessagePackWriter(buffer);
-                var rawData = h264Decoder.DiscardEmulationPreventionBytes(sps.RawData);
-                ExpGolombReader h264GolombReader = new ExpGolombReader(rawData);
-                SPSInfo spsInfo = h264GolombReader.ReadSPS();
                 //flv body video tag
                 //flv body tag header
                 FlvTags flvTags = new FlvTags
@@ -163,11 +150,9 @@ namespace JT1078.Flv
                     SPSBuffer = sps.RawData
                 };
                 flvTags.VideoTagsData.VideoData.AVCDecoderConfiguration = aVCDecoderConfigurationRecord;
-                flvMessagePackWriter.WriteUInt32(previousTagSize);
                 flvMessagePackWriter.WriteFlvTag(flvTags);
-                var data = flvMessagePackWriter.FlushAndGetArray();
-                previousTagSize = (uint)(flvTags.DataSize + 11);
-                return data;
+                flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
+                return flvMessagePackWriter.FlushAndGetArray();
             }
             finally
             {
@@ -177,10 +162,8 @@ namespace JT1078.Flv
 
         /// <summary>
         /// 编码首帧音频,即audioTag[0]
-        /// <para>
-        /// 注意:本方法已写入<see cref="previousTagSize"/>
-        /// </para>
         /// </summary>
+        /// <param name="timestamp"></param>
         /// <returns></returns>
         public byte[] EncoderFirstAudioTag(ulong timestamp)
         {
@@ -197,11 +180,9 @@ namespace JT1078.Flv
                     //flv body tag body
                     AudioTagsData = new AudioTags(AACPacketType.AudioSpecificConfig)
                 };
-                flvMessagePackWriter.WriteUInt32(previousTagSize);
                 flvMessagePackWriter.WriteFlvTag(flvTags);
-                var data = flvMessagePackWriter.FlushAndGetArray();
-                previousTagSize = (uint)(flvTags.DataSize + 11);
-                return data;
+                flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
+                return flvMessagePackWriter.FlushAndGetArray();
             }
             finally
             {
@@ -210,9 +191,9 @@ namespace JT1078.Flv
         }
 
         /// <summary>
-        /// 编码非首帧视频
+        /// 编码视频
         /// </summary>
-        /// <param name="package"></param>
+        /// <param name="package">1078包</param>
         /// <param name="needVideoHeader">是否需要首帧视频</param>
         /// <returns></returns>
         public byte[] EncoderVideoTag(JT1078Package package, bool needVideoHeader = false)
@@ -231,14 +212,26 @@ namespace JT1078.Flv
                 nalus.Remove(sei);
                 if (needVideoHeader)
                 {
-                    var firstVideoTag = EncoderFirstVideoTag(sps, pps, sei);
+                    //flv header
+                    var flvHeader = EncoderFlvHeader(true, false);
+                    flvMessagePackWriter.WriteArray(flvHeader);
+                    // always 0
+                    flvMessagePackWriter.WriteUInt32(0);
+                    //解析sps 
+                    var rawData = h264Decoder.DiscardEmulationPreventionBytes(sps.RawData);
+                    ExpGolombReader h264GolombReader = new ExpGolombReader(rawData);
+                    SPSInfo spsInfo = h264GolombReader.ReadSPS();
+                    //script tag
+                    var scriptTag = EncoderScriptTag(spsInfo);
+                    flvMessagePackWriter.WriteArray(scriptTag);
+                    // first video tag
+                    var firstVideoTag = EncoderFirstVideoTag(spsInfo, sps, pps, sei);
                     flvMessagePackWriter.WriteArray(firstVideoTag);
                 }
                 foreach (var naln in nalus)
                 {
-                    flvMessagePackWriter.WriteUInt32(previousTagSize);
-                    var videoTag = ConversionNaluToVideoTag(naln);
-                    flvMessagePackWriter.WriteArray(videoTag);
+                    var otherVideoTag = EncoderOtherVideoTag(naln);
+                    flvMessagePackWriter.WriteArray(otherVideoTag);
                 }
             }
             return flvMessagePackWriter.FlushAndGetArray();
@@ -280,15 +273,17 @@ namespace JT1078.Flv
             }
             if (aacFrameData != null && aacFrameData.Any())//编码成功,此时为一帧aac音频数据
             {
-                // PreviousTagSize
-                flvMessagePackWriter.WriteUInt32(previousTagSize);
                 // Data Tag Frame
-                flvMessagePackWriter.WriteArray(ConversionAacDataToAudioTag((uint)package.Timestamp, aacFrameData));
+                flvMessagePackWriter.WriteArray(EncoderAacAudioTag((uint)package.Timestamp, aacFrameData));
             }
             return flvMessagePackWriter.FlushAndGetArray();
         }
-
-        byte[] ConversionNaluToVideoTag(H264NALU nALU)
+        /// <summary>
+        /// 编码非首帧视频
+        /// </summary>
+        /// <param name="nALU"></param>
+        /// <returns></returns>
+        public byte[] EncoderOtherVideoTag(H264NALU nALU)
         {
             byte[] buffer = FlvArrayPool.Rent(65535);
             try
@@ -330,9 +325,8 @@ namespace JT1078.Flv
                 //    flvTags.VideoTagsData.VideoData.MultiData.Add(sei.RawData);
                 //}
                 flvMessagePackWriter.WriteFlvTag(flvTags);
-                var data = flvMessagePackWriter.FlushAndGetArray();
-                previousTagSize = (uint)(flvTags.DataSize + 11);
-                return data;
+                flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
+                return flvMessagePackWriter.FlushAndGetArray();
             }
             finally
             {
@@ -340,7 +334,7 @@ namespace JT1078.Flv
             }
         }
 
-        byte[] ConversionAacDataToAudioTag(uint timestamp, byte[] aacFrameData)
+        byte[] EncoderAacAudioTag(uint timestamp, byte[] aacFrameData)
         {
             byte[] buffer = FlvArrayPool.Rent(65535);
             try
@@ -358,7 +352,7 @@ namespace JT1078.Flv
                     AudioTagsData = new AudioTags(AACPacketType.AudioFrame, aacFrameData)
                 };
                 flvMessagePackWriter.WriteFlvTag(flvTags);
-                previousTagSize = (uint)(flvTags.DataSize + 11);
+                flvMessagePackWriter.WriteUInt32((uint)(flvTags.DataSize + 11));
                 return flvMessagePackWriter.FlushAndGetArray();
             }
             finally
diff --git a/src/JT1078.Flv/FlvTags.cs b/src/JT1078.Flv/FlvTags.cs
index 620621c..2557b79 100644
--- a/src/JT1078.Flv/FlvTags.cs
+++ b/src/JT1078.Flv/FlvTags.cs
@@ -10,7 +10,7 @@ namespace JT1078.Flv
         /// Tag Data部分大小
         /// 3个字节
         /// </summary>
-        public uint DataSize { get; set; }
+        public int DataSize { get; set; }
         /// <summary>
         /// Tag时间戳
         /// 3个字节
diff --git a/src/JT1078.Flv/JT1078.Flv.xml b/src/JT1078.Flv/JT1078.Flv.xml
index e625440..7e937e1 100644
--- a/src/JT1078.Flv/JT1078.Flv.xml
+++ b/src/JT1078.Flv/JT1078.Flv.xml
@@ -214,15 +214,16 @@
             </para>
             
             手动编码
-            1、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)"/>
-            2、<see cref="M:JT1078.Flv.FlvEncoder.EncoderScriptTag(System.Boolean,System.Double)"/>
-            3、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFirstVideoTag(JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU)"/>
+            0、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)"/>
+            1、插入  PriviousTagSize =0  always equal 0
+            2、<see cref="M:JT1078.Flv.FlvEncoder.EncoderScriptTag(JT1078.Protocol.MessagePack.SPSInfo,System.Boolean,System.Double)"/>
+            3、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFirstVideoTag(JT1078.Protocol.MessagePack.SPSInfo,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU)"/>
             4、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFirstAudioTag(System.UInt64)"/>
             5、<see cref="M:JT1078.Flv.FlvEncoder.EncoderVideoTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传false
             6、<see cref="M:JT1078.Flv.FlvEncoder.EncoderAudioTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传false
             自动编码
             1、<see cref="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)"/>
-            2、<see cref="M:JT1078.Flv.FlvEncoder.EncoderScriptTag(System.Boolean,System.Double)"/>
+            2、<see cref="M:JT1078.Flv.FlvEncoder.EncoderScriptTag(JT1078.Protocol.MessagePack.SPSInfo,System.Boolean,System.Double)"/>
             3、<see cref="M:JT1078.Flv.FlvEncoder.EncoderVideoTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传true
             4、<see cref="M:JT1078.Flv.FlvEncoder.EncoderAudioTag(JT1078.Protocol.JT1078Package,System.Boolean)"/>第二个参数传true
             </summary>
@@ -230,34 +231,25 @@
         <member name="M:JT1078.Flv.FlvEncoder.EncoderFlvHeader(System.Boolean,System.Boolean)">
             <summary>
             编码flv头
-            <para>
-            注意:本方法已写入<see cref="F:JT1078.Flv.FlvEncoder.previousTagSize"/>
-            </para>
             </summary>
-            <param name="hasVideo"></param>
-            <param name="hasAudio"></param>
+            <param name="hasVideo">是否有视频</param>
+            <param name="hasAudio">是否有音频</param>
             <returns></returns>
         </member>
-        <member name="M:JT1078.Flv.FlvEncoder.EncoderScriptTag(System.Boolean,System.Double)">
+        <member name="M:JT1078.Flv.FlvEncoder.EncoderScriptTag(JT1078.Protocol.MessagePack.SPSInfo,System.Boolean,System.Double)">
             <summary>
             编码脚本Tag
-            <para>
-            注意:本方法已写入<see cref="F:JT1078.Flv.FlvEncoder.previousTagSize"/>
-            </para>
             </summary>
-            <param name="width">视频宽度</param>
-            <param name="height">视频高度</param>
-            <param name="hasAudio">是否含有音频,如果有,则写入音频配置,后来发现即便是有音频,这里给<c>false</c>也没关系</param>
-            <param name="frameRate">帧率</param>
+            <param name="spsInfo">解析后的sps信息</param>
+            <param name="hasAudio">是否有音频</param>
+            <param name="frameRate">帧率 默认25d 即每秒25帧</param>
             <returns></returns>
         </member>
-        <member name="M:JT1078.Flv.FlvEncoder.EncoderFirstVideoTag(JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU)">
+        <member name="M:JT1078.Flv.FlvEncoder.EncoderFirstVideoTag(JT1078.Protocol.MessagePack.SPSInfo,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU,JT1078.Protocol.H264.H264NALU)">
             <summary>
-            编码首帧视频,即videoTag[0]
-            <para>
-            注意:本方法已写入<see cref="F:JT1078.Flv.FlvEncoder.previousTagSize"/>
-            </para>
+             编码首帧视频,即videoTag[0]
             </summary>
+            <param name="spsInfo">sps 解析后的数据</param>
             <param name="sps"></param>
             <param name="pps"></param>
             <param name="sei"></param>
@@ -266,17 +258,15 @@
         <member name="M:JT1078.Flv.FlvEncoder.EncoderFirstAudioTag(System.UInt64)">
             <summary>
             编码首帧音频,即audioTag[0]
-            <para>
-            注意:本方法已写入<see cref="F:JT1078.Flv.FlvEncoder.previousTagSize"/>
-            </para>
             </summary>
+            <param name="timestamp"></param>
             <returns></returns>
         </member>
         <member name="M:JT1078.Flv.FlvEncoder.EncoderVideoTag(JT1078.Protocol.JT1078Package,System.Boolean)">
             <summary>
-            编码非首帧视频
+            编码视频
             </summary>
-            <param name="package"></param>
+            <param name="package">1078包</param>
             <param name="needVideoHeader">是否需要首帧视频</param>
             <returns></returns>
         </member>
@@ -288,6 +278,13 @@
             <param name="needAacHeader">是否需要首帧音频</param>
             <returns></returns>
         </member>
+        <member name="M:JT1078.Flv.FlvEncoder.EncoderOtherVideoTag(JT1078.Protocol.H264.H264NALU)">
+            <summary>
+            编码非首帧视频
+            </summary>
+            <param name="nALU"></param>
+            <returns></returns>
+        </member>
         <member name="P:JT1078.Flv.FlvTags.DataSize">
             <summary>
             Tag Data部分大小
diff --git a/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs b/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs
index c629664..b10bcdd 100644
--- a/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs
+++ b/src/JT1078.Flv/MessagePack/FlvMessagePackWriter_Flv.cs
@@ -33,7 +33,8 @@ namespace JT1078.Flv.MessagePack
                     //todo:VIDEODATA 
                     break;
             }
-            WriteInt24Return(GetCurrentPosition() - DataSizePosition -3-7, DataSizePosition);
+            tag.DataSize = GetCurrentPosition() - 11;
+            WriteInt24Return(tag.DataSize, DataSizePosition);
         }
 
         public void WriteUInt24(uint value)