欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java利用ffmpeg實(shí)現(xiàn)視頻MP4轉(zhuǎn)m3u8

 更新時(shí)間:2024年02月04日 10:18:26   作者:Sunshine_Moon  
本文綜合了下網(wǎng)上教程,從ffmpeg工具轉(zhuǎn)碼,ffmpeg視頻播放,java語(yǔ)言操控ffmpeg轉(zhuǎn)碼,轉(zhuǎn)碼后視頻上傳阿里云oss,四個(gè)方面完整記錄下這個(gè)流程,需要的朋友可以參考下

前言

ffmpeg工具實(shí)現(xiàn)視頻轉(zhuǎn)碼網(wǎng)上有很多教程,但大多不夠具體。本博客綜合了下網(wǎng)上教程,從ffmpeg工具轉(zhuǎn)碼,ffmpeg視頻播放,java語(yǔ)言操控ffmpeg轉(zhuǎn)碼,轉(zhuǎn)碼后視頻上傳阿里云oss,四個(gè)方面完整記錄下這個(gè)流程,內(nèi)容是基于我項(xiàng)目中的需求而定,不能使用所有情況,僅供參考。

具體技術(shù)原理不做描述,如有興趣可自行研究。

(一)ffmpeg工具轉(zhuǎn)碼

1.如何安裝ffmpeg工具

官網(wǎng)下載地址: https://ffmpeg.zeranoe.com/builds/
我是用的ffmpeg是windows版本,linux自行研究

在這里插入圖片描述

下載完成后解壓壓縮包,完成后bin目錄下ffmpeg.exe文件是之后程序啟動(dòng)時(shí)需要使用的

在這里插入圖片描述

配置環(huán)境變量,至此ffmpeg工具安裝到此結(jié)束

在這里插入圖片描述

2.如何使用ffmpeg工具進(jìn)行視頻轉(zhuǎn)碼

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

地址既可以寫相對(duì)地址 ,也可以寫絕對(duì)地址,看你自己情況

這條指令參數(shù)具體含義自行百度,這里只介紹幾個(gè)重要的

參數(shù)解析:

-re :該參數(shù)表示ffmpeg將會(huì)按照當(dāng)前視頻的播放速率進(jìn)行轉(zhuǎn)碼,這樣就不會(huì)說(shuō)切片的速度和播放速度不一致。不加這個(gè)參數(shù),切片速度會(huì)非???,客戶端還來(lái)不及播放,列表已經(jīng)被更新了。

-hls_time n :設(shè)置每片的長(zhǎng)度,默認(rèn)值為2,單位為秒。

-hls_list_size n :設(shè)置m3u8文件播放列表保存的最多條目,設(shè)置為0會(huì)保存有所片信息,默認(rèn)值為5。一般用于直播流,點(diǎn)播文件可以設(shè)置成0,即全部保存。

-hls_wrap n :設(shè)置多少片之后開始覆蓋,設(shè)置為0則不會(huì)覆蓋,默認(rèn)值為0。這個(gè)選項(xiàng)能夠避免在磁盤上存儲(chǔ)過(guò)多的片,而且能夠限制寫入磁盤的最多的片的數(shù)量。

以上參數(shù)可以自己嘗試調(diào)整看看效果。

這是成功執(zhí)行命令后,ffmpeg執(zhí)行過(guò)程,出現(xiàn)這個(gè)界面沒(méi)報(bào)錯(cuò),就恭喜你成功了,安靜等待工具切片就行了

在這里插入圖片描述

工具執(zhí)行完畢之后,輸出路徑文件夾中會(huì)多出一個(gè)m3u8文件和若干ts文件,至此第一部分圓滿成功

在這里插入圖片描述

(二)播放m3u8文件

1.video.js

Video.js 是一個(gè)通用的在網(wǎng)頁(yè)上嵌入視頻播放器的 JS 庫(kù),Video.js 自動(dòng)檢測(cè)瀏覽器對(duì) HTML5 的支持情況,如果不支持 HTML5 則自動(dòng)使用 Flash 播放器。(要支持ie低版本請(qǐng)下載5.4.3版 )

官網(wǎng)地址: https://videojs.com

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

2.具體使用

1)靜態(tài)數(shù)據(jù)

<!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)動(dòng)態(tài)數(shù)據(jù)

動(dòng)態(tài)數(shù)據(jù)賦值有個(gè)src方法,但是我使用的時(shí)候容易報(bào)錯(cuò),用的vue框架,且視頻地址是oss地址。

