在线学习解决方案

视频点播解决方案
流媒体的概念:
	将视频文件分成许多小的视频文件片段,当用户请求视频资源的时候,将视频文件片段包通过网络传输发送过去,从而实现一边传输数据包一边播放视频

流式传输:
1.顺序流式传输
	即顺序下载音、视频文件,可以实现边下载边播放,不过,用户只能观看已下载的视频内容,无法快进到未
下载的视频部分,顺序流式传输可以使用Http服务器来实现,比如Nginx、Apache等。
2.实时流式传输
	实时流式传输可以解决顺序流式传输无法快进的问题,它与Http流式传输不同,它必须使用流媒体服务器并
且使用流媒体协议来传输视频,它比Http流式传输复杂。常见的实时流式传输协议有RTSP、RTMP、RSVP等。

直播使用rtmp协议:使用rtmp协议需要架设媒体服务器,造价高

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

浏览器播放器:
	H5播放器(Video.js),flash播放器

断点续传解决方案:
1、上传前先把文件分成块
2、一块一块的上传,上传中断后重新上传,已上传的分块则不用再上传
3、各分块上传完成最后合并文件

使用百度提供的WebUpload实现前端断点续传,网址 http://fexteam.gz01.bdysite.com/webuploader/

视频点播解决方案整体流程:
	1.教学管理系统使用WebUpload前端,采用断点续传的方式上传文件
	2.服务器端先得到文件片段,再合并为一个完整的文件
	3.服务器端再使用FFmpeg将这个文件编码设置参数并拆分为新的.ts结尾的文件片段,并在.m3u8中保存视频的索引,并将.ts文件和.m3u8文件存储到文件服务器,并部署到nginx服务器下
	4.用户点播视频,首先会将.m3u8文件下载下来,再根据.m3u8文件中的索引排序下载相应的.ts文件,从而达到快进快退的效果
	5.浏览器播放视频,默认使用h5播放器,如果不支持h5就换位flash播放器,如果还不支持就提示用户更换浏览器
流媒体的原理代码:
//文件分片
@Test
public void testThunk(){
    //获取目标文件
    File targetFile = new File("h:/test.avi");
    if(!targetFile.isFile()){
        throw new RuntimeException();
    }
    //指定分片存储目录
    File thunkDir = new File("h:/thunkDir/");
    if(!thunkDir.exists()){
        thunkDir.mkdirs();
    }
    //设置分片文件的大小1M,缓冲区1KB
    int fileSize = 1 * 1024 *1024;
    byte[] buff = new byte[1024];
    //计算分片的结果个数,向上取整
    int count = (int)Math.ceil( targetFile.length() * 1.0 / fileSize );
    //读取目标文件,"r"代表只读权限
    RandomAccessFile random_read = new RandomAccessFile(targetFile,"r");
    //开始对目标文件分片处理
    int len;
    for(int i = 1; i <= count ; i++ ){
        File thunkFile = new File(thunkDir,i + "");
        thunkFile.createNewFile();	//注意,需要新建一个分片文件
        //输出流,"rw"代表读写权限
        RandomAccessFile random_write = new RandomAccessFile(thunkFile,"rw");
        while((len = random_read.read(buff)) != -1){
            random_write.write(buff,0,len);
            if(thunkFile.length() >= fileSize){
                break;
            }
        }
       //释放本次输出流资源
        random_write.close();      
    }
    //释放输入流
    random_read.close();
}


//合并文件
@Test
public void testMerge(){
    //获取分片文件目录
    File thunkDir = new File("h:/thunkDir/");
    if(!thunkDir.isDirectory()){
        throw new RuntimeException();
    }
    //创建合并文件
    File mergeFile = new File("h:/mergeFile.avi");
    if(mergeFile.exists()){
        mergeFile.delete();
    }
    mergeFile.createNewFile();
    //创建输出流
    RandomAccessFile random_write = new RandomAccessFile(mergeFile,"rw");
    byte[] buff = new byte[1024];
    int len;
    //打开分片文件目录
    File[] files = thunkDir.listFiles();
    //files中的文件可能是乱序的,重新排序,升序
    List<File> fileList = Arrays.asList(files);
    Collections.sort(fileList,new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                return Integer.parseInt(o1.getName()) - Integer.parseInt(o2.getName());
            }}
        );
     for(File file : fileList){
  		RandomAccessFile random_read = new RandomAccessFile(file,"r");
         while((len = random_read.read(buff))!=-1){
             random_write.write(buff,0,len);
         }
         random_read.close();
     }    
     random_write.close();
}
=WebUpload============
before-send-file
before-send-file
	文件开始上传前前端请求服务端准备上传工作
    public ResponseResult register(String fileMd5,
                                   String fileName,
                                   Long fileSize,
                                   String mimetype,
                                   String fileExt);
1.根据fileMd5 + fileExt 找到文件目录下是否已经存在了这个文件
2.根据fileMd5从MongoDB中查询是否已经存在文件信息
3.如果两者都为true,则认为文件已经存在,抛出异常
4.创建存储数据片段的目录
before-send
before-send
	上传分块前前端请求服务端校验分块是否存在。
//上传数据片段前预检查
 public CheckChunkResult checkChunk(String fileMd5,
                                       Integer chunk,
                                       Integer chunkSize);
1.根据fileMd5 + chunk从数据片段目录从判断文件是否已经存在
2.如果存在,判断数据是否完整,不完整就删除

//开始上传数据片段
   public ResponseResult uploadChunk(MultipartFile file,
                                      Integer chunk,
                                      String fileMd5);
1.先判断上传的文件片段是否为空,空就抛异常
2.以chunk为文件名,在数据片段目录下创建文件
3.通过file.getInputStream()得到输入流,然后写入到新建的文件中
after-send-file
after-send-file	
	在所有分块上传完成后触发,可以请求服务端合并分块文件
    public ResponseResult mergeChunk(String fileMd5,
                                     String fileName,
                                     Long fileSize,
                                     String mimetype,
                                     String fileExt);
1.根据fileMd5得到数据片段文件目录下的所有的thunk文件
2.对chunk文件进行排序,输出到合并文件中
3.根据DigestUtils.md5DigestAsHex(new FileInputStream(file))得到上传文件的MD5编码,跟fileMd5比较,true表示合并文件跟原始文件一致

猜你喜欢

转载自blog.csdn.net/qq_42514129/article/details/85109504