如何实现在线视频播放?(1),程序员面试金典

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

概括理解:流媒体就是将视频文件分成许多小块儿,将这些小块儿作为数据包通过网络发送出去,实现一边传输视频 数据 包一边观看视频。

  • 流式传输

在网络上传输音、视频信息有两个方式:下载和流式传输。

下载:就是把音、视频文件完全下载到本机后开始播放,它的特点是必须等到视频文件下载完成方可播放,播放等待时间较长,无法去播放还未下载的部分视频。

流式传输:就是客户端通过链接视频服务器实时传输音、视频信息,实现“边下载边播放”。

流式传输包括如下两种方式:

1) 顺序流式传输

即顺序下载音、视频文件,可以实现边下载边播放,不过,用户只能观看已下载的视频内容,无法快进到未下载的视频部分,顺序流式传输可以使用Http服务器来实现,比如Nginx、Apache等。

2)实时流式传输

实时流式传输可以解决顺序流式传输无法快进的问题,它与Http流式传输不同,它必须使用流媒体服务器并且使用流媒体协议来传输视频,它比Http流式传输复杂。常见的实时流式传输协议有RTSP、RTMP、RSVP等。

  • 流媒体系统的概要结构

通过流媒体系统的概要结构学习流媒体系统的基本业务流程。

1、将原始的视频文件通过编码器转换为适合网络传输的流格式,编码后的视频直接输送给媒体服务器。

原始的视频文件通常是事先录制好的视频,比如通过摄像机、摄像头等录像、录音设备采集到的音视频文件,体积较大,要想在网络上传输需要经过压缩处理,即通过编码器进行编码 。

2、媒体服务获取到编码好的视频文件,对外提供流媒体数据传输接口,接口协议包括 :HTTP、RTSP、RTMP等

3、播放器通过流媒体协议与媒体服务器通信,获取视频数据,播放视频。

1.2.2 点播方案

==============

本项目包括点播和直播两种方式,我们先调研点播的方案,如下:

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

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

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

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

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

HLS协议规定:基于Http协议,视频封装格式为ts,视频的编码格式为H264,音频编码格式为MP3、AAC或者AC-3

  • HLS是什么 ?

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

IOS、Android设备、及各大浏览器都支持HLS协议。

详细参考:https://baike.baidu.com/item/HLS/8328931?fr=aladdin

采用HLS方案即可实现边下载边播放,可以不用使用rtmp等流媒体协议,不用构建专用的媒体服务器,节省成本。

本项目点播方案确定为方案3。

2 FFmpeg 的基本使用

==================

我们将视频录制完成后,使用视频编码软件对视频进行编码,本项目 使用FFmpeg对视频进行编码 。

下载 :ffmpeg-20180227-fa0c9d6-win64-static.zip,并解压,本教程将ffmpeg解压到了C:\Java_Soft\xczx\ffmpeg-20180227-fa0c9d6-win64-static下。

FFmpeg被许多开源项目采用,QQ影音、暴风影音、VLC等。

下载:FFmpeg https://www.ffmpeg.org/download.html#build-windows

将C:\Java_Soft\xczx\ffmpeg-20180227-fa0c9d6-win64-static\bin目录配置在path环境变量中。

简单的测试:

将一个.avi文件转成mp4、mp3、gif等。

比如我们将lucene.avi文件转成mp4,运行如下命令:

ffmpeg -i lucene.avi lucene.mp4

转成mp3:ffmpeg -i lucene.avi lucene.mp3

转成gif:ffmpeg -i lucene.avi lucene.gif

官方文档(英文):http://ffmpeg.org/ffmpeg.html

2.1 生成m3u8/ts文件

===================

使用ffmpeg生成 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

下面把各参数意思大概讲讲,大概了解意思即可,不再此展开流媒体专业知识的讲解。

-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 lucene.mp4   -hls_time 10 -hls_list_size 0  -hls_segment_filename ./hls/lucene_%05d.ts ./hls/lucene.m3u8

-hls_time 设置每片的长度,单位为秒

-hls_list_size n: 保存的分片的数量,设置为0表示保存所有分片

-hls_segment_filename :段文件的名称,%05d表示5位数字

生成的效果是:将lucene.mp4视频文件每10秒生成一个ts文件,最后生成一个m3u8文件,m3u8文件是ts的索引文件。

