java Android:将相机流式传输为 mjpeg
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14815103/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android: streaming the camera as mjpeg
提问by Managarm
After several days of searching SO and google I'm beginning to give up, so I thought I might as well post here.
经过几天搜索 SO 和 google 我开始放弃,所以我想我不妨在这里发帖。
I'm creating an android app which should offer some kind of video chat. As this should be as close as possible to realtime, I read about various protocols and decided to try MJPEG for starters (not concerning with audio for now).
我正在创建一个 android 应用程序,它应该提供某种视频聊天。由于这应该尽可能接近实时,我阅读了各种协议并决定为初学者尝试 MJPEG(目前不涉及音频)。
Right now streaming the data is driving me nuts. The connection gets established, the app starts writing the camera preview frames to the stream, but neither VLC nor mplayer start playing video. Monitoring the connection reveals that the data is arriving.
现在流式传输数据让我发疯。连接建立,应用程序开始将相机预览帧写入流,但 VLC 和 mplayer 都不会开始播放视频。监控连接显示数据正在到达。
ConnectingThis code is executed by an async task, a listener is notified on success:
连接此代码由异步任务执行,成功时通知侦听器:
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();
}
On my PC I execute 'mplayer http://tabletIP:8080
' and the tablet registers a connection (and thus starts my streamer and the camera preview). This also works with VLC.
在我的 PC 上,我执行 'mplayer http://tabletIP:8080
' 并且平板电脑注册一个连接(从而启动我的流媒体和相机预览)。这也适用于 VLC。
StreamingThis writes the header to the stream:
Streaming这将标头写入流:
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();
}
}
Afterwards streaming is triggered through the Camera.onPreviewFrame() Callback:
之后通过 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());
}
}
There is no exception thrown. The mHandler runs in it's own HandlerThread. Just to be sure I tried using an AsyncTask, to no avail (btw, is this better?).
没有抛出异常。mHandler 在它自己的 HandlerThread 中运行。只是为了确保我尝试使用 AsyncTask,但无济于事(顺便说一句,这更好吗?)。
The encoded frames are fine on the android side, I saved them to jpg files and could open them.
编码的帧在 android 端很好,我将它们保存为 jpg 文件并可以打开它们。
My guess is that I have to cluster the data somehow or have to set some options for the socket or something, but....well, I'm stuck.
我的猜测是我必须以某种方式对数据进行聚类,或者必须为套接字或其他东西设置一些选项,但是......好吧,我被卡住了。
tl;dr:VLC not playing stream, mplayer says 'cache not filling', problem probably in last code segment, need help~ :)
tl;dr:VLC 不播放流,mplayer 说“缓存未填充”,问题可能出现在最后一段代码中,需要帮助~ :)
Thank you kindly!
非常感谢你!
采纳答案by Managarm
I got it. Seems, like my http-/content-headers were messed up. The proper headers should be:
我知道了。似乎,就像我的 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());
and
和
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());
Of course, where to put the boundary is your own choice. Also there are probably some fields which are optional (e.g. most in Cache-Control), but this works and till now I was too lazy to strip them down. The important part is to remember the linebreaks (\r\n
thingies)...
当然,边界放在哪里是你自己的选择。也可能有一些字段是可选的(例如大多数在 Cache-Control 中),但这是有效的,直到现在我都懒得剥离它们。重要的部分是记住换行符(\r\n
东西)......