文章目录
团队博客: 汽车电子社区
一、概述
FFmpeg中的postproc库是一个视频后处理库,用于应用各种图像处理滤镜和算法来改善和优化视频质量。
postproc库提供了一些常见的视频后处理滤镜,例如去隔行滤波、去噪滤波、锐化滤波等,可以用于增强视频的清晰度、减少噪点和伪影等。
在早期版本的 FFmpeg 中,postproc 库是默认启用的,可以通过命令行选项 -postproc 来应用后处理滤镜。但是自FFmpeg 3.2版本开始,postproc 库被标记为过时(deprecated),不再是默认启用的,而且在最新的 FFmpeg 版本中已经被移除。
现在,建议使用更现代和更强大的滤镜系统,如 libavfilter,来实现视频的后处理和滤波操作。libavfilter 提供了广泛的滤镜和处理器,可以进行更复杂和高级的视频处理。
所以,如果你使用最新版本的 FFmpeg,推荐使用 libavfilter 来进行视频后处理。
二、宏定义
#define PP_CPU_CAPS_MMX 0x80000000
#define PP_CPU_CAPS_MMX2 0x20000000
#define PP_CPU_CAPS_3DNOW 0x40000000
#define PP_CPU_CAPS_ALTIVEC 0x10000000
#define PP_CPU_CAPS_AUTO 0x00080000
#define PP_FORMAT 0x00000008
#define PP_FORMAT_420 (0x00000011|PP_FORMAT)
#define PP_FORMAT_422 (0x00000001|PP_FORMAT)
#define PP_FORMAT_411 (0x00000002|PP_FORMAT)
#define PP_FORMAT_444 (0x00000000|PP_FORMAT)
#define PP_FORMAT_440 (0x00000010|PP_FORMAT)
#define PP_PICT_TYPE_QP2 0x00000010 ///< MPEG2 style QScale
#define LIBPOSTPROC_VERSION_MINOR 4
#define LIBPOSTPROC_VERSION_MICRO 100
#define LIBPOSTPROC_VERSION_INT AV_VERSION_INT(LIBPOSTPROC_VERSION_MAJOR, \
LIBPOSTPROC_VERSION_MINOR, \
LIBPOSTPROC_VERSION_MICRO)
#define LIBPOSTPROC_VERSION AV_VERSION(LIBPOSTPROC_VERSION_MAJOR, \
LIBPOSTPROC_VERSION_MINOR, \
LIBPOSTPROC_VERSION_MICRO)
#define LIBPOSTPROC_BUILD LIBPOSTPROC_VERSION_INT
#define LIBPOSTPROC_IDENT "postproc" AV_STRINGIFY(LIBPOSTPROC_VERSION)
三、类型定义
typedef void pp_context;
typedef void pp_mode;
四、重要函数
4.1、postproc_version
unsigned postproc_version(void)
{
av_assert0(LIBPOSTPROC_VERSION_MICRO >= 100);
return LIBPOSTPROC_VERSION_INT;
}
返回LIBPOSTPROC_VERSION_INT的常量值。
4.2、postproc_configuration
返回libpostproc构建时配置。
const char *postproc_configuration(void)
{
return FFMPEG_CONFIGURATION;
}
4.3、postproc_license
返回libpostproc许可证。
const char *postproc_license(void)
{
#define LICENSE_PREFIX "libpostproc license: "
return &LICENSE_PREFIX FFMPEG_LICENSE[sizeof(LICENSE_PREFIX) - 1];
}
4.4、pp_postprocess
void pp_postprocess(const uint8_t * src[3], const int srcStride[3],
uint8_t * dst[3], const int dstStride[3],
int width, int height,
const int8_t *QP_store, int QPStride,
pp_mode *vm, void *vc, int pict_type)
{
int mbWidth = (width+15)>>4;
int mbHeight= (height+15)>>4;
PPMode *mode = vm;
PPContext *c = vc;
int minStride= FFMAX(FFABS(srcStride[0]), FFABS(dstStride[0]));
int absQPStride = FFABS(QPStride);
// c->stride and c->QPStride are always positive
if(c->stride < minStride || c->qpStride < absQPStride)
reallocBuffers(c, width, height,
FFMAX(minStride, c->stride),
FFMAX(c->qpStride, absQPStride));
if(!QP_store || (mode->lumMode & FORCE_QUANT)){
int i;
QP_store= c->forcedQPTable;
absQPStride = QPStride = 0;
if(mode->lumMode & FORCE_QUANT)
for(i=0; i<mbWidth; i++) c->forcedQPTable[i]= mode->forcedQuant;
else
for(i=0; i<mbWidth; i++) c->forcedQPTable[i]= 1;
}
if(pict_type & PP_PICT_TYPE_QP2){
int i;
const int count= FFMAX(mbHeight * absQPStride, mbWidth);
for(i=0; i<(count>>2); i++){
AV_WN32(c->stdQPTable + (i<<2), AV_RN32(QP_store + (i<<2)) >> 1 & 0x7F7F7F7F);
}
for(i<<=2; i<count; i++){
c->stdQPTable[i] = QP_store[i]>>1;
}
QP_store= c->stdQPTable;
QPStride= absQPStride;
}
if(0){
int x,y;
for(y=0; y<mbHeight; y++){
for(x=0; x<mbWidth; x++){
av_log(c, AV_LOG_INFO, "%2d ", QP_store[x + y*QPStride]);
}
av_log(c, AV_LOG_INFO, "\n");
}
av_log(c, AV_LOG_INFO, "\n");
}
if((pict_type&7)!=3){
if (QPStride >= 0){
int i;
const int count= FFMAX(mbHeight * QPStride, mbWidth);
for(i=0; i<(count>>2); i++){
AV_WN32(c->nonBQPTable + (i<<2), AV_RN32(QP_store + (i<<2)) & 0x3F3F3F3F);
}
for(i<<=2; i<count; i++){
c->nonBQPTable[i] = QP_store[i] & 0x3F;
}
} else {
int i,j;
for(i=0; i<mbHeight; i++) {
for(j=0; j<absQPStride; j++) {
c->nonBQPTable[i*absQPStride+j] = QP_store[i*QPStride+j] & 0x3F;
}
}
}
}
av_log(c, AV_LOG_DEBUG, "using npp filters 0x%X/0x%X\n",
mode->lumMode, mode->chromMode);
postProcess(src[0], srcStride[0], dst[0], dstStride[0],
width, height, QP_store, QPStride, 0, mode, c);
if (!(src[1] && src[2] && dst[1] && dst[2]))
return;
width = (width )>>c->hChromaSubSample;
height = (height)>>c->vChromaSubSample;
if(mode->chromMode){
postProcess(src[1], srcStride[1], dst[1], dstStride[1],
width, height, QP_store, QPStride, 1, mode, c);
postProcess(src[2], srcStride[2], dst[2], dstStride[2],
width, height, QP_store, QPStride, 2, mode, c);
}
else if(srcStride[1] == dstStride[1] && srcStride[2] == dstStride[2]){
linecpy(dst[1], src[1], height, srcStride[1]);
linecpy(dst[2], src[2], height, srcStride[2]);
}else{
int y;
for(y=0; y<height; y++){
memcpy(&(dst[1][y*dstStride[1]]), &(src[1][y*srcStride[1]]), width);
memcpy(&(dst[2][y*dstStride[2]]), &(src[2][y*srcStride[2]]), width);
}
}
}
4.5、pp_get_mode_by_name_and_quality
如果发生错误,返回一个pp_mode或NULL。
pp_mode *pp_get_mode_by_name_and_quality(const char *name, int quality)
{
char temp[GET_MODE_BUFFER_SIZE];
char *p= temp;
static const char filterDelimiters[] = ",/";
static const char optionDelimiters[] = ":|";
struct PPMode *ppMode;
char *filterToken;
if (!name) {
av_log(NULL, AV_LOG_ERROR, "pp: Missing argument\n");
return NULL;
}
if (!strcmp(name, "help")) {
const char *p;
for (p = pp_help; strchr(p, '\n'); p = strchr(p, '\n') + 1) {
av_strlcpy(temp, p, FFMIN(sizeof(temp), strchr(p, '\n') - p + 2));
av_log(NULL, AV_LOG_INFO, "%s", temp);
}
return NULL;
}
ppMode= av_malloc(sizeof(PPMode));
if (!ppMode)
return NULL;
ppMode->lumMode= 0;
ppMode->chromMode= 0;
ppMode->maxTmpNoise[0]= 700;
ppMode->maxTmpNoise[1]= 1500;
ppMode->maxTmpNoise[2]= 3000;
ppMode->maxAllowedY= 234;
ppMode->minAllowedY= 16;
ppMode->baseDcDiff= 256/8;
ppMode->flatnessThreshold= 56-16-1;
ppMode->maxClippedThreshold= (AVRational){1,100};
ppMode->error=0;
memset(temp, 0, GET_MODE_BUFFER_SIZE);
av_strlcpy(temp, name, GET_MODE_BUFFER_SIZE - 1);
av_log(NULL, AV_LOG_DEBUG, "pp: %s\n", name);
for(;;){
const char *filterName;
int q= 1000000; //PP_QUALITY_MAX;
int chrom=-1;
int luma=-1;
const char *option;
const char *options[OPTIONS_ARRAY_SIZE];
int i;
int filterNameOk=0;
int numOfUnknownOptions=0;
int enable=1; //does the user want us to enabled or disabled the filter
char *tokstate;
filterToken= av_strtok(p, filterDelimiters, &tokstate);
if(!filterToken) break;
p+= strlen(filterToken) + 1; // p points to next filterToken
filterName= av_strtok(filterToken, optionDelimiters, &tokstate);
if (!filterName) {
ppMode->error++;
break;
}
av_log(NULL, AV_LOG_DEBUG, "pp: %s::%s\n", filterToken, filterName);
if(*filterName == '-'){
enable=0;
filterName++;
}
for(;;){ //for all options
option= av_strtok(NULL, optionDelimiters, &tokstate);
if(!option) break;
av_log(NULL, AV_LOG_DEBUG, "pp: option: %s\n", option);
if(!strcmp("autoq", option) || !strcmp("a", option)) q= quality;
else if(!strcmp("nochrom", option) || !strcmp("y", option)) chrom=0;
else if(!strcmp("chrom", option) || !strcmp("c", option)) chrom=1;
else if(!strcmp("noluma", option) || !strcmp("n", option)) luma=0;
else{
options[numOfUnknownOptions] = option;
numOfUnknownOptions++;
}
if(numOfUnknownOptions >= OPTIONS_ARRAY_SIZE-1) break;
}
options[numOfUnknownOptions] = NULL;
/* replace stuff from the replace Table */
for(i=0; replaceTable[2*i]; i++){
if(!strcmp(replaceTable[2*i], filterName)){
size_t newlen = strlen(replaceTable[2*i + 1]);
int plen;
int spaceLeft;
p--, *p=',';
plen= strlen(p);
spaceLeft= p - temp + plen;
if(spaceLeft + newlen >= GET_MODE_BUFFER_SIZE - 1){
ppMode->error++;
break;
}
memmove(p + newlen, p, plen+1);
memcpy(p, replaceTable[2*i + 1], newlen);
filterNameOk=1;
}
}
for(i=0; filters[i].shortName; i++){
if( !strcmp(filters[i].longName, filterName)
|| !strcmp(filters[i].shortName, filterName)){
ppMode->lumMode &= ~filters[i].mask;
ppMode->chromMode &= ~filters[i].mask;
filterNameOk=1;
if(!enable) break; // user wants to disable it
if(q >= filters[i].minLumQuality && luma)
ppMode->lumMode|= filters[i].mask;
if(chrom==1 || (chrom==-1 && filters[i].chromDefault))
if(q >= filters[i].minChromQuality)
ppMode->chromMode|= filters[i].mask;
if(filters[i].mask == LEVEL_FIX){
int o;
ppMode->minAllowedY= 16;
ppMode->maxAllowedY= 234;
for(o=0; options[o]; o++){
if( !strcmp(options[o],"fullyrange")
||!strcmp(options[o],"f")){
ppMode->minAllowedY= 0;
ppMode->maxAllowedY= 255;
numOfUnknownOptions--;
}
}
}
else if(filters[i].mask == TEMP_NOISE_FILTER)
{
int o;
int numOfNoises=0;
for(o=0; options[o]; o++){
char *tail;
ppMode->maxTmpNoise[numOfNoises]=
strtol(options[o], &tail, 0);
if(tail!=options[o]){
numOfNoises++;
numOfUnknownOptions--;
if(numOfNoises >= 3) break;
}
}
}
else if(filters[i].mask == V_DEBLOCK || filters[i].mask == H_DEBLOCK
|| filters[i].mask == V_A_DEBLOCK || filters[i].mask == H_A_DEBLOCK){
int o;
for(o=0; options[o] && o<2; o++){
char *tail;
int val= strtol(options[o], &tail, 0);
if(tail==options[o]) break;
numOfUnknownOptions--;
if(o==0) ppMode->baseDcDiff= val;
else ppMode->flatnessThreshold= val;
}
}
else if(filters[i].mask == FORCE_QUANT){
int o;
ppMode->forcedQuant= 15;
for(o=0; options[o] && o<1; o++){
char *tail;
int val= strtol(options[o], &tail, 0);
if(tail==options[o]) break;
numOfUnknownOptions--;
ppMode->forcedQuant= val;
}
}
}
}
if(!filterNameOk) ppMode->error++;
ppMode->error += numOfUnknownOptions;
}
av_log(NULL, AV_LOG_DEBUG, "pp: lumMode=%X, chromMode=%X\n", ppMode->lumMode, ppMode->chromMode);
if(ppMode->error){
av_log(NULL, AV_LOG_ERROR, "%d errors in postprocess string \"%s\"\n", ppMode->error, name);
av_free(ppMode);
return NULL;
}
return ppMode;
}