流媒体

一,什么是流媒体

我们这里采用录播第三种技术方案:

点播和直播两种方式,我们先调研点播的方案,如下:

1、 播放器通过 http协议从http服务器上下载视频文件进行播放

问题:必须等到视频下载完才可以播放,不支持快进到某个时间点进行播放

2、 播放器通过rtmp协议连接媒体服务器以实时流方式播放视频

使用rtmp协议需要架设媒体服务器,造价高,对于直播多采用此方案。

3、 播放器使用HLS协议连接http服务器(Nginx、Apache tomcat等)实现近实时流方式播放视频

HLS协议:将一个视频通过一个解码器(ffmeg)转换为一个编码格式为m3u8文件(装很多ts文件的索引信息)和若干个ts文件列表,用户只用下载M3u8文件,然后可以实现视频播放和跳转(快进快退)。

MP4,rmvb,avi是文件格式

H264是视频的编码格式

视频上传到专门的服务器上然后下载,流媒体就是将视频文件分成许多小块儿,将这些小块儿作为数据包通过网络发送出去,实现一边传输视频 数据 包一边观看视频

二,ffmeg转码方式

(一)使用ffmeg生成m3u8的步骤

先将avi转换为MP4

ffmpeg.exe -i  lucene.avi -c:v libx264 -s 1280x720 -pix_fmt yuv420p -b:a 63k -b:v 753k -r 18 .\lucene.mp4

(二)再将MP4生成M3u8

ffmpeg -i  lucene.mp4   -hls_time 10 -hls_list_size 0  -hls_segment_filename ./hls/lucene_%05d.ts ./hls/lucene.m3u8

生成了一个M3U8文件和ts文件类表。

三。视频播放器

技术选型:(选H5)

flash播放器:缺点是需要在客户机安装Adobe Flash Player播放器,优点是flash播放器已经很成熟了,并且浏览器对flash支持也很好。

H5播放器:基于h5自带video标签进行构建,优点是大部分浏览器支持H5,不用再安装第三方的flash播放器,并且随着前端技术的发展,h5技术会越来越成熟。

本项目采用H5播放器,使用Video.js开源播放器。

​ Video.js是一款基于HTML5世界的网络视频播放器。它支持HTML5和Flash视频,它支持在台式机和移动设备上播放视频。这个项目于2010年中开始,目前已在40万网站使用。

官方地址:http://videojs.com/

2搭建媒体服务器

nginx的config的代理转发:

www.XXX/video(其中的proxy代理路径)的,根据proxy的路径找到upstream(其中的127.0.0.1的90端口),再根据90端口找到本地的保存视频的文件夹。

四,项目中的运用

(一)总体框架

老师通过视频管理系统前端上传视频到媒资管理系统,媒资管理系统将媒资描述性信息保存在mongoDB中(比如文件名称,文件路径),同时上传到指定的视频服务器。

课程管理服务需要从媒资管理系统中检索到媒资信息,并完成媒资信息和课程计划的绑定,然后存在mysql中,

学生访问学习中心前端,点击课程计划三级目录中视频播放,请求学习服务,学习服务查询媒资信息,通过代理转发播放视频。

(二)断点续传

原理:将文件拆成若干个小文件,分块上传,然后全部分块上传完毕后,进行文件的合并。

(三)视频处理

视频处理进程的任务是接收视频处理消息进行视频处理,业务流程如下:

1、监听MQ,接收视频处理消息。

2、进行视频处理。

3、向数据库写入视频处理结果。

视频处理进程属于媒资管理系统的一部分,考虑提高系统的扩展性,将视频处理单独定义视频处理工程。

 

通过processbuilder调用执行外部命令。

MP4utils将avi转换MP4

hlsvideoutils生成M3u8和ts文件列表。

