using System.IO; using System.Windows.Media.Imaging; using IvfTl.ControlHost.Debug; using Xunit; namespace IvfTl.ControlHost.Tests { public class MjpegStreamWriterTests { // 造一张 4x4 纯色 24bpp BGR 像素(每像素 3 字节)。 private static byte[] SolidBgr(int w, int h, byte b, byte g, byte r) { var buf = new byte[w * h * 3]; for (int i = 0; i < w * h; i++) { buf[i * 3] = b; buf[i * 3 + 1] = g; buf[i * 3 + 2] = r; } return buf; } [Fact] public void EncodeJpeg_ProducesDecodableJpeg_WithRightSize() { var rgb = SolidBgr(4, 4, 10, 20, 30); byte[] jpeg = MjpegStreamWriter.EncodeJpeg(rgb, 4, 4); Assert.NotNull(jpeg); Assert.True(jpeg.Length > 2); // JPEG 魔数 FF D8 开头 Assert.Equal(0xFF, jpeg[0]); Assert.Equal(0xD8, jpeg[1]); // 能被解码器读回、尺寸对 var dec = new JpegBitmapDecoder(new MemoryStream(jpeg), BitmapCreateOptions.None, BitmapCacheOption.OnLoad); Assert.Equal(4, dec.Frames[0].PixelWidth); Assert.Equal(4, dec.Frames[0].PixelHeight); } [Fact] public void FrameBytes_WrapsJpegInMultipartBoundary() { var jpeg = new byte[] { 0xFF, 0xD8, 1, 2, 0xFF, 0xD9 }; byte[] frame = MjpegStreamWriter.FrameBytes(jpeg); string head = System.Text.Encoding.ASCII.GetString(frame, 0, 60); Assert.Contains("--frame", head); Assert.Contains("Content-Type: image/jpeg", head); Assert.Contains("Content-Length: 6", head); // 帧体起点 = header 长度;尾部是 \r\n string headerStr = "--frame\r\nContent-Type: image/jpeg\r\nContent-Length: 6\r\n\r\n"; int bodyStart = System.Text.Encoding.ASCII.GetByteCount(headerStr); var body = new byte[jpeg.Length]; System.Array.Copy(frame, bodyStart, body, 0, jpeg.Length); Assert.Equal(jpeg, body); // 帧体整段 == 原 jpeg Assert.Equal((byte)'\r', frame[frame.Length - 2]); Assert.Equal((byte)'\n', frame[frame.Length - 1]); } } }