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等。

参考资料

FFmpeg filter简介

https://www.ffmpeg.org/doxygen/3.2/vf__scale__npp_8c_source.html

How can I convert an FFmpeg AVFrame with pixel format AV_PIX_FMT_CUDA to a new AVFrame with pixel format AV_PIX_FMT_RGB

ffmpeg + cuda(cuvid) 硬解码+像素格式转换(cpu主导)

原文地址:https://www.cnblogs.com/scw2901/p/14925790.html