using System;
using System.IO;
using System.Text;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace IvfTl.ControlHost.Debug
{
///
/// MJPEG 推流的纯逻辑:RGB 像素 → JPEG 字节,JPEG → multipart 帧字节。
/// 无 IO、无相机依赖,可纯单测。真正的抓帧/写流由 ControlHttpServer 推流分支驱动。
/// 相机抓帧返回 24bpp BGR(GrabStable),WPF JpegBitmapEncoder 需 Bgr24 像素格式。
///
public static class MjpegStreamWriter
{
public const string Boundary = "frame";
/// 把 24bpp BGR 像素(width*height*3)编码成 JPEG 字节(质量 85)。
public static byte[] EncodeJpeg(byte[] bgr, int width, int height, int quality = 85)
{
if (bgr == null || bgr.Length < width * height * 3) return null;
int stride = width * 3;
var bmp = BitmapSource.Create(width, height, 96, 96,
PixelFormats.Bgr24, null, bgr, stride);
var encoder = new JpegBitmapEncoder { QualityLevel = quality };
encoder.Frames.Add(BitmapFrame.Create(bmp));
using (var ms = new MemoryStream())
{
encoder.Save(ms);
return ms.ToArray();
}
}
/// 把 JPEG 字节封成一个 multipart/x-mixed-replace 帧(含 boundary 头 + 帧体 + \r\n)。
public static byte[] FrameBytes(byte[] jpeg)
{
if (jpeg == null) return null;
string header = $"--{Boundary}\r\nContent-Type: image/jpeg\r\nContent-Length: {jpeg.Length}\r\n\r\n";
byte[] head = Encoding.ASCII.GetBytes(header);
byte[] tail = Encoding.ASCII.GetBytes("\r\n");
var frame = new byte[head.Length + jpeg.Length + tail.Length];
Buffer.BlockCopy(head, 0, frame, 0, head.Length);
Buffer.BlockCopy(jpeg, 0, frame, head.Length, jpeg.Length);
Buffer.BlockCopy(tail, 0, frame, head.Length + jpeg.Length, tail.Length);
return frame;
}
/// 响应头里的 Content-Type 值(供 ControlHttpServer 写头用)。
public static string ContentType => $"multipart/x-mixed-replace; boundary={Boundary}";
}
}