Java利用ffmpeg实现视频MP4转m3u8
作者:Sunshine_Moon
前言
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
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的资料请关注脚本之家其它相关文章!