Java HTTP处理"流式传输"视频文件

7

我正在创建一个Java应用程序,将视频文件通过http流传输到浏览器(目前是Chrome v24.x)。该视频被发送到FFmpeg,其输出通过HTTP发送。

现在,一旦文件完全编码,该文件将使用分块传输进行服务,并响应范围请求。示例标头:

请求

GET /file/9fe6b502-c127-47c2-b6d2-83ea58676a8d HTTP/1.1 : Host: localhost:1234 : Connection: keep-alive : Accept-Encoding: identity;q=1, *;q=0 : User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17 : Accept: */* : Referer: http://localhost:1234/media/9fe6b502-c127-47c2-b6d2-83ea58676a8d : Accept-Language: en-US,en;q=0.8 : Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 : Cookie: plushContainerWidth=100%25; plushNoTopMenu=0 : Range: bytes=0- :  

响应

HTTP/1.1 206 Partial Content : Connection: close : Date: Mon, 28 Jan 2013 14:51:52 GMT : Content-Type: video/mp4 : Etag: "9fe6b502-c127-47c2-b6d2-83ea58676a8d" : Accept-Ranges: bytes : Content-Range: bytes 0-625825/625826 : Content-Length: 625826 : Transfer-Encoding: chunked :  

发送的数据量为625826字节,不包括头部数据和分块开销。

现在,这个工作非常正常!

问题是当GET请求发生在文件编码完成之前。我尝试立即开始使用HTTP发送文件,仅使用分块传输,没有内容长度属性或范围,因为它们目前未知。这会导致浏览器等待完整的文件(并且在传输完成之前不开始播放)。此外,当文件传输完成时,浏览器报告视频错误,无法播放该文件。示例标头:

请求

Request : GET /file/9fe6b502-c127-47c2-b6d2-83ea58676a8d HTTP/1.1 : Host: localhost:1234 : Connection: keep-alive : Accept-Encoding: identity;q=1, *;q=0 : User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17 : Accept: */* : Referer: http://localhost:1234/media/9fe6b502-c127-47c2-b6d2-83ea58676a8d : Accept-Language: en-US,en;q=0.8 : Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 : Cookie: plushContainerWidth=100%25; plushNoTopMenu=0 : Range: bytes=0- 

响应

HTTP/1.1 200 OK : Connection: close : Date: Mon, 28 Jan 2013 14:51:29 GMT : Content-Type: video/mp4 : Transfer-Encoding: chunked

发送的数据为625826字节,不包括头部数据和分块开销。

有没有人有任何想法是什么出了问题,或如何开始播放一个视频而不知道文件的完整长度?

感谢您的时间,

詹姆斯。

//编辑

由于请求不完整文件状态“范围:字节= 0-”,我可以回复部分内容的“n”字节(比如1000字节)与Content-Range:字节0-999 / *?

//编辑2 根据要求,这里是输出文件的代码。由于代码实际上跨越了几个类,因此这是压缩的。

File f = new File(_filename);
RandomAccessFile raf = new RandomAccessFile(f, "r");
ChunkedOutputStream cos = new ChunkedOutputStream(_out, 1024 * 100);
byte[] bytes;

while (true){
    lBufferSizeMax = Math.min(lBufferSize, fc.length() - lCompleted);
    bytes = new byte[(int)lBufferSizeMax];

    lCurrentRead = fc.read(bytes);
    if (lCurrentRead == 0){
        break;
    }

    cos.write(bytes);
}

这不是输出代码的问题,因为在转码完成之前和之后,相同的代码都可用于输出视频。这是关于标头的问题,涉及浏览器对请求的期望以及范围请求如何处理未知文件大小。 - JAC2703
1
以编程方式设置您要写入“outputStream”的文件大小为“-1”。 - Andremoniy
1
需要澄清的是,播放视频流的不是浏览器本身,而是浏览器插件(例如媒体播放器等)。你能否澄清一下?也许插件需要以某种格式输入URL?http://fmj-sf.net/fmj/getting_started.php - djangofan
@JAC2703请展示你写content-type等的完整代码。 - Andremoniy
为什么不在启动http服务之前对所有文件进行编码?它们是否正在快速更改,甚至提供实时内容? - gyorgyabraham
显示剩余3条评论
1个回答

2
请注意,ffmpeg 在编码结束时很可能会重写文件的部分内容。这意味着视频文件中的元数据直到最后才完整,因此在那之前没有必要尝试流式传输文件开头的部分。您可以通过将 ffmpeg 命令行中的输出文件名替换为单破折号并将 stdout 重定向到文件来检查。将 ffmpeg ... out.mov 替换为 ffmpeg ... - > out.mov。如果您收到错误消息,表示格式需要可寻址输出,则受到影响。
我几乎做了您尝试做的事情,结果在我的Git repo中。我使用的容器格式是 FLV,其中我可以玩弄元数据。不幸的是,那不是 iPad/iPhone 设备想要看到的东西。

Knut,你说得完全正确!我自己通过粗略的vi比较发现了这一点——输出文件的开头在编码过程中会发生变化。FLV并不是一个真正的选择,然而随着我的选择越来越有限,情况可能会改变。我可能不得不采取gyabraham的方法。 - JAC2703

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接