团队博客: 汽车电子社区


一、概述

  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;
}
01-21 13:36