使用VLC打开m3u8文件,测试播放效果,VLC 是一款自由、开源的跨平台多媒体播放器及框架,可播放大多数多媒体文件,以及 DVD、音频 CD、VCD 及各类流媒体协议。(http://www.videolan.org/)

3. 视频(媒资)处理

================

开发环境

========

1. 创建媒资数据库

===============

导入shcool.sql

2. 创建媒资服务工程

================

基于springboot创建工程

3. 上传文件

============

3.1 断点续传解决方案

================

通常视频文件都比较大,所以对于媒资系统上传文件的需求要满足大文件的上传要求。http协议本身对上传文件大小没有限制,但是客户的网络环境质量、电脑硬件环境等参差不齐,如果一个大文件快上传完了网断了,电断了没有上传完成,需要客户重新上传,这是致命的,所以对于大文件上传的要求最基本的是断点续传。

什么是断点续传:

引用百度百科:断点续传指的是在下载或上传时,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载,断点续传可以提高节省操作时间,提高用户体验性。

上传流程如下:

1、上传前先把文件分成块

2、一块一块的上传,上传中断后重新上传,已上传的分块则不用再上传

3、各分块上传完成最后合并文件

文件下载则同理。

3.2 文件分块与合并

===============

为了更好的理解文件分块上传的原理,下边用java代码测试文件的分块与合并。

3.3 文件分块

============

文件分块的流程如下:

1、获取源文件长度

2、根据设定的分块文件的大小计算出块数

3、从源文件读数据依次向每一个块文件写数据

//测试文件分块方法

@Test

public void testChunk() throws IOException {

File sourceFile = new File(“F:/develop/ffmpeg/lucene.mp4”);

//       File sourceFile = new File(“d:/logo.png”);

String chunkPath = “F:/develop/ffmpeg/chunk/”;

File chunkFolder = new File(chunkPath);

if(!chunkFolder.exists()){

chunkFolder.mkdirs();

}

//分块大小

long chunkSize = 102410241;

//分块数量

long chunkNum = (long) Math.ceil(sourceFile.length() * 1.0 / chunkSize );

if(chunkNum<=0){

chunkNum = 1;

}

//缓冲区大小

byte[] b = new byte[1024];

//使用RandomAccessFile访问文件

RandomAccessFile raf_read = new RandomAccessFile(sourceFile, “r”);

//分块

for(int i=0;i<chunkNum;i++){

//创建分块文件

File file = new File(chunkPath+i);

boolean newFile = file.createNewFile();

if(newFile){

//向分块文件中写数据

RandomAccessFile raf_write = new RandomAccessFile(file, “rw”);

int len = -1;

while((len = raf_read.read(b))!=-1){

raf_write.write(b,0,len);

if(file.length()>chunkSize){

break;

}

}

raf_write.close();

}

}

raf_read.close();

}

3.4 文件合并

============

文件合并流程:

1、找到要合并的文件并按文件合并的先后进行排序

2、创建合并文件

3、依次从合并的文件中读取数据向合并文件写入数据

//测试文件合并方法

@Test

public void testMerge() throws IOException {

//块文件目录

File chunkFolder = new File(“F:/develop/ffmpeg/chunk/”);

//合并文件

File mergeFile = new File(“F:/develop/ffmpeg/lucene1.mp4”);

if(mergeFile.exists()){

mergeFile.delete();

}

//创建新的合并文件

mergeFile.createNewFile();

//用于写文件

RandomAccessFile raf_write = new RandomAccessFile(mergeFile, “rw”);

//指针指向文件顶端

raf_write.seek(0);

//缓冲区

byte[] b = new byte[1024];

//分块列表

File[] fileArray = chunkFolder.listFiles();

// 转成集合,便于排序

List fileList = new ArrayList(Arrays.asList(fileArray));

// 从小到大排序

Collections.sort(fileList, new Comparator() {

@Override

public int compare(File o1, File o2) {

if (Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())) {

return -1;

}

return 1;

}

});

//合并文件

for(File chunkFile:fileList){

RandomAccessFile raf_read = new RandomAccessFile(chunkFile,“rw”);

int len = -1;

while((len=raf_read.read(b))!=-1){

raf_write.write(b,0,len);

}

raf_read.close();

}

raf_write.close();

}

4. 前端页面

============

WebUploader介绍

=================

如何在web页面实现断点续传?

常见的方案有:

1、通过Flash上传,比如SWFupload、Uploadify。

2、安装浏览器插件,变相的pc客户端,用的比较少。

3、Html5

随着html5的流行,本项目采用Html5完成文件分块上传。

本项目使用WebUploader完成大文件上传功能的开发,WebUploader官网地址:http://fexteam.gz01.bdysite.com/webuploader/

使用WebUploader上传流程如下:

钩子方法

========

在webuploader中提供很多钩子方法,下边列出一些重要的:

本项目使用如下钩子方法:

1)before-send-file

在开始对文件分块儿之前调用,可以做一些上传文件前的准备工作,比如检查文件目录是否创建完成等。

2)before-send

在上传文件分块之前调用此方法,可以请求服务端检查分块是否存在,如果已存在则此分块儿不再上传。

3)after-send-file

在所有分块上传完成后触发,可以请求服务端合并分块文件。

注册钩子方法源代码:

WebUploader.Uploader.register({

“before-send-file”:“beforeSendFile”,

“before-send”:“beforeSend”,

“after-send-file”:“afterSendFile”

}

构建WebUploader

=================

使用webUploader前需要创建webUploader对象。

指定上传分块的地址:/api/media/upload/uploadchunk

// 创建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:110241024, // 分块大小(默认5M)

threads:3, // 开启多个线程(默认3个)

prepareNextFile:true// 允许在文件传输时提前把下一个文件准备好

}

)

before-send-file

====================

