使用ffmpeg转码MP4至m3u8格式并切片,以及m3u8自动切片脚本编写
使用ffmpeg转码MP4至m3u8格式并切片,以及m3u8自动切片脚本编写
本文将详细介绍如何使用ffmpeg将MP4视频转码为m3u8格式并进行切片,以及如何编写自动切片脚本。内容涵盖HLS协议、m3u8文件格式、ffmpeg的使用方法等,适合对视频处理和流媒体传输感兴趣的读者。
1,HLS简介
HLS(Http Live Streaming)是由Apple公司定义的用于实时流传输的协议,基于HTTP协议实现,传输内容包括两部分:M3U8描述文件和TS媒体文件。
1.1,m3u8描述文件
M3U8文件是指UTF-8编码格式的M3U文件,是一个索引纯文本文件,播放软件根据它的索引找到对应的音视频文件的网络地址进行在线播放。M3U8格式既支持直播又支持点播,在Android、iOS等平台最为常用。
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-ALLOW-CACHE:YES
#EXT-X-TARGETDURATION:3
#EXTINF:1.969
test0.ts
#EXTINF:1.972
test1.ts
#EXTINF:1.109
test2.ts
#EXT-X-DISCONTINUITY
#EXTINF:2.969
test3.ts
#EXT-X-ENDLIST
常见的参数说明:
#EXTM3U
:M3U8文件必须包含的标签,并且必须在文件的第一行。#EXT-X-VERSION
:M3U8文件的版本,常见的是3(目前最高版本应该是7)。#EXT-X-TARGETDURATION
:单个媒体文件持续时间的最大值,播放文件列表中的媒体文件在EXTINF标签中定义的持续时间必须小于或者等于该标签指定的持续时间。#EXT-X-MEDIA-SEQUENCE
:M3U8直播时的直播切换序列。#EXTINF
:每个分片的duration。#EXT-X-DISCONTINUITY
:表示前一片分片和后一片分片有不连续。#EXT-X-ENDLIST
:表明M3U8文件不会再产生更多的切片,可以理解为该M3U8已停止更新。#EXT-X-STREAM-INF
:出现在多级M3U8文件中时,包含一些属性如BANDWIDTH、AVERAGE-BANDWIDTH等。#EXT-X-KEY
:表示如何对media segments进行解码。#EXT-X-PROGRAM-DATE-TIME
:将一个绝对时间或日期和一个媒体段中的第一个sample相关联。#EXT-X-ALLOW-CACHE
:是否允许做cache。#EXT-X-PLAYLIST-TYPE
:提供关于PlayList的可变性的信息。
1.2,ts媒体文件
HLS的优势在于自适应码率流播,客户端会根据网络状况自动选择不同码率的视频流。服务器端提供多码率视频流,并在列表文件中注明,播放器根据播放进度和下载速度进行自动调整。
为什么要用TS而不是MP4?这是因为两个TS片段可以无缝拼接独立解码,播放器能连续播放,而MP4文件由于编码方式的原因,两段MP4不能无缝拼接,播放器连续播放两个MP4文件会出现破音和画面间断,影响用户体验。
2,ffmpeg简介
官方网站:https://ffmpeg.org/
FFMPEG堪称自由软件中最完备的一套多媒体支持库,它几乎实现了所有当下常见的数据封装格式、多媒体传输协议以及音视频编解码器,堪称多媒体业界的瑞士军刀。对于从事多媒体技术开发的工程师来说,深入研究FFMPEG成为一门必不可少的工作。
2.1,FFMPEG命令
FFMPEG提供的命令行(CLI)工具ffmpeg,其使用方法如下(方括号表示可选项,花括号表示必选项目):
ffmpeg [global options] {[infile options]['-i' 'infile'] ...} {[outfile options] 'outfile' ...}
参数选项由三部分组成:可选的一组全局参数、一组或多组输入文件参数、一组或多组输出文件参数,其中,每组输入文件参数以‘-i’为结束标记;每组输出文件参数以输出文件名为结束标记。
2.2,基本参数
能力集列表
-formats
:列出支持的文件格式。-codecs
:列出支持的编解码器。-decoders
:列出支持的解码器。-encoders
:列出支持的编码器。-protocols
:列出支持的协议。-bsfs
:列出支持的比特流过滤器。-filters
:列出支持的滤镜。-pix_fmts
:列出支持的图像采样格式。-sample_fmts
:列出支持的声音采样格式。
常用输入选项
-i filename
:指定输入文件名。-f fmt
:强制设定文件格式。-ss hh:mm:ss[.xxx]
:设定输入文件的起始时间点。
对于输入,以下选项通常是自动识别的,但也可以强制设定:
-c codec
:指定解码器。-acodec codec
:指定声音的解码器。-vcodec codec
:指定视频的解码器。-b:v bitrate
:设定视频流的比特率。-r fps
:设定视频流的帧率。-s WxH
:设定视频的画面大小。-pix_fmt format
:设定视频流的图像格式。-ar sample rate
:设定音频流的采样率。-ab bitrate
:设定音频流的比特率。-ac channels
:设置音频流的声道数目。
常用输出选项
-f fmt
:强制设定文件格式。-c codec
:指定编码器。-acodec codec
:指定声音的编码器。-vcodec codec
:指定视频的编码器。-r fps
:设定视频编码器的帧率。-pix_fmt format
:设置视频编码器使用的图像格式。-ar sample rate
:设定音频编码器的采样率。-b bitrate
:设定音视频编码器输出的比特率。-ab bitrate
:设定音频编码器输出的比特率。-ac channels
:设置音频编码器的声道数目。-an
:忽略任何音频流。-vn
:忽略任何视频流。-t hh:mm:ss[.xxx]
:设定输出文件的时间长度。-to hh:mm:ss[.xxx]
:如果没有设定输出文件的时间长度的画可以设定终止时间点。
3,使用ffmpeg进行mp4与m3u8之间转换变切片
切片会将MP4切成一个m3u8文件和无数个ts文件。
3.1,安装ffmpeg
首先是给服务器安装ffmpeg,这里以ubuntu为例,ubuntu上安装非常简单,执行如下代码:
sudo apt install ffmpeg
ffmpeg -version
第一行是安装ffmpeg,第二行是查看ffmpeg版本,来确认是否安装好了。
3.2,转码切片m3u8
然后MP4转码并切片,基本命令:
ffmpeg -i movie.mp4 -profile:v baseline -level 3.0 -start_number 0 -hls_time 10 -hls_list_size 0 -f hls movie.m3u8
这个命令,大概15分钟左右一部片子。其中movie.mp4和movie.m3u8就是原文件和需要转换成的m3u8文件。
-profile:v baseline
:大概意思是档次转成基本画质,有四种画质级别,分别是baseline, extended, main, high,从低到高。-level 3.0
:大概也是视频画质级别吧,基本上是从1到5。-start_number 0
:表示从0开始。-hls_time 10
:标识每10秒切一个。
转码速度有点慢,但是可以多线程跑,加上参数,-threads 10
和 -preset ultrafast
:
-threads
:线程,根据自己cpu核数自己调整。-preset
:调节编码速度和质量的平衡,有ultrafast(转码速度最快,视频往往也最模糊)、superfast、veryfast、faster、fast、medium、slow、slower、veryslow、placebo这10个选项,从快到慢。
ffmpeg -i movie.mp4 -threads 10 -preset ultrafast -profile:v baseline -level 3.0 -start_number 0 -hls_time 10 -hls_list_size 0 -f hls movie.m3u8
以上速度虽然提升了,但是画质可能会降低,有时候会模糊。命令大概5分钟一部片子。
有没有速度快,而且画质不影响的命令?
有的,这也是我最常用的,最简单的命令。
重要参数:
-codec: copy
:复制原来的码率和编码,因为不进行转码,所以速度很快,基本10秒一部片,而且基本无损画质。
所以最终的命令为:(正常片子10秒一部片)
ffmpeg -i movie.mp4 -codec: copy -start_number 0 -hls_time 10 -hls_list_size 0 -f hls movie.m3u8
3.3,hls_time不生效的问题
这个命令,虽然速度快,但是遇到一个问题。我改切片时间hls_time不生效,如我改成4秒。
ffmpeg -i movie.mp4 -codec: copy -start_number 0 -hls_time 4 -hls_list_size 0 -f hls movie.m3u8
但在 m3u8 文件中始终是#EXTINF:8.。hls_time不管改成多少,都是这个数。在GitHub问大神,得到答案。大神原话参考:
-c copy is fast and (oftentimes) lossless because you aren’t remuxing. That’s pretty much the entire point. To quote
The video streams can be encoded in the MPEG-1, MPEG-2, MPEG-4 and H. 264/AVC standards. The audio streams can be (HE)-AAC, MPEG-1 Audio Layer 1-2-3, CELP, TwinVQ, Vorbis or Apple Lossless.
Note: it’s missing H.265/HEVC. On first glance. Some codecs actually are segmented already. Hence you’d have to remux if you want to change the segmentation time.
大概意思, -codec: copy
所支持的视频流MPEG-1, MPEG-2, MPEG-4 and H. 264/AVC 标准流,和音频流(HE)-AAC, MPEG-1 Audio Layer 1-2-3, CELP, TwinVQ, Vorbis,Apple Lossless。
注:H.265/HEVC是不支持的。有些转码器已经内置切片时长,如果需要改切片时长,需要重新编码。
所以我片子是通过格式工厂默认编码,查询得知,格式工厂默认内置为8秒或者10秒。所以hls_time自定义切片时间不生效。
解决方法,格式工厂重新编码即可。格式工厂编码输出设置,改两个选项。
1:二次编码,是
2:关键帧间,选择2或者更小。2代表可以切2秒的倍数,如可切2秒,4秒,6秒。默认应该为8,所以只能切8秒。
重新编码的影片,可以重新切片2秒或者4秒。
4,编写脚本批量自动检测切片
服务器影片目录为TV,子目录分别由m3u8,mp4,upload三个文件夹。
ubuntu@ubuntu:/TV$ tree .
├── m3u8
├── mp4
└── upload
脚本逻辑,电影上传至upload,脚本检测到后,将影片移动至mp4文件夹作为原片保存,然后切片文件保存在m3u8文件夹。upload可自行创建文件夹,脚本自动在mp4和m3u8自动生成对应文件夹。切片完成,保存相关观看链接至指定txt文档,作为日志保存。
脚本内容:
#! /bin/bash
#調試模式
#set -x
#上传目录
path_tv=/TV/upload
cut_m3u8(){
mv $path_upload/$filemp4 $path_mp4 &&
ffmpeg -i $path_mp4/$filemp4 -codec: copy -start_number 0 -hls_time 4 -hls_list_size 0 -f hls $path_m3u8/$filem3u8 &&
echo "$name.m3u8 切片完成,时间:`date +%F_%R`" >> /var/www/tv_link/link.txt &&
echo "$name网站链接为:https://ywbj.cc/$path/$name.m3u8" >> /var/www/tv_link/link.txt &&
echo >> /var/www/tv_link/link.txt
}
#循环在upload查找所有的MP4格式文件
for file in `find $path_tv $1 | grep 'mp4'`
do
#截取影片名称
name1=${file%*.mp4}
name=${name1*/}
#合并MP4文件名,和m3u8文件名。
filem3u8=`eval echo $name.m3u8`
filemp4=`eval echo $name.mp4`
#截取upload下影片文件夹名称
path1=${file%/*}
path=${path1*/}
path_upload=/TV/upload/$path
path_mp4=/TV/mp4/$path
path_m3u8=/TV/m3u8/$path
#检测是否由对应文件夹,没有则创建
if [ ! -d $path_mp4 ]; then
# echo "创建文件夹"
mkdir -p $path_mp4
fi
if [ ! -d $path_m3u8 ]; then
# echo "创建文件夹"
mkdir -p $path_m3u8
fi
if [ -f $path_m3u8/$filem3u8 ]; then
echo "$filem3u8已存在,删除旧文件,重新切片..." >> /var/www/tv_link/link.txt &&
rm -f $path_m3u8/$name* &&
cut_m3u8
else
cut_m3u8
fi
done
5,定时任务,自动运行脚本
定时任务,脚本每5分钟,检测一次upload文件夹,是否有mp4文件。
crontab -e
添加定时任务,脚本保存在/shell/m3u8_auto.sh路径。
#切片脚本,5分钟检查一次
*/5 * * * * bash /shell/m3u8_auto.sh