HLS

实时流式传输协议有RTSP、RTMP、RSVP,需要架设媒体服务器,造价高,对于直播多采用此方案。播放器使用HLS协议连接http服务器(Nginx、Apache等)实现近实时流方式播放视频.HLS是动态码率自适应技术,协议规定:基于Http协议,视频封装格式为ts,视频的编码格式为H264,音频编码格式为MP3、AAC或者AC-3。

HLS的工作方式是:将视频拆分成若干ts格式的小文件,通过m3u8格式的索引文件对这些ts小文件建立索引。一般10秒一个ts文件,播放器连接m3u8文件播放,当快进时通过m3u8即可找到对应的索引文件,并去下载对应的ts文件,从而实现快进、快退以近实时 的方式播放视频

1.FFmpeg视频转码器

 下载地址:http://www.ffmpeg.org/download.html 

下载后解压,将解压到/bin目录的路径配置到环境变量path中,ffmpeg -version 检测是否配置成功

 ffmpeg将avi格式文件转换成MP4格式文件:
ffmpeg.exe ‐i  haha.avi ‐c:v libx264 ‐s 1280x720 ‐pix_fmt yuv420p ‐b:a 63k ‐b:v 753k ‐r 18 .\haha.mp4
-c:v 视频编码为x264 ,x264编码是H264的一种开源编码格式。
-s 设置分辨率
-pix_fmt yuv420p:设置像素采样方式,主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0,它的作用是根据采样方式来从码流中还原每个像素点的YUV(亮度信息与色彩信息)值。
-b 设置码率,-b:a和-b:v分别表示音频的码率和视频的码率,-b表示音频加视频的总码率。码率对一个视频质量有很大的作用。
-r:帧率,表示每秒更新图像画面的次数,通常大于24肉眼就没有连贯与停顿的感觉了。
将mp4生成m3u8
ffmpeg ‐i  haha.mp4   ‐hls_time 10 ‐hls_list_size 0 ‐hls_segment_filename ./hls/haha_%05d.ts ./hls/haha.m3u8
-hls_time 设置每片的长度,单位为秒
-hls_list_size n: 保存的分片的数量,设置为0表示保存所有分片
-hls_segment_filename :段文件的名称,%05d表示5位数字
文件大小(转成bit)/ 时长(秒)/1024 = kbps

2.Video.js是一款基于HTML5世界的网络视频播放器。

 

Video.js : https://github.com/videojs/video.js
videojs-contrib-hls : https://github.com/videojs/videojs-contrib-hls#installation
package.json增加如下配置,执行cnpm install
  "videojs-contrib-hls": "^5.14.1",
  "videojs-contrib-hls.js": "^3.2.0",
  "videojs-flash": "^2.1.0",
  "vue-video-player": "^4.0.6"
配置路由
import learning_video from '@/module/course/page/playing_crosstalk.vue';
export default [
{
path: '/playing/:crosstalkId/:chapter',
component: playing_crosstalk,
name: '观看相声视频',
hidden: false,
iconCls: 'el-icon-document'
}
]
页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta http‐equiv="content‐type" content="text/html; charset=utf‐8" />
    <title>视频播放</title>
    <link href="/plugins/videojs/video‐js.css" rel="stylesheet">
</head>
<body>
<video id=example‐video width=800 height=600 class="video‐js vjs‐default‐skin vjs‐big‐play‐
centered" controls poster="c:/video/add.jpg">
    <source
            src="c:/video/hls/lucene.m3u8"
            type="application/x‐mpegURL">
</video>
<input type="button" onClick="switchvideo()" value="switch"/>
<script src="/plugins/videojs/video.js"></script>
<script src="/plugins/videojs/videojs‐contrib‐hls.js"></script>
<script>
    var player = videojs('example‐video');
//切换视频    
    function switchvideo(){
        player.src({
            src: 'http://www.baidu.com/video/hls/lucene.m3u8',
            type: 'application/x‐mpegURL',
            withCredentials: true
        });
        player.play();
    }
</script>
</body>
</html>


3.WebUploader大文件上传

官网地址:http://fexteam.gz01.bdysite.com/webuploader/

    前端 WebUploader        后端
1.获取文件信息
2.检查文件是否存在,创建文件目录
文件是否上传,已上传则直接报错。
检查文件上传路径是否存在,不存在则创建。
3.将文件分块
4.检查分块是否存在
检查分块文件是否上传,已上传则返回true。
未上传则检查上传路径是否存在,不存在则创建。
5.上传分块
6.将文件保存到分块目录
将分块文件上传到指定的路径。
7.通知所有分块上传完毕
8.合并文件
将所有分块文件合并为一个文件。RandomAccessFile
校验文件DigestUtils.md5Hex(new FileInputStream(new File()))
在数据库记录文件信息。
<template>
<div><br/>
操作步骤:<br/>
1、点击“选择文件”,选择要上传的文件<br/>
2、点击“开始上传”,开始上传文件<br/>
3、如需重新上传请重复上边的步骤。<br/><br/>

<div id="uploader" class="wu-example">
<div class="btns" style="float:left;padding-right: 20px">
<div id="picker">选择文件</div>
</div>
<div id="ctlBtn" class="webuploader-pick" @click="upload()">开始上传</div>

