java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java ffmpeg实现MP4转m3u8

Java利用ffmpeg实现视频MP4转m3u8

作者:Sunshine_Moon

本文综合了下网上教程,从ffmpeg工具转码,ffmpeg视频播放,java语言操控ffmpeg转码,转码后视频上传阿里云oss,四个方面完整记录下这个流程,需要的朋友可以参考下

前言

ffmpeg工具实现视频转码网上有很多教程,但大多不够具体。本博客综合了下网上教程,从ffmpeg工具转码,ffmpeg视频播放,java语言操控ffmpeg转码,转码后视频上传阿里云oss,四个方面完整记录下这个流程,内容是基于我项目中的需求而定,不能使用所有情况,仅供参考。

具体技术原理不做描述,如有兴趣可自行研究。

(一)ffmpeg工具转码

1.如何安装ffmpeg工具

官网下载地址: https://ffmpeg.zeranoe.com/builds/
我是用的ffmpeg是windows版本,linux自行研究

在这里插入图片描述

下载完成后解压压缩包,完成后bin目录下ffmpeg.exe文件是之后程序启动时需要使用的

在这里插入图片描述

配置环境变量,至此ffmpeg工具安装到此结束

在这里插入图片描述

2.如何使用ffmpeg工具进行视频转码

打开cmd黑窗口,输入以下指令
ffmpeg -i xxxxxxx.mp4 -c:v libx264 -hls_time 60 -hls_list_size 0 -c:a aac -strict -2 -f hls xxxxxxx.m3u8

地址既可以写相对地址 ,也可以写绝对地址,看你自己情况

这条指令参数具体含义自行百度,这里只介绍几个重要的

参数解析:

-re :该参数表示ffmpeg将会按照当前视频的播放速率进行转码,这样就不会说切片的速度和播放速度不一致。不加这个参数,切片速度会非常快,客户端还来不及播放,列表已经被更新了。

-hls_time n :设置每片的长度,默认值为2,单位为秒。

-hls_list_size n :设置m3u8文件播放列表保存的最多条目,设置为0会保存有所片信息,默认值为5。一般用于直播流,点播文件可以设置成0,即全部保存。

-hls_wrap n :设置多少片之后开始覆盖,设置为0则不会覆盖,默认值为0。这个选项能够避免在磁盘上存储过多的片,而且能够限制写入磁盘的最多的片的数量。

以上参数可以自己尝试调整看看效果。

这是成功执行命令后,ffmpeg执行过程,出现这个界面没报错,就恭喜你成功了,安静等待工具切片就行了

在这里插入图片描述

工具执行完毕之后,输出路径文件夹中会多出一个m3u8文件和若干ts文件,至此第一部分圆满成功

在这里插入图片描述

(二)播放m3u8文件

1.video.js

Video.js 是一个通用的在网页上嵌入视频播放器的 JS 库,Video.js 自动检测浏览器对 HTML5 的支持情况,如果不支持 HTML5 则自动使用 Flash 播放器。(要支持ie低版本请下载5.4.3版 )

官网地址: https://videojs.com

API地址:https://docs.videojs.com/index.html

2.具体使用

1)静态数据

<!DOCTYPE html>
<html>

	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
		<meta http-equiv="X-UA-COMPATIBLE" CONTENT="IE=edge,chrome=1">
		<link href="css/style.css" rel="external nofollow"  rel="stylesheet" />
		<link href="js/video-js.css" rel="external nofollow"  rel="stylesheet">

		<!-- If you'd like to support IE8 (for Video.js versions prior to v7) -->
		<script src="js/videojs-ie8.min.js"></script>
	</head>

	<body>
		<video id="myVideo" class="video-js vjs-default-skin vjs-big-play-centered">
			<source type="application/x-mpegURL"></source>
		</video>

	</body>

	<script type="text/javascript" src="js/jquery-3.3.1.js"></script>
	<script src='js/video.js'></script>
	<script>
		videojs(document.querySelector('.video-js'), {
			controls: true,
			autoplay: false,
			preload: 'auto',
			sources: [{
				src: "video/20191010173819_cjOaOJ.mp4",
				type: "application/x-mpegURL"
			}]
		});
	</script>

</html>

2)动态数据

动态数据赋值有个src方法,但是我使用的时候容易报错,用的vue框架,且视频地址是oss地址。

就想了个折中的方法,每次url变化的时候销毁原始video对象,重新初始化。

