当前位置 博文首页 > wanggao的专栏:ffmpeg学习 (18)AVUtil库常见例子(AVLog、AVO

    wanggao的专栏:ffmpeg学习 (18)AVUtil库常见例子(AVLog、AVO

    作者:[db:作者] 时间:2021-09-09 09:49

    本文参考雷神博客。FFmpeg的libavutil中几种工具函数的使用方法:

    • AVLog:日志输出
    • AVOption (AVClass):选项设置
    • AVDictionary:键值对存储

    1、AVLog

    AVLog是FFmpeg的日志输出工具。在FFmpeg中所有的日志输出不是通过printf()函数而是通过av_log()函数。av_log()会最终调用fprintf(stderr,…)函数将日志内容输出到命令行界面上。

    1.1、自定义log回调函数

    在一些非命令行程序(MFC程序,Android程序等)中,av_log()调用的fprintf(stderr,…)就无法将日志内容显示出来了。对于这种情况,FFmpeg提供了日志回调函数av_log_set_callback()。该函数可以指定一个自定义的日志输出函数,将日志输出到指定的位置。

    下面的自定义函数custom_output()将日志输出到了“simplest_ffmpeg_log.txt”文本中。

    void custom_output(void* ptr, int level, const char* fmt,va_list vl){
    	FILE *fp = fopen("simplest_ffmpeg_log.txt","a+");   
    	if(fp){   
    		vfprintf(fp,fmt,vl);
    		fflush(fp);
    		fclose(fp);
    	}   
    }
    
    int main(int argc, char* argv[])
    {
    	av_log_set_callback(custom_output);
    	return 0;
    }
    

    1.2、日志输出

    日志信息从重到轻分为Panic、Fatal、Error、Warning、Info、Verbose、Debug几个级别,输出到控制台的颜色也不同。下面的函数输出了几种不同级别的日志。

    void test_log(){
    	av_register_all();
    	AVFormatContext *obj=NULL;
    	obj=avformat_alloc_context();
    	printf("====================================\n");
    	av_log(obj,AV_LOG_PANIC,"Panic: Something went really wrong and we will crash now.\n");
    	av_log(obj,AV_LOG_FATAL,"Fatal: Something went wrong and recovery is not possible.\n");
    	av_log(obj,AV_LOG_ERROR,"Error: Something went wrong and cannot losslessly be recovered.\n");
    	av_log(obj,AV_LOG_WARNING,"Warning: This may or may not lead to problems.\n");
    	av_log(obj,AV_LOG_INFO,"Info: Standard information.\n");
    	av_log(obj,AV_LOG_VERBOSE,"Verbose: Detailed information.\n");
    	av_log(obj,AV_LOG_DEBUG,"Debug: Stuff which is only useful for libav* developers.\n");
    	printf("====================================\n");
    	avformat_free_context(obj);
    }
    

    av_log函数要求第一个参数为以指向一个AVClass类型的指针,若没有可以设置为NULL。

    日志输出源代码、颜色设置等,参见 FFmpeg源代码简单分析:日志输出系统(av_log()等)。

    2、AVOption (AVClass)

    AVOption是FFmpeg的选项设置工具。与AVOption最相关的选项设置函数就是av_opt_set()了。AVOption的核心概念就是“根据字符串操作结构体的属性值”

    有关AVOption函数说明见 libavutil\opt.h ,对于libx264提供的codec选项可以参见libavcodec\libx264.c。注意不同平台下不同实现option可能不尽一致。见博文 。。。。。option设置。。。。。。。。。

    (1)选项设值

    对于AVCodecContext类型,可以使用成员方式访问直接设置,也可以使用av_opt_set_xxx系列函数设置。例如下面代码中“#if”和“#else”之间代码的作用和“#else”和“#endif”之间代码的作用是一样的。

    #if TEST_OPT
    	av_opt_set(pCodecCtx,"b","400000",0);		//bitrate
    	//Another method
    	//av_opt_set_int(pCodecCtx,"b",400000,0);	//bitrate
    	
    	av_opt_set(pCodecCtx,"time_base","1/25",0);	//time_base
    	av_opt_set(pCodecCtx,"bf","5",0);			//max b frame
    	av_opt_set(pCodecCtx,"g","25",0);			//gop
    	av_opt_set(pCodecCtx,"qmin","10",0);		//qmin/qmax
    	av_opt_set(pCodecCtx,"qmax","51",0);
    #else
    	pCodecCtx->time_base.num = 1;  
    	pCodecCtx->time_base.den = 25;  
    	pCodecCtx->max_b_frames=5;
    	pCodecCtx->bit_rate = 400000;  
    	pCodecCtx->gop_size=25;
    	pCodecCtx->qmin = 10;
    	pCodecCtx->qmax = 51;
    #endif
    

    (2)选项取值

    同理,av_opt_get()可以将结构体的属性值以字符串的形式返回回来。

    // 使用 char val_str[50]; 貌似无效
    char *val_str=(char *)av_malloc(50);   
     
    //preset: ultrafast, superfast, veryfast, faster, fast, 
    //medium, slow, slower, veryslow, placebo
    av_opt_set(pCodecCtx->priv_data,"preset","slow",0);
    //tune: film, animation, grain, stillimage, psnr, 
    //ssim, fastdecode, zerolatency
    av_opt_set(pCodecCtx->priv_data,"tune","zerolatency",0);
    //profile: baseline, main, high, high10, high422, high444
    av_opt_set(pCodecCtx->priv_data,"profile","main",0);
     
    //print
    av_opt_get(pCodecCtx->priv_data,"preset",0,(uint8_t **)&val_str);
    printf("preset val: %s\n",val_str);
    av_opt_get(pCodecCtx->priv_data,"tune",0,(uint8_t **)&val_str);
    printf("tune val: %s\n",val_str);
    av_opt_get(pCodecCtx->priv_data,"profile",0,(uint8_t **)&val_str);
    printf("profile val: %s\n",val_str);
    
    av_free(val_str);
    

    对于非字符串选项,如int类型,可以使用av_opt_get_int函数,其他类型类似。例如

    int64_t gop;
    av_opt_get_int(pCodecCtx->priv_data,"g",0,&gop);
    printf("gop val: %lld\n",gop);
    

    (3)选项查找

    可以通过av_opt_find()获取结构体中任意选项的AVOption结构体,并打印AVOption的值(亦可以通过调试查看)。

    void print_opt(const AVOption *opt_test){
     
    	printf("====================================\n");
    	printf("Option Information:\n");
    	printf("[name]%s\n",opt_test->name);
    	printf("[help]%s\n",opt_test->help);
    	printf("[offset]%d\n",opt_test->offset);
     
    	switch(opt_test->type){
    	case AV_OPT_TYPE_INT:{
    		printf("[type]int\n[default]%d\n",opt_test->default_val.i64);
    		break;
    		}
    	case AV_OPT_TYPE_INT64:{
    		printf("[type]int64\n[default]%lld\n",opt_test->default_val.i64);
    		break;
    		}
    	case AV_OPT_TYPE_FLOAT:{
    		printf("[type]float\n[default]%f\n",opt_test->default_val.dbl);
    		break;
    		}
    	case AV_OPT_TYPE_STRING:{
    		printf("[type]string\n[default]%s\n",opt_test->default_val.str);
    		break;
    		}
    	case AV_OPT_TYPE_RATIONAL:{
    		printf("[type]rational\n[default]%d/%d\n",opt_test->default_val.q.num,opt_test->default_val.q.den);
    		break;
    		}
    	default:{
    		printf("[type]others\n");
    		break;
    		}
    	}
     
    	printf("[max val]%f\n",opt_test->max);
    	printf("[min val]%f\n",opt_test->min);
     
    	if(opt_test->flags&AV_OPT_FLAG_ENCODING_PARAM){
    		printf("Encoding param.\n");
    	}
    	if(opt_test->flags&AV_OPT_FLAG_DECODING_PARAM){
    		printf("Decoding param.\n");
    	}
    	if(opt_test->flags&AV_OPT_FLAG_AUDIO_PARAM){
    		printf("Audio param.\n");
    	}
    	if(opt_test->flags&AV_OPT_FLAG_VIDEO_PARAM){
    		printf("Video param.\n");
    	}
    	if(opt_test->unit!=NULL)
    		printf("Unit belong to:%s\n",opt_test->unit);
     
    	printf("====================================\n");
    }
    
    
    void test()