将相对应的信息保存在mongodb中

  1 @Component
  2 public class MediaProcessConsumer {
  3 
  4     @Autowired
  5     private MediaFileRepository mediaFileRepository;
  6 //xc-service-manage-media.ffmpeg-path
  7     @Value("${xc-service-manage-media.ffmpeg-path}")
  8     private String ffmpegPath;
  9 
 10     @Value("${xc-service-manage-media.video-location}")
 11     private String videoLocation;
 12 
 13 
 14     @RabbitListener(queues = {"${xc-service-manage-media.mq.queue-media-video-processor}"})
 15     public void receiveMediaMessage(String message){
 16         // message :  {"mediaId","4848rdgdfg"}
 17 
 18         //转换消息
 19         Map map = JSON.parseObject(message, Map.class);
 20         String mediaId = (String) map.get("mediaId");
 21 
 22         //查询mediafile对象信息
 23         Optional<MediaFile> mediaFileOptional = mediaFileRepository.findById(mediaId);
 24         if (!mediaFileOptional.isPresent()){
 25             return;
 26         }
 27         MediaFile mediaFile = mediaFileOptional.get();
 28 
 29         //当前操作的文件类型必须是avi
 30         String fileType = mediaFile.getFileType();
 31         if (fileType == null || !"avi".equals(fileType)){
 32             mediaFile.setProcessStatus("303004"); //无需处理
 33             mediaFileRepository.save(mediaFile);
 34             return;
 35         }else{
 36             mediaFile.setProcessStatus("303001");
 37             mediaFileRepository.save(mediaFile);
 38         }
 39 
 40         //将avi转成MP4
 41         /**
 42          * String ffmpeg_path,
 43          * String video_path, :被转换的文件位置
 44          * String mp4_name,: 生成的mp4文件名称
 45          * String mp4folder_path: MP4存储的路径
 46          */
 47         String video_path =videoLocation+mediaFile.getFilePath()+mediaFile.getFileName();
 48         String mp4_name = mediaFile.getFileId()+".mp4";
 49         String mp4folder_path = videoLocation+mediaFile.getFilePath();
 50 
 51         Mp4VideoUtil mp4VideoUtil = new Mp4VideoUtil(ffmpegPath,video_path,mp4_name,mp4folder_path);
 52 
 53         String result = mp4VideoUtil.generateMp4();
 54 
 55         if (result == null || !"success".equals(result)){
 56             //转换失败
 57             mediaFile.setProcessStatus("303003");
 58             MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
 59             mediaFileProcess_m3u8.setErrormsg(result);
 60             mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);
 61 
 62             mediaFileRepository.save(mediaFile);
 63             return;
 64         }
 65 
 66 
 67         //将mp4转成ts文件列表 & .m3u8文件
 68         /**
 69          * String ffmpeg_path,
 70          * String video_path,:被转换的mp4文件的路径
 71          * String m3u8_name,: m3u8文件名称
 72          * String m3u8folder_path: 保存m3u8文件与ts文件列表的文件夹路径
 73          */
 74         String MP4_video_path =videoLocation+mediaFile.getFilePath()+mp4_name;
 75         String m3u8_name = mediaFile.getFileId()+".m3u8";
 76         String m3u8folder_path = videoLocation+mediaFile.getFilePath()+"hls/";
 77 
 78         HlsVideoUtil hlsVideoUtil = new HlsVideoUtil(ffmpegPath,MP4_video_path,m3u8_name,m3u8folder_path);
 79 
 80         String m3u8Result = hlsVideoUtil.generateM3u8();
 81         if (m3u8Result == null || !"success".equals(m3u8Result)){
 82             //转换失败
 83             mediaFile.setProcessStatus("303003");
 84             MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
 85             mediaFileProcess_m3u8.setErrormsg(result);
 86             mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);
 87 
 88             mediaFileRepository.save(mediaFile);
 89             return;
 90         }
 91 
 92         //将信息保存到mongo
 93 
 94         MediaFileProcess_m3u8 mediaFileProcess_m3u8 = new MediaFileProcess_m3u8();
 95         mediaFileProcess_m3u8.setTslist(hlsVideoUtil.get_ts_list());
 96 
 97         mediaFile.setMediaFileProcess_m3u8(mediaFileProcess_m3u8);
 98 
 99         mediaFile.setProcessStatus("303002");
100 
101         //保存m3u8文件路径
102         mediaFile.setFileUrl(mediaFile.getFilePath()+"hls/"+m3u8_name);
103 
104         mediaFileRepository.save(mediaFile);
105     }
106 }

消费者

posted @ 2019-07-03 21:52  枫糖浆  阅读(949)  评论(0编辑  收藏  举报