最近还在研究,如果有好方法会再更新上来。如有解决方案也可以留言我。

videojs(document.querySelector('.video-js'),{}).ready(function(){    
	var myPlayer = this;
 
    myPlayer.src("http://www.example.com/path/to/video.mp4");
 
    myPlayer.play();
});

需要注意,如果是在Vue里使用,建议用ref获取元素,我这里标出初始化播放器方法代码

在这里插入图片描述

initPalyer(){
     const videoDom = this.$refs.myVideo;   //不能用document 获取节点
     let myPlayer = videojs( videoDom, {
         controls: true,
         autoplay: false,
         preload: 'auto',
         sources: [{
             src: vm.yfVideoDetail.videoUrl,
             type: "application/x-mpegURL"
         }]
     });
 }

Api上有着详细的使用方法的介绍,我这里因为只用到这些所以就只写了这部分代码,如果想进一步深入,可以自行研究。

更新播放方法:

const vue = new Vue({
	el: ".body",
	data: {
		myPlayer: null
	},
	methods: {
		initPalyer() {
			const videoDom = this.$refs.myVideo; //不能用document 获取节点
			
			if(vurl) {
				vue.myPlayer = videojs(videoDom, {
					controls: true,
					autoplay: false,
					preload: 'auto',
					sources: [{
						src: vurl,  //视频url
						type: "application/x-mpegURL"
					}]
				});
			}
		},
		//切换视频
		switchVideo(item) {
			vue.myPlayer.reset(); //重置 video
			vue.myPlayer.src([{
				src: item.videoUrl  //新视频url
			}, ]);
			vue.myPlayer.load(item.videoUrl);
			vue.myPlayer.currentTime(0);
		}
	}
})

(三)Java程序上传本地视频地址并通过ffmpeg工具转成m3u8文件

实现前提:笔者用的是springboot框架,,前端上传视频用的是elementui里的上传组件.

以下代码是根据网上已有的源码结合自身需求做了改变,仅供参考,请勿直接copy,直接copy报错了不要怪我哦。需要根据自身需求进行改变。

后台实现上传代码:

public R saveVideo(@RequestParam  MultipartFile file) {
		// TODO Auto-generated method stub
		String url=null;
		try {
			String f = file.getOriginalFilename(); //获取文件名
			String suffix = StringUtils.substringAfter(f, "."); //获取文件后缀
			String filename = FileUtils.getFileName(null, null);  //我自己封装的方法,给文件重新起个名字,文件名不带后缀

			/*********本地上传(Tomcat配置映射C:/upload/file)*********/
			//先将文件本地上传后,调用ffmpeg切片转成m3u8,在将转换后文件上传到oss上去
			/*
				我的思路就是根据我重新起的名字,生成对应文件夹,将mp4视频和转换后的m3u8以及ts放在一起,然后遍历文件目录,将文件上传后,删除本地文件夹和文件
			 */
			String folderUrl = FileUtils.localPath+filename; //文件夹路径
			String fileName = filename+"."+suffix;   //文件名带后缀
			String uploadPath= folderUrl + "/" + fileName;  //上传后路径
			File fileFolder = new File(folderUrl);
			if (!fileFolder.exists()) {
				fileFolder.mkdirs();
			}
			File newFile = new File(uploadPath);
			file.transferTo(newFile);
			//mp4转m3u8
			boolean b = convertM3U8.convertOss(folderUrl + "/", fileName);
			if (!b){
				return R.error("上传失败!系统转码异常!");
			}
			//访问本地上传文件夹所有文件,依次上传至oss服务器
			File[] files = fileFolder.listFiles();
			if (null == files || files.length == 0) {
				return null;
			}
			boolean flag = true;
			for (int i = 0; i < files.length ; i++) {
				if (!files[i].isDirectory()) {
					//上传
					String name = files[i].getName();
					String suf = StringUtils.substringAfter(name, ".");
					String pre = StringUtils.substringBefore(name, ".");
					FileInputStream fis = new FileInputStream(files[i]);
					if ("m3u8".equals(suf)){
						if (flag && filename.equals(pre)){
							//这是封装的上传阿里云oss的方法
							url = OSSFactory.build().upload(fis, "video/" + filename + "/" + name);
							flag = false;
						}
					} else if ("ts".equals(suf)){
						OSSFactory.build().uploadPublic(fis, "video/" + filename + "/" + name);
					}
					fis.close();
					FileUtils.deletefile(files[i]);
				}
			}
			//删除文件夹
			fileFolder.delete();
			/*********本地上传(Tomcat配置映射C:/upload/file)*********/

		} catch (Exception e) {
			e.printStackTrace();
			R.error("上传异常");
		}
		return R.ok().put("data",url);
	}

