Android:将相机流式传输为mjpeg
经过几天搜索SO和谷歌,我开始放弃,所以我想我不妨在这里发帖。
我正在创建一个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”,平板电脑注册连接(从而启动我的流媒体和相机预览)。这也适用于VLC。http://tabletIP:8080
流这会将标头写入流:
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说“缓存未填充”,问题可能在最后一个代码段中,需要帮助〜:)
谢谢!