8

MJPEG ストリームを作成しようとしています。一連の jpeg をストリームにまとめて、ユーザーが URL をクリックして mjpeg ストリームを取得できるようにしたいと考えています。私はここ数日、これを機能させるために努力してきましたが、それは不可能かもしれません。私はエーテルを育て、ネットのどこかで軸カメラからのパケットを聞いて、それを模倣しようとしました。私はもともとWCFを使用して「ストリーム」を返そうとしましたが、後でそのストリームにコンテンツタイプを設定する必要があることがわかったので、WCF REST APIを試しましたが、同じ問題に苦しんでいます。そのため、必要最小限の HTTPListener を使用してイベントを処理しています。私は WCF を使用したいと思っていますが、適切なコンテンツ タイプのストリームを返すことができるかどうかはわかりません。だからここに」

リスナーのコールバックのハンドラーに次のように記述します。

        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);

Render メソッドは次のようになります

        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 および境界セットに設定する必要があるということです。次に、ストリーム内の jpeg を境界で区切ります。

これは、私が作っているよりも簡単なはずですが、どこが間違っているのか疑問に思っています。この URL を IE または Firefox にロードすると、ハングします。ソースが URL である img タグを使用してスタブ HTML ページを作成しようとすると、壊れた画像が表示されます。

任意のアイデア、ありがとう

ジョシュ

4

2 に答える 2

7

さて、私が知る限り、あなたの問題は次のとおりです。

  1. StreamWriter正しい選択ではありません。通常のストリーム書き込み機能を使用しても問題ありません。つまり、文字列ではなくバイト配列にデータを書き込む必要があります。

  2. 画像の Binary データを String64 に変換すると、ブラウザはそれを認識せず、32bit データだと思い込んでしまいます。

  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);
    }
}
于 2009-11-05T18:59:02.177 に答える