ffmpeg视频转码工具类:

/**
 *  mp4转换m3u8工具类
 */
@Component
public class ConvertM3U8 {
    @Value("${m3u8.ffmpegpath}")
    private String ffmpegpath; // ffmpeg.exe的目录

    public boolean convertOss(String folderUrl,String fileName){
        if (!checkfile(folderUrl + fileName)){
            System.out.println("文件不存在!");
            return false;
        }

        //验证文件后缀
        String suffix = StringUtils.substringAfter(fileName, ".");
        String fileFullName = StringUtils.substringBefore(fileName, ".");
        if (!validFileType(suffix)){
            return false;
        }

        return  processM3U8(folderUrl,fileName,fileFullName);
    }

    /**
     * 验证上传文件后缀
     * @param type
     * @return
     */
    private boolean validFileType ( String type ) {
        if ("mp4".equals(type)){
            return true;
        }
        return false;
    }

    /**
     * 验证是否是文件格式
     * @param path
     * @return
     */
    private boolean checkfile(String path) {
        File file = new File(path);
        if (!file.isFile()) {
            return false;
        } else {
            return true;
        }
    }

    // ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)

    /**
     * ffmpeg程序转换m3u8
     * @param folderUrl
     * @param fileName
     * @param fileFullName
     * @return
     */
    private boolean processM3U8(String folderUrl,String fileName, String fileFullName) {
    	//这里就写入执行语句就可以了
        List commend = new java.util.ArrayList();
        commend.add(ffmpegpath);
        commend.add("-i");
        commend.add(folderUrl+fileName);
        commend.add("-c:v");
        commend.add("libx264");
        commend.add("-hls_time");
        commend.add("20");
        commend.add("-hls_list_size");
        commend.add("0");
        commend.add("-c:a");
        commend.add("aac");
        commend.add("-strict");
        commend.add("-2");
        commend.add("-f");
        commend.add("hls");
        commend.add(folderUrl+ fileFullName +".m3u8");
        try {
            ProcessBuilder builder = new ProcessBuilder();//java
            builder.command(commend);
            Process p = builder.start();
            int i = doWaitFor(p);
            System.out.println("------>"+i);
            p.destroy();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 监听ffmpeg运行过程
     * @param p
     * @return
     */
    public int doWaitFor(Process p) {
        InputStream in = null;
        InputStream err = null;
        int exitValue = -1; // returned to caller when p is finished
        try {
            System.out.println("comeing");
            in = p.getInputStream();
            err = p.getErrorStream();
            boolean finished = false; // Set to true when p is finished

            while (!finished) {
                try {
                    while (in.available() > 0) {
                        Character c = new Character((char) in.read());
                        System.out.print(c);
                    }
                    while (err.available() > 0) {
                        Character c = new Character((char) err.read());
                        System.out.print(c);
                    }

                    exitValue = p.exitValue();
                    finished = true;

                } catch (IllegalThreadStateException e) {
                    Thread.currentThread().sleep(500);
                }
            }
        } catch (Exception e) {
            System.err.println("doWaitFor();: unexpected exception - "
                    + e.getMessage());
        } finally {
            try {
                if (in != null) {
                    in.close();
                }

            } catch (IOException e) {
                System.out.println(e.getMessage());
            }
            if (err != null) {
                try {
                    err.close();
                } catch (IOException e) {
                    System.out.println(e.getMessage());
                }
            }
        }
        return exitValue;
    }

}

(四)上传m3u8文件至OSS需要注意的问题

1.同一个视频的m3u8格式文件以及ts文件需要放在同一级目录。最好是一个视频的相关文件放入以文件名命名的路径下以示区别

在这里插入图片描述

2.m3u8链接无法播放,video.js提示无法访问对应资源,需要在阿里云oss上设置跨域。bucket空间—>基础设置—>跨域设置

在这里插入图片描述

在这里插入图片描述

3.ts文件的读写权限需要公共读不然video.js访问不到的,然后为了防止视频被恶意下载可以设置防盗链

总结

以上就是Java利用ffmpeg实现视频MP4转m3u8的详细内容,更多关于Java ffmpeg实现MP4转m3u8的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文