FFmpeg-filter
概述
FFmpeg一共包含8个库:
- avcodec:编解码(最重要的库)。
- avformat:封装格式处理。
- avfilter:滤镜特效处理。
- avdevice:各种设备的输入输出。
- avutil:工具库(大部分库都需要这个库的支持)。
- postproc:后加工。
- swresample:音频采样数据格式转换。
- swscale:视频像素数据格式转换。
FFmpeg重要数据结构:
-
AVFormatContext
封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关。 -
AVInputFormat
每种封装格式(例如FLV, MKV, MP4, AVI)对应一个该结构体。 -
AVStream
视频文件中每个视频(音频)流对应一个该结构体。 -
AVCodecContext
编码器上下文结构体,保存了视频(音频)编解码相关信息。 -
AVCodec
每种视频(音频)编解码器(例如H.264解码器)对应一个该结构体。 -
AVPacket
存储一帧压缩编码数据。 -
AVFrame
存储一帧解码后像素(采样)数据。data:解码后的图像像素数据(音频采样数据)
linesize:对视频来说是图像中一行像素的大小;对音频来说是整个音频帧的大小。
width, height:图像的宽高(只针对视频)
key_frame:是否为关键帧(只针对视频)
FFmpeg中filter分为:
- source filter (只有输出)
- audio filter
- video filter
- Multimedia filter
- sink filter (只有输入)
除了source和sink filter,其他filter都至少有一个输入、至少一个输出。下面是一个例子,使用filter实现宽高减半显示:
ffplay sample.rmvb -vf scale=iw/2:ih/2
经典的 filter
音频 filter
过滤器 描述
adelay 实现不同声道的延时处理。使用参数如adelay=1500|0|500,这个例子中实现了第一个声道的延迟 1.5
aecho 实现回声效果
amerge 将多个音频流合并成一个多声道音频流
ashowinfo 显示每一个 audio frame 信息
pan 特定声道处理,比如立体声变为单声道,或者通过特定参数修改声道或交换声道。主要有两大类:
混音处理,比如下面的例子pan=1c|c0=0.9*c0+0.1*c1,实现立体声到单声道的变换;
声道变换,比如5.1声道顺序调整,pan=“5.1 | c0=c1 | c1=c0 | c2=c2 | c3=c3 | c4=c4 | c5=c5”
silencedetech 和 silenceremove 根据特定参数检测静音和移除静音
volume 和 volumedetect 这两个filter分别实现音量调整和音量检测
视频 filter
参数 描述
blend tblend 将两帧视频合并为一帧
crop 按照特定分辨率裁剪输入视频
drawbox drawgrid drawtext 绘制 box(对话框) grid(表格) text(文本)
fps 按照指定帧率输出视频帧(丢帧或复制)
hflip vflip 水平和垂直镜像
overlay 视频叠加
rotate 视频任意角度旋转
showinfo 显示视频的参数信息,比如时间戳,采样格式,帧类型等
subtitles 使用 libass 库绘制 subtitle(字幕)
transpose 图像转置
scale 使用 libswscale 库完成视频缩放
thumbnail 提取缩略图
多媒体 filter
参数 描述
ahistogram 将音频转换为视频输出,并显示为音量的直方图
concat 将音频流、视频流拼接成一个
metadata ametadata 操作 metadata 信息
setpts asetpts 改变输入音频帧或视频帧的 pts
showfreqs showspectrum showspertrumpic showvolume showwaves 将输入音频转换为视频显示,并显示频谱、音量等信息
split asplit 将输入切分为多个相同的输出
movie amovie 从 movie 容器中读取音频或视频
npp scale
scale_npp: This is a scaling filter implemented in NVIDIA's Performance Primitives. It's primary dependency is the CUDA SDK, and it must be explicitly enabled by passing --enable-libnpp, --enable-cuda-nvcc and --enable-nonfree flags to ./configure at compile time when building FFmpeg from source. Use this filter in place of scale_cuda wherever possible.
npp像素格式转换命令行用法:
ffmpeg -vsync 0 -hwaccel_device 2 -hwaccel cuda -hwaccel_output_format cuda -i ~/vedio/drone1.flv -vf "scale_npp=format=yuv420p,hwdownload,format=yuv420p" ff22cuda2.yuv
对应源码文件是 libavfilter/vf_scale_npp.c
static const enum AVPixelFormat supported_formats[] = {
AV_PIX_FMT_YUV420P,
AV_PIX_FMT_NV12,
AV_PIX_FMT_YUV444P,
};
像素格式相关
bitdepth和bpp(bits per pixel)
bitdepth是指每个通道的每个像素分量的有效比特数,它越高,表示该图片格式能表示的总颜色数上限越高。bitdepth = 总有效比特数/(所有通道的总像素分量数目).
bpp(bits per pixel)是指每个像素所占用的有效比特数(忽略通道),bpp = 总有效比特数/总像素数目 = 总有效比特数/(W*H).
①对于YUV420(bitdepth=8)的图片,
图片的总像素数为W*H,其中Y通道有W*H个像素分量,U通道有W*H/4个像素分量,V通道有W*H/4个像素分量,
因此,总有效比特数= W*H*8+W*H/4*8+W*H/4*8 = W*H*12,
bpp = 总有效比特数/总像素数目 = (W*H*12)/(W*H) = 12.
②对于YUV420(bitdepth=10)的图片,
图片的总像素数为W*H,其中Y通道有W*H个像素分量,U通道有W*H/4个像素分量,V通道有W*H/4个像素分量,
虽然每个像素占10bit(有效比特数),但是需要用16bit即2字节(实际占用的比特数)来存储,
因此,总有效比特数= W*H*10+W*H/4*10+W*H/4*10 = W*H*15,
bpp = 总有效比特数/总像素数目 = (W*H*15)/(W*H) = 15.
③对于RGB24格式(bitdepth=8)的图片,
图片的总像素数为W*H,而R/G/B三个通道都是W*H个像素分量,
因此,总有效比特数= W*H*8*3,
bpp = 总有效比特数/总像素数目 = (W*H*8*3)/(W*H) = 24
④对于RGB555格式(bitdepth=5)的图片,
每个像素15bit = 5bit的R+5bit的G+5bit的B,
总有效比特数= W*H*5*3,
bpp = 总有效比特数/总像素数目 = (W*H*5*3)/(W*H) = 15
YUV420p和YUVJ420P的区别
YUVJ420P的字面含义是“使用了JPEG颜色范围的YUV420P,像素使用表示颜色的数值范围发生了变化。
YUV420p的像素颜色范围是[16,235],16表示黑色,235表示白色
YUVJ420P的像素颜色范围是[0,255]。0表示黑色,255表示白色
从这里来看,貌似是YUVJ420P表示的更准确一下。
区别的缘由, YUV420p对应的是电视。YUVJ420P对应的是显示器。
yuvj420p和yuv420p格式上是一致的,只是颜色空间上的不同。
FFmpeg常用像素格式
AV_PIX_FMT_YUV420P = 0, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
AV_PIX_FMT_RGB24 = 2, ///< packed RGB 8:8:8, 24bpp, RGBRGB...
AV_PIX_FMT_BGR24 = 3, ///< packed RGB 8:8:8, 24bpp, BGRBGR...
AV_PIX_FMT_YUVJ420P = 12, ///< planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting color_range
AV_PIX_FMT_NV12 = 23, ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V)
AV_PIX_FMT_ARGB = 25, ///< packed ARGB 8:8:8:8, 32bpp, ARGBARGB...
/**
* HW acceleration through CUDA. data[i] contain CUdeviceptr pointers exactly as for system memory frames.
*/
AV_PIX_FMT_CUDA = 119,
帧转码(像素格式转换)
解码得到的帧格式是YUV格式的,具体格式存放在AVFrame的format(类型为int)成员中,打印出数值后,再到AVPixelFormat中查找具体是哪个格式。
一般而言,大多是实际使用场景中,最常用的是RGB格式,因此接下来就以RGB举例说明如何做帧转码。注意,其他格式的做法也是一样的。
核心是调用int sws_scale(struct SwsContext* c, ...),该函数接受的参数有一大堆,具体参数和对应的含义建议查询官网,该函数主要做了尺寸缩放(scale)和转码(transcode)工作,源码阅读推荐雷神的FFmpeg源代码简单分析:libswscale的sws_scale()。
第一个参数struct SwsContext* c,需要调用struct SwsContext* sws_getContext(..., enum AVPixelFormat dstFormat, ...)创建,该函数也是一堆参数,请自行官网查询,其中参数enum AVPixelFormat dstFormat,指定了目标格式,随后调用sws_scale()后得到的目标帧就是dstFormat格式的。
因此,如果你的目标格式是RGB,只需要指定dstFormat为需要的RGB类型即可,FFMPEG中的RGB系列的有AV_PIX_FMT_RGB24、AV_PIX_FMT_ARGB等。
参考资料
https://www.ffmpeg.org/doxygen/3.2/vf__scale__npp_8c_source.html
ffmpeg + cuda(cuvid) 硬解码+像素格式转换(cpu主导)