You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 01:01:17