using System.Collections.Generic; using System.Text; using ivf_tl_Operate.Debug; using Xunit; namespace IvfTl.ControlHost.Tests { public class MjpegFrameParserTests { // 造一个 multipart 帧字节(与 control MjpegStreamWriter.FrameBytes 同格式)。 private static byte[] MakeFrame(byte[] jpeg) { string head = $"--frame\r\nContent-Type: image/jpeg\r\nContent-Length: {jpeg.Length}\r\n\r\n"; var ms = new List(); ms.AddRange(Encoding.ASCII.GetBytes(head)); ms.AddRange(jpeg); ms.AddRange(Encoding.ASCII.GetBytes("\r\n")); return ms.ToArray(); } private static List FeedAll(MjpegFrameParser p, byte[] data) { var all = new List(); all.AddRange(p.Feed(data, data.Length)); return all; } [Fact] public void SingleWholeFrame_YieldsOneJpeg() { var jpeg = new byte[] { 0xFF, 0xD8, 1, 2, 3, 0xFF, 0xD9 }; var p = new MjpegFrameParser(); var frames = FeedAll(p, MakeFrame(jpeg)); Assert.Single(frames); Assert.Equal(jpeg, frames[0]); } [Fact] public void TwoFramesInOneChunk_YieldsTwo() { var j1 = new byte[] { 0xFF, 0xD8, 1, 0xFF, 0xD9 }; var j2 = new byte[] { 0xFF, 0xD8, 9, 8, 0xFF, 0xD9 }; var combined = new List(); combined.AddRange(MakeFrame(j1)); combined.AddRange(MakeFrame(j2)); var p = new MjpegFrameParser(); var frames = FeedAll(p, combined.ToArray()); Assert.Equal(2, frames.Count); Assert.Equal(j1, frames[0]); Assert.Equal(j2, frames[1]); } [Fact] public void FrameSplitAcrossChunks_Reassembles() { var jpeg = new byte[] { 0xFF, 0xD8, 5, 6, 7, 8, 0xFF, 0xD9 }; byte[] full = MakeFrame(jpeg); var p = new MjpegFrameParser(); // 在帧中间切两半喂 int mid = full.Length / 2; var first = new byte[mid]; var second = new byte[full.Length - mid]; System.Array.Copy(full, 0, first, 0, mid); System.Array.Copy(full, mid, second, 0, second.Length); var frames = new List(); frames.AddRange(p.Feed(first, first.Length)); Assert.Empty(frames); // 半帧,还吐不出 frames.AddRange(p.Feed(second, second.Length)); Assert.Single(frames); Assert.Equal(jpeg, frames[0]); } [Fact] public void BadHeaderWithoutContentLength_SkippedThenRecovers() { var jpeg = new byte[] { 0xFF, 0xD8, 7, 0xFF, 0xD9 }; var bad = Encoding.ASCII.GetBytes("--frame\r\nContent-Type: image/jpeg\r\n\r\n"); // 无 Content-Length var data = new List(); data.AddRange(bad); data.AddRange(MakeFrame(jpeg)); var p = new MjpegFrameParser(); var frames = new List(p.Feed(data.ToArray(), data.Count)); Assert.Single(frames); Assert.Equal(jpeg, frames[0]); } [Fact] public void EmptyInput_ReturnsEmpty_NoThrow() { var p = new MjpegFrameParser(); var frames = new List(p.Feed(new byte[0], 0)); Assert.Empty(frames); } } }