经过几天搜索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();
}
在我的电脑上执行'mplayer http://tabletIP:8080
'并且平板电脑注册了一个连接(从而开始我的流光和相机预览)。这也适用于VLC。
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();
}
}
之后通过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说'缓存没有填充',问题可能在最后一段代码段,需要帮助〜:)
谢天谢地!
答案 0 :(得分:12)
我明白了。好像我的http- / content-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-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
东西)...