背景
Node 使用 node-fluent-ffmpeg 进行 ffmpeg 操作,需要下载并且设置 ffmpeg 路径。node-ffmpeg-installer会自动下载ffmpeg,但是这个下载的ffmpeg 版本较老。ffmpeg-static,此版本为ffmpeg 5.x以上。只支持macOS(64位和arm64)、Linux(32位和64位、armhf、arm64)和Windows(32和64位)。
ffmpeg 各版本下载地址、淘宝rpm地址、ffmpeg 能不能低 CPU 转码视频
fluent-ffmpeg
ffmpeg的原生命令行较为复杂,而 fluent-ffmpeg 则将这些命令抽象为一个npm包,间接调用ffmpeg。 node-fluent-ffmpeg命令查看,一般配合 node-ffmpeg-installer 和 ffmpeg-static使用。也可以自主安装ffmpeg,将使用路径指向ffmpeg。
centos linux下安装ffmpeg
下载解压
wget http://www.ffmpeg.org/releases/ffmpeg-5.1.2.tar.gz
tar -zxvf ffmpeg-5.1.2.tar.gz
进入解压后目录,输入如下命令/usr/local/ffmpeg为自己指定的安装目录
cd ffmpeg-5.1.2
./configure --prefix=/usr/local/ffmpeg
make && make install
配置变量
vi /etc/profile
在最后PATH添加环境变量:
export PATH=$PATH:/usr/local/ffmpeg/bin
保存退出
查看是否生效
source /ect/profile 设置生效
查看版本
ffmpeg -version 查看版本
配置参数编译
执行以下命令是最基础的编译,很多的ffmpeg参数无法使用,因此需要下载插件并且带上相应的参数进行编译。
./configure --prefix=/usr/local/ffmpeg
从如下图看到,安装的 ffmpeg 5.1.2 版本带了很多编译参数。
错误QA
1、yasm/nasm not found or too old
yasm/nasm not found or too old. Use –disable-yasm for a crippled build.
If you think configure made a mistake, make sure you are using the latest
version from Git. If the latest version fails, report the problem to the
ffmpeg-user@ffmpeg.org mailing list or IRC #ffmpeg on irc.freenode.net.
Include the log file “config.log” produced by configure as this will help
solve the problem.
需要安装 yasm
wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
tar -zxvf yasm-1.3.0.tar.gz
cd yasm-1.3.0
./configure
make && make install
或者安装
yum install nasm -y
2、ffmpeg: error while loading shared libraries: libavdevice.so.58: cannot open shared object file: No such file or directory
export LD_LIBRARY_PATH=/usr/local/lib/
lbavdevice.so.58到底是什么东西呢?
lbavdevice是FFmpeg的一个库libavdevice库提供了用于从许多常见的多媒体输入/输出设备获取和呈现的通用框架,并支持多种输入和输出设备,包括Video4Linux2,VfW,DShow和ALSA。 .so 文件是基于Linux下的动态链接,其功能和作用类似与windows下.dll文件。
3、ffmpeg: error while loading shared libraries: libasound.so.2: cannot open shared object file: No such file or directory
yum -y install alsa-lib-devel
4、zsh: no matches found: rtmp:
setopt nonomatch
echo does-not-exist?
does-not-exist?
构建 RPM 包
centos 采用ffmpeg转码rtmp推流
错误QA
1、Failed to update header with correct duration
把ffmpeg -re -i改成ffmpeg -re -stream_loop -1 -i。让视频文件推流的时候无限循环(-stream_loop后面的数字为循环次数,写-1为无限循环)
1、av_interleaved_write_frame推流失败,AVPacket包使用注意
- 如果解码、二次处理、编码分配的AVPacket包,有引用计数器逻辑处理,av_interleaved_write_frame使用完AVPacket包,不能手动释放会导致程序奔溃;
- 建议自己分配好固定内存,进行解码、编码处理以及write_frame处理完后,调用ffmpeg提供相关处理函数就可以,中间处理期间不用考虑内存使用问题
2、av_interleaved_write_frame推流几分钟偶尔出现断流问题
- 按照当前推流情况出现以上问题是因为av_interleaved_write_frame不支持线程安全,音视频两线程同时调用该接口会导致数据错乱,最终导致rtmp推流失败异常断开
- 如果音视频pts计算不正确也会导致以上问题发生
3、音频重采用播放重叠问题
- 输入音频opus、48000、双声道,输出音频aac、44100、双声道,需要对音频进行重采样处理
- 音频解码、重采样、编码需要根据输入音频的PTS、音频编码后的数据量重新计算编码后的音频PTS,来解决音频播放堆叠问题;如下步骤:
1.将输入opus流的pts进行转化为编码pts采用av_rescale_q_rnd()接口进行转化
2.将编码当前pts加上采用变成一帧数据尾的pts,然后转化为微妙标记为mypts
3.通过swr_convert重采样、fifo进行数据缓存,调用一下av_audio_fifo_size判断一下FIFO中的音频够不够frame_size,如果不够的话,那就要再进行前面的读取,如果够的话,那就调用av_audio_fifo_read从FIFO中读取frame_size个长度的音频数据,当然因为读取frame_size后,FIFO中可能还有数据,因此要计算一下FIFO中音频数据的微秒长度,标记为restpts
4.重采样、编码后的aac编码pts=mypts-restpts
5.通过av_rescale_q_rnd()接口将aac编码pts转化为aac流pts,该pts为推rtmp流的实际pts
- 以上计算涉及了ffmpeg内部时间基(timebase)的计算:
1.ffmpeg 内部针对timebase的分层结构:
1.转码流程(如:flv格式数据-->h264/aac数据-->yuv/pcm数据-->h264/aac数据-->mp4格式数据):
1.把flv格式数据或者mp4格式数据这一层叫做mux/demux层或者复用层有些人习惯于叫做封装层;即:mux/demux层
2.把h264/aac数据这一层叫做编解码层或者codec/decode;即:codec/decode层
3.把yuv/pcm数据这一层叫做原始数据层或者Raw data层;即:Raw data层
2.通过转码流程可以总结出ffmpeg基本把数据或者说结构分为了“mux/demux层”也就是ffmpeg中的AVStream:“codec/decode层” 也就是ffmpeg中的AVCodec ;“Raw data层”这个也在AVStream 中存放着(如果是自己填写的例如ios或者android获取当前毫秒时间的可以单独放置到一个timebse的结构体中)
3.针对ffmpeg关于分层结构timebase的转换过程,转码流程里时间基转换过程:
1.针对ffmpeg关于分层结构timebase的转换过程,转码流程里时间基转换过程:
1.picture->pts = av_rescale_q_rnd(picture->pts,streams->time_base,streams->decodectext->time_base, AV_ROUND_NEAR_INF);
2.然后将decode层的timebase转换成codec层(这两个同层但也需要转换)的timebase,这里有个问题Raw data 层的timebase可以略过,可以直接从decode转换为coedec,Raw data 层的timebse在摄像头采集和播放器的时候会用到。
1.picture->pts = av_rescale_q_rnd(picture->pts,streams->decodectext->time_base,streams->codectext->time_base, AV_ROUND_NEAR_INF);
3.最后将codec层的timebase转换为mux层的timebase。
1.picture->pts = av_rescale_q_rnd(picture->pts,streams->codectext->time_base,streams->time_base,AV_ROUND_NEAR_INF)
2.具体可参考:https://blog.csdn.net/zhuweigangzwg/article/details/64919706
- 音频重采样过程pts计算,实际跟ffmpeg转码过程内部pts计算流出一样
4、音频推流音频不正常问题
- 检测音频pcm数据是否正常
- 检测音频编码后的数据是否正常
- 以上都正常,如果推rtmp流,请通过av_bitstream_filter_init("aac_adtstoasc");该接口进行去掉aac的adts头,如下:
1.将AAC编码器编码后的原始码流(ADTS头 + ES流)封装为MP4或者FLV或者MOV等格式时,需要先将ADTS头转换为MPEG-4 AudioSpecficConfig (将音频相关编解码参数提取出来),并将原始码流中的ADTS头去掉(只剩下ES流)
2.相反,从MP4或者FLV或者MOV等格式文件中解封装出AAC码流(只有ES流)时,需要在解析出的AAC码流前添加ADTS头(含音频相关编解码参数)
5、ffmpeg通过api进行rtmp推流需要注意音视频首包问题
- rtmp推流在avformat_write_header时,会向rtmp服务器发送sps/pps、aac header数据,在编码音视频时需要在编码中使能CODEC_FLAG_GLOBAL_HEADER,但调用avformat_write_header前请确保音视频全局头流包含这些数据pVCodeCtx->extradata
- 如果使能CODEC_FLAG_GLOBAL_HEADER该标志,x264每一个I帧不会携带sps/pps如果需要请手动设置添加
- 在rtc场景下,分辨率会根据网络情况进行动态调整分辨率,如刚开始视频源是个1280720,但是数据源作为数据发送方通过网络带宽统计,得知当前网络丢包严重,画面需要降质,分辨率调整为640480,接收过程中识别到视频源编码信息发生了变化,需要把编码信息变化实时更新到flv容器中,在运行期间通过AVPacket更新avc sequence header的方法,一直以为这个信息是avcodec范畴修改才能才能更新的,曾经尝试更新AVCodecContext,也没有太多处理,最终通过av_packet_new_side_data函数来增加编码信息,这个函数自动给AVPacket分配了side_data的空间,而av_packet_free_side_data函数则可以释放分配的空间
6、ffmpeg推rtmp流,长时间推流偶尔会出现RTMP_Write阻塞,在avio_open2接口增加超时回调函数,来解决阻塞问题
int PushMediaStream::CallBackIOInterrupt(void *param)
{
if(param)
{
PushMediaStream *process - (PushMediaStream *)param;
long long t -av_gettime()/1000 - process->llLasttime;
int nDuration - 2000; // 单位毫秒
if(t > nDuration)
{
ELOG_DEBUG("Interrupt write stream block");
return 1;
}
}
return 0;
}