18

SO と google を数日間検索した後、私はあきらめ始めたので、ここに投稿したほうがよいと思いました。

ある種のビデオチャットを提供する Android アプリを作成しています。これは可能な限りリアルタイムに近づける必要があるため、さまざまなプロトコルについて読み、最初に MJPEG を試すことにしました (今のところオーディオには関係ありません)。

現在、データのストリーミングは私を夢中にさせています。接続が確立され、アプリはカメラ プレビュー フレームのストリームへの書き込みを開始しますが、VLC も mplayer もビデオの再生を開始しません。接続を監視すると、データが到着していることがわかります。

接続 このコードは非同期タスクによって実行され、成功するとリスナーに通知されます。

try
    {
        ServerSocket server = new ServerSocket(8080);

        socket = server.accept();

        server.close();

        Log.i(TAG, "New connection to :" + socket.getInetAddress());

        stream = new DataOutputStream(socket.getOutputStream());
        prepared = true;
    }
    catch (IOException e)
    {
        Log.e(TAG, e.getMessage();
    }

PC で「mplayer http://tabletIP:8080」を実行すると、タブレットが接続を登録します (したがって、ストリーマーとカメラのプレビューが開始されます)。これは VLC でも動作します。

ストリーミングこれにより、ヘッダーがストリームに書き込まれます。

if (stream != null)
{
    try
    {
        // send the header
        stream.write(("HTTP/1.0 200 OK\r\n" +
                      "Server: iRecon\r\n" +
                      "Connection: close\r\n" +
                      "Max-Age: 0\r\n" +
                      "Expires: 0\r\n" +
                      "Cache-Control: no-cache, private\r\n" + 
                      "Pragma: no-cache\r\n" + 
                      "Content-Type: multipart/x-mixed-replace; " +
                      "boundary=--" + boundary +
                      "\r\n\r\n").getBytes());

        stream.flush();

        streaming = true;
    }
    catch (IOException e)
    {
        notifyOnEncoderError(this, "Error while writing header: " + e.getMessage());
        stop();
    }
}

その後、Camera.onPreviewFrame() コールバックを通じてストリーミングがトリガーされます。

@Override
public void onPreviewFrame(byte[] data, Camera camera)
{
    frame = data;

    if (streaming)
        mHandler.post(this);
}

@Override
public void run()
{
    // TODO: cache not filling?
    try
    {
                    // buffer is a ByteArrayOutputStream
        buffer.reset();

        switch (imageFormat)
        {
            case ImageFormat.JPEG:
                // nothing to do, leave it that way
                buffer.write(frame);
                break;

            case ImageFormat.NV16:
            case ImageFormat.NV21:
            case ImageFormat.YUY2:
            case ImageFormat.YV12:
                new YuvImage(frame, imageFormat, w, h, null).compressToJpeg(area, 100, buffer);
                break;

            default:
                throw new IOException("Error while encoding: unsupported image format");
        }

        buffer.flush();

        // write the content header
        stream.write(("--" + boundary + "\r\n" + 
                      "Content-type: image/jpg\r\n" + 
                      "Content-Length: " + buffer.size() + 
                      "\r\n\r\n").getBytes());

        // Should omit the array copy
        buffer.writeTo(stream);

        stream.write("\r\n\r\n".getBytes());
        stream.flush();
    }
    catch (IOException e)
    {
        stop();
        notifyOnEncoderError(this, e.getMessage());
    }
}

スローされる例外はありません。mHandler は、独自の HandlerThread で実行されます。念のため、AsyncTask を使用してみましたが、役に立ちませんでした (ところで、これの方が良いですか?)。

エンコードされたフレームは Android 側で問題ありません。それらを jpg ファイルに保存して開くことができました。

私の推測では、どうにかしてデータをクラスター化するか、ソケットなどにいくつかのオプションを設定する必要がありますが、....まあ、私は立ち往生しています。

tl;dr: VLC がストリームを再生していません。mplayer は「キャッシュがいっぱいではありません」と言っています。おそらく最後のコード セグメントに問題があります。助けが必要です ~ :)

よろしくお願いします!

4

1 に答える 1

12

わかった。私の http-/content-headers がめちゃくちゃだったようです。適切なヘッダーは次のとおりです。

stream.write(("HTTP/1.0 200 OK\r\n" +
                          "Server: iRecon\r\n" +
                          "Connection: close\r\n" +
                          "Max-Age: 0\r\n" +
                          "Expires: 0\r\n" +
                          "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\r\n" +
                          "Pragma: no-cache\r\n" + 
                          "Content-Type: multipart/x-mixed-replace; " +
                          "boundary=" + boundary + "\r\n" +
                          "\r\n" +
                          "--" + boundary + "\r\n").getBytes());

stream.write(("Content-type: image/jpeg\r\n" +
                      "Content-Length: " + buffer.size() + "\r\n" +
                      "X-Timestamp:" + timestamp + "\r\n" +
                      "\r\n").getBytes());

buffer.writeTo(stream);
stream.write(("\r\n--" + boundary + "\r\n").getBytes());

もちろん、境界線をどこに置くかはあなたの自由です。また、おそらくオプションのフィールドがいくつかあります (たとえば、Cache-Control のほとんど) が、これは機能します。重要な部分は、改行(\r\nもの)を覚えておくことです...

于 2013-02-12T14:59:13.857 に答える