文件开始上传前前端请求服务端准备上传工作。

type:“POST”,

url:“/api/media/upload/register”,

data:{

// 文件唯一表示

fileMd5:this.fileMd5,

fileName: file.name,

fileSize:file.size,

mimetype:file.type,

fileExt:file.ext

}

before-send

===============

上传分块前前端请求服务端校验分块是否存在。

type:“POST”,

url:“/api/media/upload/checkchunk”,

data:{

// 文件唯一表示

fileMd5:this.fileMd5,

// 当前分块下标

chunk:block.chunk,

// 当前分块大小

chunkSize:block.end-block.start

}

after-send-file

===================

在所有分块上传完成后触发,可以请求服务端合并分块文件。

type:“POST”,

url:“/api/media/upload/mergechunks”,

data:{

fileMd5:this.fileMd5,

fileName: file.name,

fileSize:file.size,

mimetype:file.type,

fileExt:file.ext

}

页面效果

========

学员使用:

  1. 直接解压资料xc-ui-pc-teach到webstorm工作目录

  2. 通过nginx访问,在nginx中配置 (解决跨域)

server {

listen       82;

server_name localhost;

#视频中心

location / {

proxy_pass http://127.0.0.1:12000;

proxy_set_header Host $http_host;

add_header Access-Control-Allow-Origin *;

add_header Access-Control-Allow-Credentials true;

add_header Access-Control-Allow-Methods GET;

}

#媒资管理后台跨域

location ^~ /api/media/ {

proxy_pass http://127.0.0.1:9000/media/;

proxy_set_header Host $http_host;

add_header Access-Control-Allow-Origin *;

add_header Access-Control-Allow-Credentials true;

add_header Access-Control-Allow-Methods “GET,POST,OPTIONS”;

}

}

可以访问 http://127.0.0.1:12000/#/media/upload 或者 http://localhost:82/#/media/upload/ 查看页面效果。

5. 媒资服务端编写

===============

服务端需要实现如下功能:

1、上传前检查上传环境

检查文件是否上传,已上传则直接返回。

检查文件上传路径是否存在,不存在则创建。

2、分块检查

检查分块文件是否上传,已上传则返回true。

未上传则检查上传路径是否存在,不存在则创建。

3、分块上传

将分块文件上传到指定的路径。

4、合并分块

将所有分块文件合并为一个文件。

在数据库记录文件信息。


  • 由于上传过程复杂,开发时按业务流程分别实现。
  1. 配置

application.yml配置上传文件的路径:

xc-service-manage-media:

upload-location: C:/school/video/  # 媒资保存路径

ffmpeg-path: C:/Java_Soft/xczx/ffmpeg-20180227-fa0c9d6-win64-static/bin/ffmpeg.exe # ffmpeg路径

  1. 定义Dao

使用mybatis-plus

@Mapper

@Component

public interface FileMsgMapper extends BaseMapper {

}

后话

对于面试,说几句个人观点。

面试,说到底是一种考试。正如我们一直批判应试教育脱离教育的本质,为了面试学习技术也脱离了技术的初心。但考试对于人才选拔的有效性是毋庸置疑的,几千年来一直如此。除非你有实力向公司证明你足够优秀,否则,还是得乖乖准备面试。这也并不妨碍你在通过面试之后按自己的方式学习。
其实在面试准备阶段,个人的收获是很大的,我也认为这是一种不错的学习方式。首先,面试问题大部分基础而且深入,这些是平时工作的基础。就好像我们之前一直不明白学习语文的意义,但它的意义就在每天的谈话间。

所谓面试造火箭,工作拧螺丝。面试往往有更高的要求,也迫使我们更专心更深入地去学习一些知识,也何尝不是一种好事。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 由于上传过程复杂,开发时按业务流程分别实现。
  1. 配置

application.yml配置上传文件的路径:

xc-service-manage-media:

upload-location: C:/school/video/  # 媒资保存路径

ffmpeg-path: C:/Java_Soft/xczx/ffmpeg-20180227-fa0c9d6-win64-static/bin/ffmpeg.exe # ffmpeg路径

  1. 定义Dao

使用mybatis-plus

@Mapper

@Component

public interface FileMsgMapper extends BaseMapper {

}

后话

对于面试,说几句个人观点。

面试,说到底是一种考试。正如我们一直批判应试教育脱离教育的本质,为了面试学习技术也脱离了技术的初心。但考试对于人才选拔的有效性是毋庸置疑的,几千年来一直如此。除非你有实力向公司证明你足够优秀,否则,还是得乖乖准备面试。这也并不妨碍你在通过面试之后按自己的方式学习。
其实在面试准备阶段,个人的收获是很大的,我也认为这是一种不错的学习方式。首先,面试问题大部分基础而且深入,这些是平时工作的基础。就好像我们之前一直不明白学习语文的意义,但它的意义就在每天的谈话间。

所谓面试造火箭,工作拧螺丝。面试往往有更高的要求,也迫使我们更专心更深入地去学习一些知识,也何尝不是一种好事。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-w7QQvp87-1713551132046)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 27
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值