创建自己的MJPEG流

8
我正在尝试创建一个MJPEG流,我有一系列的JPEG图片,想把它们合并成一个流,以便用户只需点击一个URL即可获取MJPEG流。我已经尝试了几天,但可能无法实现。我使用Ethereal监听了来自某个网络上Axis相机的数据包,并试图模仿它。最初我尝试使用WCF返回“流”,但后来发现需要设置该流的内容类型,所以我尝试了WCF REST API,但它也存在同样的问题。因此,现在我只是使用了最基本的HTTPListener,并处理事件。我非常希望使用WCF,但我不确定它是否允许我返回具有正确内容类型的流。下面是HTTPListener的处理程序。
        HttpListenerResponse response = context.Response;
        response.ProtocolVersion = new System.Version(1, 0);
        response.StatusCode = 200;
        response.StatusDescription = "OK";
        response.ContentType = "multipart/x-mixed-replace;boundary=" + BOUNDARY + "\r\n";
        System.IO.Stream output = response.OutputStream;
        Render(output);

渲染方法看起来像这样:
        var writer = new StreamWriter(st);
        writer.Write("--" + BOUNDARY + "\r\n");
        while (true)
        {
            for (int i = 0; i < imageset.Length; i++)
            {
                var resource = Properties.Resources.ResourceManager.GetObject(imageset[i]) as Bitmap;
                var memStream = new MemoryStream();
                resource.Save(memStream,ImageFormat.Jpeg);
                byte[] imgBinaryData = memStream.ToArray();
                string s = Convert.ToBase64String(imgBinaryData);
                writer.Write("Content-type: image/jpeg\r\n");
                foreach (var s1 in imgBinaryData)
                {
                    writer.Write((char)s1);
                }
                writer.Write("\n--" + BOUNDARY + "\n");
                writer.Flush();
                Thread.Sleep(500);
            }
        }

目前,我已经将几个jpeg图像作为dll的属性添加,并正在对它们进行迭代。最终,这些将是动态图像,但现在我只想让它正常工作。

根据我对MJPEG(规范)的理解,内容必须设置为multipart/x-mixed-replace,并设置一个边界。然后,您只需通过边界在流中分隔jpegs即可。

这似乎应该比我做的更简单,但我想知道我错在哪里。如果我在IE或Firefox中加载此URL,它就会挂起。如果我尝试制作一个带有img标记的存根html页面,其源是URL,则会出现损坏的图像。

有任何想法,谢谢。

Josh

2个回答

7
据我所知,以下是您的问题:
1. StreamWriter不是正确的选择。使用常规流写入函数就可以了。也就是说,您应该用字节数组而不是字符串来写入数据。
2. 您将图像的二进制数据转换为String64,但浏览器并不知道这一点,仍然认为它是32位数据。
3. 您的jpeg帧格式不正确。您还应该在帧头中添加Content-Length,以便接收流的应用程序知道何时停止读取,而无需每次读取都检查下一个边界字符串。这将使读取数据的速度快4-5倍。而且您的换行符也存在不一致性,有些是"\r\n",而有些则是"\n"。
4. While循环是一个无限循环。
因此,以下是解决方案。
注意:可能会有一些语法错误,但您大概能理解基本思路。
private byte[] CreateHeader(int length)
{
    string header = 
        "--" + BOUDARY + "\r\n" +
        "Content-Type:image/jpeg\r\n" +
        "Content-Length:" + length + "\r\n" +
        + "\r\n"; // there are always 2 new line character before the actual data

    // using ascii encoder is fine since there is no international character used in this string.
    return ASCIIEncoding.ASCII.GetBytes(header); 
}

public byte[] CreateFooter()
{
    return ASCIIEncoding.ASCII.GetBytes("\r\n");
}

private void WriteFrame(Stream st, Bitmap image)
{
    // prepare image data
    byte[] imageData = null;

    // this is to make sure memory stream is disposed after using
    using (MemoryStream ms = new MemoryStream())
    {
        image.Save(ms, ImageFormat.Jpeg);

        imageData = ms.ToArray();
    }

    // prepare header
    byte[] header = CreateHeader(imageData.Length);
    // prepare footer
    byte[] footer = CreateFooter();

    // Start writing data
    st.Write(header, 0, header.Length);
    st.Write(imageData, 0, imageData.Length);
    st.Write(footer, 0, footer.Length);
}

private void Render(Stream st)
{
    for (int i = 0; i < imageset.Length; i++)
    {
        var resource = Properties.Resources.ResourceManager.GetObject(imageset[i]) as Bitmap;
        WriteFrame(st, resource);
        Thread.Sleep(500);
    }
}

3
我知道这已经过去一年多了...但还是要说一下。规格说明中指出,在标头名称后的冒号和标头值之间应该有一个单空格(' ',ASCII 32)。 - Thomas Kjørnes

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接