Java生成m3u8文件

生成m3u8视频

maven依赖

	<dependency>
		<groupId>org.bytedeco</groupId>
		<artifactId>javacv</artifactId>
		<version>1.5.6</version>
	</dependency>
	<dependency>
		<groupId>org.bytedeco</groupId>
		<artifactId>ffmpeg-platform</artifactId>
		<version>4.4-1.5.6</version>
	</dependency>

import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.FFmpegLogCallback;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameRecorder;

import java.io.*;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * javacv ffmpeg 工具类
 */

public class FfmpegUtil {

    //
    public static void main(String[] args) throws Exception {
        String from = "C:/videos/video/8f1a0304-a874-4619-8598-4c65c0539b6a";

        mp4ToM3u8(from, "8f1a0304-a874-4619-8598-4c65c0539b6a.mp4");
    }


    /**
     * 通过Java代码生成m3u8分片视频  
     * 这是方法1,使用这种方法需要引入前面的pom依赖,使用命令分方式则不需要
     * @param sourceDir
     * @param fileName
     */
    public static void mp4ToM3u8(String sourceDir, String fileName) {
        try {


            avutil.av_log_set_level(avutil.AV_LOG_INFO);
            FFmpegLogCallback.set();

            boolean isStart = true;// 该变量建议设置为全局控制变量,用于控制录制结束
            //加载文件
            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(sourceDir + "/" + fileName + ".mp4");
            grabber.start();

            File tempFile3 = new File(sourceDir + "/m3u8", "output.m3u8");


            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(tempFile3, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
            //格式方式
            recorder.setFormat("hls");
            //关于hls_wrap的说明,hls_wrap表示重复覆盖之前ts切片,这是一个过时配置,ffmpeg官方推荐使用hls_list_size 和hls_flags delete_segments代替hls_wrap
            //设置单个ts切片的时间长度(以秒为单位)。默认值为2秒
  /*          recorder.setOption("hls_time", "60");
            //不根据gop间隔进行切片,强制使用hls_time时间进行切割ts分片
            recorder.setOption("hls_flags", "split_by_time");

            //设置播放列表条目的最大数量。如果设置为0,则列表文件将包含所有片段,默认值为5
            // 当切片的时间不受控制时,切片数量太小,就会有卡顿的现象
            recorder.setOption("hls_list_size", "0");
            //自动删除切片,如果切片数量大于hls_list_size的数量,则会开始自动删除之前的ts切片,只保留hls_list_size个数量的切片
            recorder.setOption("hls_flags", "delete_segments");
            //ts切片自动删除阈值,默认值为1,表示早于hls_list_size+1的切片将被删除
            recorder.setOption("hls_delete_threshold", "1");
            recorder.setOption("hls_segment_type", "mpegts");
            recorder.setOption("hls_segment_filename", sourceDir + File.separator + "m3u8" + "-%5d.ts");


            recorder.setVideoOption("tune", "fastdecode");
            // 快速
            recorder.setVideoOption("preset", "ultrafast");
            recorder.setVideoOption("threads", "12");
            recorder.setVideoOption("vsync", "2");
            recorder.setFrameRate(grabber.getFrameRate());// 设置帧率
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);*/


            recorder.setOption("start_number", "0");
            recorder.setOption("hls_time", "50");
            recorder.setOption("hls_list_size", "0");
            recorder.setOption("hls_segment_filename", sourceDir + "/m3u8" + File.separator + "m3u8" + "-%5d.ts");


            //  ffmpeg -i 8f1a0304-a874-4619-8598-4c65c0539b6a.mp4 -codec: copy -start_number 0 -hls_time 50 -hls_list_size 0 -f hls output.m3u8

            recorder.start(grabber.getFormatContext());
            AVPacket packet;
            while ((packet = grabber.grabPacket()) != null) {
                try {
                    recorder.recordPacket(packet);
                } catch (FrameRecorder.Exception e) {

                }
            }
            recorder.setTimestamp(grabber.getTimestamp());
            recorder.stop();
            recorder.release();
            grabber.stop();
            grabber.release();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static void mp4ToM3u8(String source, String to, String fileName) {

        try {


            avutil.av_log_set_level(avutil.AV_LOG_INFO);
            FFmpegLogCallback.set();

            boolean isStart = true;// 该变量建议设置为全局控制变量,用于控制录制结束
            //加载文件
            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(source);
            grabber.start();

            File tempFile3 = new File(to, fileName + ".m3u8");


            System.out.println("--------------------");
            System.out.println(tempFile3);
            System.out.println("--------------------===========");

            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(tempFile3, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
            //格式方式
            recorder.setFormat("hls");
            //关于hls_wrap的说明,hls_wrap表示重复覆盖之前ts切片,这是一个过时配置,ffmpeg官方推荐使用hls_list_size 和hls_flags delete_segments代替hls_wrap
            //设置单个ts切片的时间长度(以秒为单位)。默认值为2秒
            recorder.setOption("hls_time", "60");
            //不根据gop间隔进行切片,强制使用hls_time时间进行切割ts分片
            recorder.setOption("hls_flags", "split_by_time");

            //设置播放列表条目的最大数量。如果设置为0,则列表文件将包含所有片段,默认值为5
            // 当切片的时间不受控制时,切片数量太小,就会有卡顿的现象
            recorder.setOption("hls_list_size", "0");
            //自动删除切片,如果切片数量大于hls_list_size的数量,则会开始自动删除之前的ts切片,只保留hls_list_size个数量的切片
            recorder.setOption("hls_flags", "delete_segments");
            //ts切片自动删除阈值,默认值为1,表示早于hls_list_size+1的切片将被删除
            recorder.setOption("hls_delete_threshold", "1");
            /*hls的切片类型:
             * 'mpegts':以MPEG-2传输流格式输出ts切片文件,可以与所有HLS版本兼容。
             * 'fmp4':以Fragmented MP4(简称:fmp4)格式输出切片文件,类似于MPEG-DASH,fmp4文件可用于HLS version 7和更高版本。
             */
            recorder.setOption("hls_segment_type", "mpegts");
            //指定ts切片生成名称规则,按数字序号生成切片,例如'file%03d.ts',就会生成file000.ts,file001.ts,file002.ts等切片文件
            //recorder.setOption("hls_segment_filename", toFilePath + "-%03d.ts");
            recorder.setOption("hls_segment_filename", to + File.separator + fileName + "-%5d.ts");


            recorder.setVideoOption("tune", "fastdecode");
            // 快速
            recorder.setVideoOption("preset", "ultrafast");
//        recorder.setVideoOption("crf", "26");
            recorder.setVideoOption("threads", "12");
            recorder.setVideoOption("vsync", "2");
            recorder.setFrameRate(grabber.getFrameRate());// 设置帧率
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);


            recorder.start(grabber.getFormatContext());
            AVPacket packet;
            while ((packet = grabber.grabPacket()) != null) {
                try {
                    recorder.recordPacket(packet);
                } catch (FrameRecorder.Exception e) {

                }
            }
            recorder.setTimestamp(grabber.getTimestamp());
            recorder.stop();
            recorder.release();
            grabber.stop();
            grabber.release();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 通过命令的方式生成分片视频   
     * 使用这种方式不需要引入pom依赖
     * @param sourcePath 要分割的视频完整路径(包含文件名)
     *                   例:C:/video/test/liuLangDiQiu.mp4
     * @param targetPath 生成m3u8的视频地址(包含文件名)
     *                   例:C:/video/test/m3u8/output.m3u8
     * 参考文档 https://blog.youkuaiyun.com/hantanxin/article/details/103957871/
     */
    public static void mp4ToM3u8ByCommand(String sourcePath, String targetPath) {

        List<String> command = new ArrayList<>();
        command.add("ffmpeg");
        command.add("-i");
        command.add(sourcePath);
        command.add("-codec:");
        command.add("copy");
        command.add("-start_number");
        command.add("0");
        command.add("-hls_time");
        command.add("300"); //每个切片视频的时长(秒)  这里设置一个视频切成五分钟一小片,如果视频小于五分钟的话,则相当于不切分,只有一片
        command.add("-hls_list_size");
        command.add("0");
        command.add("-f");
        command.add("hls");
        command.add(targetPath);

        StringBuilder stringBuilder = new StringBuilder();
        ProcessBuilder builder = new ProcessBuilder(command);

        try {
            Process process = builder.start();
            final InputStream is1 = process.getInputStream();
            new Thread(new Runnable() {
                public void run() {
                    BufferedReader bufferedReader = null;
                    String line = null;
                    try {
                        bufferedReader = new BufferedReader(
                                new InputStreamReader(is1, "GBK"));
                        while ((line = bufferedReader.readLine()) != null) {
                            stringBuilder.append(line + "\n");
                        }
                        is1.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start(); // 启动单独的线程来清空p.getInputStream()的缓冲区
            InputStream is2 = process.getErrorStream();
            BufferedReader br2 = new BufferedReader(new InputStreamReader(is2));
            StringBuilder buf = new StringBuilder(); // 保存输出结果流
            String line2 = null;
            while ((line2 = br2.readLine()) != null) buf.append(line2); //
            System.out.println("----res:----" + stringBuilder + "&" + buf);
            System.out.println(stringBuilder + "&" + buf);
            System.out.println("ok");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.toString());
        }
    }
}

nginx配置

        server {
        listen 8701;
        server_name localhost;  # 替换为您的域名或IP地址
        location / {
            root C:/zh/hls;  # 替换为您的视频文件所在的目录,hls目录下就是 *.m3u8所在的地址,如果不行,前端报跨域,则可以给m3u8文件再套一层文件夹
            add_header Cache-Control no-cache;
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Credentials' 'true';
            types {
                application/vnd.apple.mpegurl m3u8;
                video/mp2t ts;
            }
        }
    }
### 方法一:使用 M3u8ToMp4 工具 可以通过专门设计的工具 `M3u8ToMp4` 来完成此操作。该工具能够处理带密钥(key)的 m3u8 文件,并将其转换为 mp4 格式[^1]。 #### 环境准备 - **JRE 1.8**:确保已安装 Java Runtime Environment (JRE) 版本 1.8 并正确配置环境变量。 #### 使用步骤说明 - 准备两个空文件夹 `input` 和 `output`,并将下载的 m3u8 文件及其关联的文件夹放入 `input` 中。 - 双击运行 `M3u8ToMp4.exe` 执行脚本。 - 转换完成后,在 `output` 文件夹中找到生成的 mp4 文件。 --- ### 方法二:基于 Python 的 FFmpeg 实现 另一种方法是借助 Python 编写脚本来调用 FFmpeg 完成转换任务。以下是具体实现代码: ```python import subprocess def convert_to_mp4(input_file, output_file): command = ['ffmpeg', '-i', input_file, '-c', 'copy', output_file] try: result = subprocess.run(command, check=True) if result.returncode == 0: print("转换成功!") except FileNotFoundError: print("未安装FFmpeg或者路径设置不正确。请先安装FFmpeg并添加到系统的PATH中。") # 示例调用 convert_to_mp4('E:\\example\\file.m3u8', 'E:\\example\\file.mp4') ``` 这段代码依赖于 FFmpeg 命令行工具,因此需提前确认 FFmpeg 是否已正确安装并加入 PATH 环境变量[^2]。 --- ### 方法三:直接使用 FFmpeg 命令行 对于熟悉命令行操作的用户来说,可以直接通过 FFmpeg 提供的功能将远程或本地的 m3u8 文件转换为 mp4 格式[^3]。 #### 基础语法 ```bash ffmpeg -i 输入文件地址 -c copy 输出文件地址 ``` #### 示例 假设输入文件位于网络地址,则可以如下操作: ```bash ffmpeg -i https://pull-hs1.vzan.com/295539/007845640599847546/replay.1711435561.51287065.m3u8 -c copy output.mp4 ``` 如果输入的是本地文件,只需替换 URL 地址为实际路径即可[^4]。 --- ### 注意事项 1. 如果 m3u8 文件涉及加密(如 AES-128),则需要提供相应的密钥文件才能顺利完成解码过程。 2. 对于某些特殊场景下的 m3u8 文件,可能还需要额外参数支持,比如 `-protocol_whitelist file,http,https,tcp,tls,crypto` 或者指定密钥位置等。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值