</div>
<!--用来存放文件信息-->
<div id="thelist" class="uploader-list" >
<div v-if="uploadFile.id" :id='uploadFile.id'><span>{{uploadFile.name}}</span>&nbsp;<span class='percentage'>{{percentage}}%</span></div>

</div>
</div>
</template>
<script>
import $ from '../../../../static/plugins/jquery/dist/jquery.js'
import webuploader from '../../../../static/plugins/webuploader/dist/webuploader.js'
import '../../../../static/css/webuploader/webuploader.css'
export default{
data(){
return{
uploader:{},
uploadFile:{},
percentage:0,
fileMd5:''
}
},
methods:{
//开始上传
upload(){
if(this.uploadFile && this.uploadFile.id){
this.uploader.upload(this.uploadFile.id);
}else{
alert("请选择文件");
}
}
},
mounted(){
WebUploader.Uploader.register({
"before-send-file":"beforeSendFile",
"before-send":"beforeSend",
"after-send-file":"afterSendFile"
},{
beforeSendFile:function(file) {
var deferred = WebUploader.Deferred();// 创建一个deffered,用于通知是否完成操作
// 计算文件的唯一标识,用于断点续传
(new WebUploader.Uploader()).md5File(file, 0, 100*1024*1024) .then(function(val) {
this.fileMd5 = val;
this.uploadFile = file;
$.ajax({
type:"POST",
url:"/api/media/upload/register",
data:{
// 文件唯一表示
fileMd5:this.fileMd5,
fileName: file.name,
fileSize:file.size,
mimetype:file.type,
fileExt:file.ext
},
dataType:"json",
success:function(response) {
if(response.success) {
deferred.resolve();
} else {
deferred.reject();
}
}
});
}.bind(this));
return deferred.promise();
}.bind(this),
beforeSend:function(block) {
var deferred = WebUploader.Deferred();
// 每次上传分块前校验分块,如果已存在分块则不再上传,达到断点续传的目的
$.ajax({
type:"POST",
url:"/api/media/upload/checkchunk",
data:{
// 文件唯一表示
fileMd5:this.fileMd5,
// 当前分块下标
chunk:block.chunk,
// 当前分块大小
chunkSize:block.end-block.start
},
dataType:"json",
success:function(response) {
if(response.fileExist) {
// 分块存在,跳过该分块
deferred.reject();
} else {
// 分块不存在或不完整,重新发送
deferred.resolve();
}
}
});
//构建fileMd5参数,上传分块时带上fileMd5
this.uploader.options.formData.fileMd5 = this.fileMd5;
this.uploader.options.formData.chunk = block.chunk;
return deferred.promise();
}.bind(this),
afterSendFile:function(file) {
// 合并分块
$.ajax({
type:"POST",
url:"/api/media/upload/mergechunks",
data:{
fileMd5:this.fileMd5,
fileName: file.name,
fileSize:file.size,
mimetype:file.type,
fileExt:file.ext
},
success:function(response){
//在这里解析合并成功结果
if(response && response.success){
alert("上传成功")
}else{
alert("上传失败")
}
}
});
}.bind(this)
});
// 创建uploader对象,配置参数
this.uploader = WebUploader.create({
swf:"/static/plugins/webuploader/dist/Uploader.swf",//上传文件的flash文件,浏览器不支持h5时启动flash
server:"/api/media/upload/uploadchunk",//上传分块的服务端地址,注意跨域问题
fileVal:"file",//文件上传域的name
pick:"#picker",//指定选择文件的按钮容器
auto:false,//手动触发上传
disableGlobalDnd:true,//禁掉整个页面的拖拽功能
chunked:true,// 是否分块上传
chunkSize:1*1024*1024, // 分块大小(默认5M)
threads:3, // 开启多个线程(默认3个)
prepareNextFile:true// 允许在文件传输时提前把下一个文件准备好
});
// 将文件添加到队列
this.uploader.on("fileQueued", function(file) {
this.uploadFile = file;
this.percentage = 0;
}.bind(this));
//选择文件后触发
this.uploader.on("beforeFileQueued", function(file) {
//重置uploader
this.uploader.reset()
this.percentage = 0;
}.bind(this));

// 监控上传进度 percentage:代表上传文件的百分比
this.uploader.on("uploadProgress", function(file, percentage) {
this.percentage = Math.ceil(percentage * 100);
}.bind(this));
//上传失败触发
this.uploader.on("uploadError", function(file,reason) {
console.log(reason)
alert("上传文件失败");
});
//上传成功触发
this.uploader.on("uploadSuccess", function(file,response ) {
console.log(response)
//alert("上传文件成功!");
});
//每个分块上传请求后触发
this.uploader.on( 'uploadAccept', function( file, response ) {
if(!(response && response.success)){//分块上传失败,返回false
return false;
}
});
}
}
</script>
<style scoped>
</style>
根据文件md5得到文件路径规则:一级目录:md5的第一个字符;二级目录:md5的第二个字符;三级目录:md5;文件名:md5+文件扩展名
 
 

 

 

 

 

 

  

 

posted @ 2020-02-28 23:25  袋子里的袋鼠  阅读(1740)  评论(0)    收藏  举报