一, 背景
笔者想将自己收藏的一些电影放到网站上可以用来随时播放, 不过遇到了一个问题, 便是如果直接将 MP4 文件放放到网站目录当中, 手机端必须下载整个视频才可以播放, 而如果跨外网传输, 这实在是不太现实.
为了解决这个问题, 便想着搭建一套流媒体服务, 这样手机就可以边看边下载, 查询了一些资料了了解到需要先将视频分成一小片来传输, 比如将 MP4 转码为 M3U8 格式, 查询了相关转码方法, 比较主流的方式是使用 FFMPEG 这个开源工具
二, 操作概要
安装 FFMPEG
服务搭建
功能测试
三, 搭建 FFMPEG
视频转码的工具可能有很多, 但开源且使用人数最多的还是莫过于 FFMPEG 这个工具, 具体功能笔者不在这里详细讲解; 安装此工具的方式有很多, 比如 apt 安装, 源码安装, docker 安装等等, 不过 docker 是跨平台的, 因此笔者这里将以 docker 方式安装为例
3.1 镜像下载
首先笔者需要下载对应的 docker 镜像, 参考命令如下
docker pull jrottenberg/FFMPEG
命令执行过程中将会从远处下载镜像, 这个时间由当前的网络带宽所决定, 当下载完成之后, 可以看到如下参考信息
- Using default tag: latest
- latest: Pulling from jrottenberg/FFMPEG
- b234f539f7a1: Pull complete
- 55172d420b43: Pull complete
- 5ba5bbeb6b91: Pull complete
- 43ae2841ad7a: Pull complete
- f6c9c6de4190: Pull complete
- 2a0ef76bfa54: Pull complete
- 40ddf796a4bb: Pull complete
- 32ba137d2764: Pull complete
- Digest: sha256:bcf65375f593518de7e450fd6b775d16a047d3ded00957c2e794e2fe8f7e1590
- Status: Downloaded newer image for jrottenberg/FFMPEG:latest
3.2 容器运行
当容器下载完毕之后, 可以用一些命令进行验证是否能够正常运行, 如下参考命令
docker run jrottenberg/FFMPEG
命令执行完毕之后, 会返回如下结果
- Hyper fast Audio and Video encoder
- usage: FFMPEG [options] [[infile options] -i infile]... {
- [outfile options] outfile
- }...
- Getting help:
..... 省略
- Audio options:
- -aframes number set the number of audio frames to output
- -aq quality set audio quality (codec-specific)
- -ar rate set audio sampling rate (in Hz)
- -ac channels set number of audio channels
- -an disable audio
- -acodec codec force audio codec ('copy' to copy stream)
- -vol volume change audio volume (256=normal)
- -af filter_graph set audio filters
- Subtitle options:
- -s size set frame size (WxH or abbreviation)
- -sn disable subtitle
- -scodec codec force subtitle codec ('copy' to copy stream)
- -stag fourcc/tag force subtitle tag/fourcc
- -fix_sub_duration fix subtitles duration
- -canvas_size size set canvas size (WxH or abbreviation)
- -spre preset set the subtitle options to the indicated preset
3.3 查看支持协议
FFMPEG 所支持的输入输出协议非常多, 比如可以选择 file 协议作为来源, 使用 hls 协议作为输出结果, 具体所支持的协议可以通过如下命令查看
docker run jrottenberg/FFMPEG -protocols
执行命令之后, 参考结果如下
- FFMPEG version 3.4.2 Copyright (c) 2000-2018 the FFMPEG developers
- built with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.9) 20160609
- configuration: --disable-debug --disable-doc --disable-ffplay --enable-shared --enable-avresample --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-gpl --enable-libass --enable-libfreetype --enable-libvidstab --enable-libmp3lame --enable-libopenjpeg --enable-libopus --enable-libtheora --enable-libvorbis
..... 省略
- Supported file protocols:
- Input:
- async
- cache
- concat
- crypto
- data
..... 省略
- Output:
- crypto
- file
..... 省略
tls
udp
3.4 转换测试
现在笔者使用 FFMPEG 对视频进行转码测试, 命令非常简单, 首先需要通过 - v 将视频所在的目录挂载到容器中, 然后使用 - i 选项找到容器中对应的视频文件;
接着就可以对编码进行一些选项, 比如 - hls_time 10 便是将文件没 10 秒输出一个 TS 文件,-hls_list_size 0 则是在 m3u8 文件中记录所以 ts 文件 (默认是记录最后五个 TS 文件), 参数最后则填写文件输出路径, 具体参考命令如下:
docker run -v /Users/song/video:/root/download jrottenberg/FFMPEG:latest -i /root/download/1.mp4 -hls_time 10 -hls_list_size 0 -f hls /root/download/index.m3u8
命令执行过程中会展示转换进度, 参考如下返回所示
- Metadata:
- major_brand : mp42
- minor_version : 0
- compatible_brands: mp42mp41
- encoder : Lavf57.83.100
- Stream #0:0(eng): Video: h264 (libx264), yuv420p(progressive), 1920x1080, q=-1--1, 30 fps, 90k tbn, 30 tbc (default)
- Metadata:
- creation_time : 2018-08-21T15:09:24.000000Z
- handler_name : Alias Data Handler
- encoder : Lavc57.107.100 libx264
- Side data:
- cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: -1
- Stream #0:1(eng): Audio: aac, 48000 Hz, stereo, fltp, 128 kb/s (default)
- Metadata:
- creation_time : 2018-08-21T15:09:24.000000Z
- handler_name : Alias Data Handler
- encoder : Lavc57.107.100 aac
- frame= 82 fps= 12 q=29.0 size=N/A time=00:00:02.62 bitrate=N/A speed=0.381x
此时便可以在刚才的挂载点查看 TS 文件, 如下图所示
现在笔者将刚才的 TS 文件都删除, 在下面将使用自动化完成.
四, 服务搭建
在上一步中笔者已经成功通过终端使用 FFMPEG 将视频进行转码, 下面笔者将结合 PHP 代码将这些操作完全自动化实现, 这样便可以达到通过手机访问网站, 服务端自动完成转码播放的需求, 这个过程包括创建虚拟主机, 编写展示视频列表, 视频自动解码三个部分
4.1 创建虚拟主机
首先笔者需要借助 nginx 搭建一个 web 服务, 这时便需要修改配置文件, 但并不记得 nginx 配置文件存放位置, 此时可以借助如下命令
sudo nginx -t
得到结果如下, 在结果中可以便可以看到 nginx 的配置文件存放位置
- nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
- nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
使用 VIM 编辑器直接编辑 nginx 配置文件
VIM /usr/local/etc/nginx/nginx.conf
然后在配置文件中加入如下参考配置信息
- server {
- listen 8089;
- server_name localhost;
- root /Users/song/mycode/work/test/video;
- location / {
- index index.html index.htm index.PHP;
- }
- location ~ \.PHP$ {
- fastcgi_pass 127.0.0.1:9000;
- fastcgi_index index.PHP;
- fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
- include fastcgi_params;
- }
- }
4.2 获取视频列表
nginx 配置完成之后, 便需要编写 PHP 代码, 通过 PHP 可以获取到目录的视频列表, 然后将其输出到网页当中, 参考代码如下所示
- <?PHP
- $list = scandir('/Users/song/video/');
- foreach ($list as $key => $val) {
- if (!in_array(pathinfo($val, PATHINFO_EXTENSION), ['mp4', 'rmvb', 'wmv'])) {
- continue;
- }
- ?>
- <a class="btn btn-default btn-video btn-lg" href="./encode.php?name=<?= $val ?>" role="button">
- <h2><?= $val ?></h2></a>
- <?PHP }
- } ?>
在代码中, 首先通过 scandir 读取文件夹下所有文件, 然后进行 foreach 循环, 通过后缀名来判断是否为视频文件, 如果是视频文件, 则输出一个链接地址方便用户选择.
4.3 进行视频转码
上面的代码在列出视频列表之后, 当用户点击链接后就需要使用 FFMPEG 进行转码, 参考代码如下
- <?PHP
- // 接收必要参数
- $name = $_GET['name'] ?? '1.mp4';
- $forced = $_GET['forced'] ?? 0;
- $fileName = getFileName($name);
- $outPath = '/Users/song/video';
- $inPath = '/root/download';
- $dir = __DIR__;
- // 判断之前是否已经转码, 如果不强制转码便先返回
- if (is_dir("$outPath/$fileName") && empty($forced)) {
- header("location:./static/{$fileName}/index.m3u8");
- die;
- }
- // 将目标映射过来
- system("ln -s {$outPath} {$dir}/static");
- // 先创建文件夹
- system("mkdir -p {$outPath}/{$fileName}");
- // 进行转码
- $FFMPEG = "docker run -v $outPath:/root/download jrottenberg/ffmpeg:latest";
- $cmd = "nohup $ffmpeg -i {$inPath}/{$name} -hls_time 10 -hls_list_size 0 -f hls -r 25 {$inPath}/{$fileName}/index.m3u8>> ./code.log &";
- system($cmd);
- // 延时执行跳转
- returnUrl($fileName);
- function getFileName($filename)
- {
- $houzhui = substr(strrchr($filename, '.'), 1);
- $result = basename($filename, "." . $houzhui);
- return $result;
- }
- function returnUrl($fileName)
- {
- echo "<a class='btn btn-video btn-lg'href='./static/{$fileName}/index.m3u8'><h1 > 正在处理中... 点击进行跳转 </h1></a>";
- die;
- }
在上面代码当中, 考虑文件是否已经被转码, 如果已经转码过了直接返回播放地址, 否则创建一个存放 TS 文件的文件夹, 然后进行转码, 转码的时候使用 nohup 命令可以让 FFMPEG 异步执行, 然后 PHP 返回播放地址.
五, 检验与测试
通过前面的步骤, 笔者已经完整的搭建了一套流媒体服务器, 下面将检验这些服务是否能否正常运行, 包括视频列表展示, 视频转码是否正常, 已经转码的视频能否播放
5.1 视频列表
首先通过浏览器打开 URL 地址如下
http://localhost:8089/
加载完成之后可以看到如下的视频列表
读者如果将上方的代码运行界面有稍有差异, 因为笔者为了节省文章篇幅, 并没有将样式代码放到文章当中, 如需界面好看可以自行编写样式代码.
5.2 视频转码
在视频列表点击一个链接之后, 后台 PHP 程序将会执行转码任务, 然后返回一个链接地址, 如下图所示
此时便代表 FFMPEG 已经在后台运行, 可以通过如下命令进行查看 FFMPEG 这个容器的运行状态, 参考命令如下
docker ps
返回的参考结果如下所示
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- ac3e7233eb9f jrottenberg/FFMPEG:latest "ffmpeg -i /root/dow..." 1 hours ago Up 1 hours keen_feynman
从上面的返回结果当中可以看出当前正有一个任务处于运行状态, 此时打开视频输出目录, 会看到有多个 ts 格式的视频文件, 这些文件是刚在通过 PHP 自动执行所产生的, 如下图所示
当看到如上图的转码视频文件时, 便可以通过浏览器进行访问
5.3 视频播放
这里需要记住, HLS 协议是苹果公司所开发的, 因此除了苹果的浏览器外, 其他浏览器默认都是不支持 m3u8 的解析的, 如果需要使用其他浏览器播放, 需要安装插件; 苹果的默认就支持则不需要
笔者重新通过 Safari 浏览器打开页面, 然后再次选择 1.mp4 视频, 则直接跳转到了播放页面, 如下图所示
看到这里, 搭建流媒体就基本已经完成了, 如果需要将更多视频播放, 只需要将视频文件存放到指定的视频目录, 网页中便会自动读取出来, 页面可能太简化, 读者可以根据自己的需要将 HTML 页面美化一下.
六, 新书推荐
如果对笔者的实战文章较为感兴趣, 可以关注笔者新书《PHP Web 安全开发实战》, 现已在各大平台上架销售, 封面如下图所示
来源: https://www.cnblogs.com/tangqingsong/p/9864905.html