java+ffmpeg实现mp4转m3u8切片(ts)

😊 @ 作者: 一恍过去
🎊 @ 社区: Java技术栈交流
🎉 @ 主题: java+ffmpeg实现mp4转m3u8切片(ts)

在这里插入图片描述

前言

M3U8是一种基于文本的播放列表文件格式,用于指定多个媒体文件(通常是视频或音频)的播放顺序和信息,常用于网络流媒体传输。M3U8文件通常包含一系列URL地址,用于指定媒体文件的片段(segment)或流(stream),以及相关的元数据和参数。

M3U8文件一般通过HTTP协议进行下载和访问,播放器通过解析M3U8文件获取媒体文件的地址和相关信息,并根据需要逐个下载和播放分片媒体文件,从而实现流媒体的播放。由于其开放的文本格式和广泛的支持,M3U8文件在各种流媒体应用中得到了广泛的应用,特别是在移动设备和网络直播领域。

1、前提

首先需要安装ffmpeg,参考《Linux+Windows实现ffmpeg配置安装》

注意:以下功能的实现是在Windwos平台

2、mp4转m3u8

命令如下:

ffmpeg -i D:/m3u8/demo.mp4 -c:v copy -c:a copy -f ssegment -segment_format mpegts -segment_list D:/m3u8/hls/test.m3u8 -segment_time 10 D:/m3u8/hls/test%05d.ts

D:/m3u8/demo.mp4输入的mp4文件
D:/m3u8/hls/test.m3u8输出的m3u8文件
-segment_time设置每片的长度,单位为秒
-segment_list :段文件的名称,%05d表示5位数字


生成的效果是:将demo.mp4视频文件每10秒生成一个ts文件,最后生成一个m3u8文件,m3u8文件是ts的索引文件。

在这里插入图片描述
在这里插入图片描述

3、播放测试

3.1、html

为了方便测试,写了一个简单的html,详细代码见《测试html》,html只需要解压后,修改里面的src地址,设置为实际的m3u8播放地址
比如:
在这里插入图片描述

3.2、nginx

除此之外需要配置,nginx实现资源播放,配置如下:

location /hls {
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Allow-Headers X-Requested-With;
            add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
            
            types {  
                application/vnd.apple.mpegurl m3u8;  
                video/mp2t ts;  
            }
		    alias  D:/m3u8/hls/; #切片存放地址
            expires -1;
            add_header Cache-Control no-cache;  
    }

本地的m3u8播放路径为:http:127.0.0.1/hls/test.m3u8

4、java实现

在实际开发中,可以通过java调用cmd命令,使用ffmpeg进行切片操作,主要代码如下:

4.1、ProcessUtil.java

public class ProcessUtil {

    public static String execute(List<String> command) {
        StringBuffer inputStringBuffer = new StringBuffer();
        StringBuffer errorStringBuffer = new StringBuffer();
        try {
            ProcessBuilder builder = new ProcessBuilder(command);
            Process process = builder.start();
            System.out.println("============inputStream============");
            // 处理InputStream
            Thread t1 = new Thread(() -> {
                InputStream input = null;
                InputStreamReader reader = null;
                BufferedReader buffer = null;

                try {
                    input = process.getInputStream();
                    reader = new InputStreamReader(input);
                    buffer = new BufferedReader(reader);
                    String inputLine = "";
                    while ((inputLine = buffer.readLine()) != null) {
                        System.out.println(inputLine);
                        inputStringBuffer.append(inputLine);
                    }
                    //退出循环后表示结束流
                    System.out.println("===》》退出循环后表示结束流");
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (buffer != null) {
                            buffer.close();
                        }
                        if (reader != null) {
                            reader.close();
                        }
                        if (input != null) {
                            input.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
            t1.setName("deviceName");
            t1.start();

            System.out.println("============errorStream============");
            // 处理ErrorStream
            new Thread(() -> {
                InputStream input = null;
                InputStreamReader reader = null;
                BufferedReader buffer = null;
                try {
                    input = process.getErrorStream();
                    reader = new InputStreamReader(input);
                    buffer = new BufferedReader(reader);
                    String errorLine = "";
                    while ((errorLine = buffer.readLine()) != null) {
                        System.out.println(errorLine);
                        errorStringBuffer.append(errorLine);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (buffer != null) {
                            buffer.close();
                        }
                        if (reader != null) {
                            reader.close();
                        }
                        if (input != null) {
                            input.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();

            // 这里进程阻塞,将等待外部转换成功后,才往下执行
            //process.waitFor();
            t1.wait();

            /**
             * 只会存在一个输入流返回
             */
            if (inputStringBuffer != null) {
                return inputStringBuffer.toString();
            }
            if (errorStringBuffer != null) {
                return errorStringBuffer.toString();
            }

        } catch (InterruptedException | IOException e) {
            e.printStackTrace();
            return null;
        }
        return null;
    }
}

4.2、Main.java

public class Main {
    public static void main(String[] args) {
        String input = "D:/m3u8/demo.mp4";
        String output = "D:/m3u8/hls/test.m3u8";
        List<String> command = new ArrayList<>();
        command.add("ffmpeg");
        command.add("-i");
        command.add(input);
        command.add("-c:v");
        command.add("copy");
        command.add("-c:a");
        command.add("copy");
        command.add("-f");
        command.add("ssegment");
        command.add("-segment_format");
        command.add("mpegts");
        command.add("-segment_list");
        command.add(output);
        command.add("-segment_time");
        command.add("10");
        command.add("D:/m3u8/hls/test%05d.ts");
        String result = ProcessUtil.execute(command);
    }
}

4.3、验证测试

再执行完java程序以后,如果在D:/m3u8/hls生成了ts和m3u8文件,则表示成功;

5、TS切片好处

比如:我们上传一个10MB的视频进行播放,如果不进行切片话,前端页面在播放时需要先下载完整的视频,这样会导致视频加载时间过长。如果使用ts切片后使用m3u8进行播放,在前端播放时页面会自动间隔一定时间下载ts文件进行播放。

下面视频展示,动态加载ts的过程:
在这里插入图片描述

ffmpeg是一个开源的跨平台多媒体处理工具,可以实现MP4视频文件换为M3U8格式。以下是一种常见的使用ffmpeg实现MP4M3U8的方法: 1. 首先,将MP4文件切片TS格式,创建M3U8清单。可以使用以下命令来实现: ``` ffmpeg -i demo.mp4 -profile:v baseline -level 3.0 -start_number 0 -hls_time 10 -hls_list_size 0 -f hls demo.m3u8 ``` 这个命令将会把demo.mp4文件切片TS格式,并生成一个名为demo.m3u8M3U8清单文件。 2. 如果需要批量执行MP4M3U8操作,可以使用像引用中提供的PHP脚本一样的方法。这个脚本将遍历一个视频文件数组,针对每个视频文件执行MP4M3U8的操作。 3. 如果已经拥有一个包含TS清单的M3U8文件,可以使用以下命令将其换为MP4文件: ``` ffmpeg -i https://xxx.com/index.m3u8 -acodec copy -vcodec copy -absf aac_adtstoasc xxx.mp4 ``` 这个命令将会下载并解析名为index.m3u8M3U8文件,并将其中的TS流合并为一个MP4文件,保存为xxx.mp4。 总结起来,FFmpeg可以通过切片MP4文件并生成M3U8清单文件来实现MP4M3U8的功能。同时,也可以通过解析包含TS清单的M3U8文件来将其换为MP4文件。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [使用ffmpeg进行mp4m3u8之间换](https://blog.csdn.net/shamqu/article/details/114042996)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一恍过去

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值