就想了個(gè)折中的方法,每次url變化的時(shí)候銷毀原始video對(duì)象,重新初始化。

最近還在研究,如果有好方法會(huì)再更新上來(lái)。如有解決方案也可以留言我。

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

需要注意,如果是在Vue里使用,建議用ref獲取元素,我這里標(biāo)出初始化播放器方法代碼

在這里插入圖片描述

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

Api上有著詳細(xì)的使用方法的介紹,我這里因?yàn)橹挥玫竭@些所以就只寫了這部分代碼,如果想進(jìn)一步深入,可以自行研究。

更新播放方法:

const vue = new Vue({
	el: ".body",
	data: {
		myPlayer: null
	},
	methods: {
		initPalyer() {
			const videoDom = this.$refs.myVideo; //不能用document 獲取節(jié)點(diǎn)
			
			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程序上傳本地視頻地址并通過(guò)ffmpeg工具轉(zhuǎn)成m3u8文件

實(shí)現(xiàn)前提:筆者用的是springboot框架,,前端上傳視頻用的是elementui里的上傳組件.

以下代碼是根據(jù)網(wǎng)上已有的源碼結(jié)合自身需求做了改變,僅供參考,請(qǐng)勿直接copy,直接copy報(bào)錯(cuò)了不要怪我哦。需要根據(jù)自身需求進(jìn)行改變。

后臺(tái)實(shí)現(xiàn)上傳代碼:

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);  //我自己封裝的方法,給文件重新起個(gè)名字,文件名不帶后綴

			/*********本地上傳(Tomcat配置映射C:/upload/file)*********/
			//先將文件本地上傳后,調(diào)用ffmpeg切片轉(zhuǎn)成m3u8,在將轉(zhuǎn)換后文件上傳到oss上去
			/*
				我的思路就是根據(jù)我重新起的名字,生成對(duì)應(yīng)文件夾,將mp4視頻和轉(zhuǎn)換后的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轉(zhuǎn)m3u8
			boolean b = convertM3U8.convertOss(folderUrl + "/", fileName);
			if (!b){
				return R.error("上傳失敗!系統(tǒng)轉(zhuǎn)碼異常!");
			}
			//訪問(wèn)本地上傳文件夾所有文件,依次上傳至oss服務(wù)器
			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視頻轉(zhuǎn)碼工具類:

/**
 *  mp4轉(zhuǎn)換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;
        }

        //驗(yàn)證文件后綴
        String suffix = StringUtils.substringAfter(fileName, ".");
        String fileFullName = StringUtils.substringBefore(fileName, ".");
        if (!validFileType(suffix)){
            return false;
        }

        return  processM3U8(folderUrl,fileName,fileFullName);
    }

    /**
     * 驗(yàn)證上傳文件后綴
     * @param type
     * @return
     */
    private boolean validFileType ( String type ) {
        if ("mp4".equals(type)){
            return true;
        }
        return false;
    }

    /**
     * 驗(yàn)證是否是文件格式
     * @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程序轉(zhuǎn)換m3u8
     * @param folderUrl
     * @param fileName
     * @param fileFullName
     * @return
     */
    private boolean processM3U8(String folderUrl,String fileName, String fileFullName) {
    	//這里就寫入執(zhí)行語(yǔ)句就可以了
        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;
        }
    }

    /**
     * 監(jiān)聽(tīng)ffmpeg運(yùn)行過(guò)程
     * @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需要注意的問(wèn)題

1.同一個(gè)視頻的m3u8格式文件以及ts文件需要放在同一級(jí)目錄。最好是一個(gè)視頻的相關(guān)文件放入以文件名命名的路徑下以示區(qū)別

在這里插入圖片描述

2.m3u8鏈接無(wú)法播放,video.js提示無(wú)法訪問(wèn)對(duì)應(yīng)資源,需要在阿里云oss上設(shè)置跨域。bucket空間—>基礎(chǔ)設(shè)置—>跨域設(shè)置

在這里插入圖片描述

在這里插入圖片描述

3.ts文件的讀寫權(quán)限需要公共讀不然video.js訪問(wèn)不到的,然后為了防止視頻被惡意下載可以設(shè)置防盜鏈

總結(jié)

以上就是Java利用ffmpeg實(shí)現(xiàn)視頻MP4轉(zhuǎn)m3u8的詳細(xì)內(nèi)容,更多關(guān)于Java ffmpeg實(shí)現(xiàn)MP4轉(zhuǎn)m3u8的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論