Android端无需依赖Nginx,如何将FFmpeg输出的m3u8流推至自建HTTP服务器?
解决方案:Android本地m3u8流的HTTP分发(无需Nginx类服务器)
不用依赖第三方HTTP服务器,直接基于现有轻量框架扩展+FFmpeg配置就能搞定,具体步骤如下:
1. 扩展轻量HTTP服务器(以nanohttpd为例)
不用纠结框架有没有“流监听”功能,自己给它加路由逻辑,处理m3u8和TS分片的请求就行。重写nanohttpd的serve方法,判断请求路径后返回对应文件:
- 若请求
.m3u8文件:读取本地生成的m3u8内容,设置响应头为application/vnd.apple.mpegurl返回 - 若请求
.ts分片:找到对应本地文件,支持Range请求(播放端常用断点续传),响应头设为video/MP2T
示例代码:
@Override public Response serve(IHTTPSession session) { String uri = session.getUri(); // 处理m3u8请求 if (uri.endsWith(".m3u8")) { File m3u8File = new File(getFilesDir(), "stream.m3u8"); if (!m3u8File.exists()) { return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "File not found"); } try { String content = new String(Files.readAllBytes(m3u8File.toPath())); return newFixedLengthResponse(Response.Status.OK, "application/vnd.apple.mpegurl", content); } catch (IOException e) { return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, "text/plain", "Error reading file"); } } // 处理TS分片请求 else if (uri.endsWith(".ts")) { String tsFileName = uri.substring(uri.lastIndexOf("/") + 1); File tsFile = new File(getFilesDir(), tsFileName); if (!tsFile.exists()) { return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "File not found"); } try { // 支持Range请求,实现播放端断点续传 Map<String, String> headers = session.getHeaders(); if (headers.containsKey("Range")) { String range = headers.get("Range").replace("bytes=", ""); String[] parts = range.split("-"); long start = Long.parseLong(parts[0]); long end = parts.length > 1 ? Long.parseLong(parts[1]) : tsFile.length() - 1; long length = end - start + 1; RandomAccessFile raf = new RandomAccessFile(tsFile, "r"); raf.seek(start); byte[] data = new byte[(int) length]; raf.read(data); raf.close(); Response response = newFixedLengthResponse(Response.Status.PARTIAL_CONTENT, "video/MP2T", data, length); response.addHeader("Content-Range", "bytes " + start + "-" + end + "/" + tsFile.length()); response.addHeader("Accept-Ranges", "bytes"); return response; } else { return newFixedLengthResponse(Response.Status.OK, "video/MP2T", new FileInputStream(tsFile), tsFile.length()); } } catch (IOException e) { return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, "text/plain", "Error reading file"); } } // 其他请求返回404 return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "Not found"); }
2. 配置FFmpeg生成可访问的m3u8流
让FFmpeg把m3u8和TS分片输出到应用自己的文件目录(比如getFilesDir(),默认有读写权限,不用额外申请),用以下命令:
ffmpeg -i [Vulkan流源路径] -c:v h264 -crf 23 -preset medium -hls_time 2 -hls_list_size 5 -hls_flags delete_segments -hls_segment_filename "/data/data/你的应用包名/files/stream_%03d.ts" "/data/data/你的应用包名/files/stream.m3u8"
参数说明:
-hls_time 2:每个TS分片时长设为2秒-hls_list_size 5:m3u8文件只保留最近5个分片,避免文件过大-hls_flags delete_segments:自动删除旧分片,节省存储空间- 记得把
你的应用包名替换成实际包名,或者用代码动态获取目录路径
3. 实时流更新处理
FFmpeg会实时更新m3u8文件内容,HTTP服务器每次收到m3u8请求时都读取最新文件返回,播放端就能拿到最新的分片列表,实现实时流播放。
4. 测试播放
启动HTTP服务器后,在同一局域网的设备上,用支持m3u8的播放器访问http://Android设备IP:服务器端口/stream.m3u8即可播放。
内容的提问来源于stack exchange,提问作者SOSparachuter1
相关产品推荐
相关产品推荐

