ffmpeg实战教程(五)libswscale,libavfilter实践指南

xiaoxiao2021-02-28  105

1.libswscale实现YUV转RGB

libswscale里面实现了各种图像像素格式的转换。

libswscale使用起来很方便,最主要的函数只有3个: (1) sws_getContext():使用参数初始化SwsContext结构体。 (2) sws_scale():转换一帧图像。 (3) sws_freeContext():释放SwsContext结构体。 其中sws_getContext()也可以用另一个接口函数sws_getCachedContext()取代。

初始化方法

初始化SwsContext除了调用sws_getContext()之外还有另一种方法,更加灵活,可以配置更多的参数。该方法调用的函数如下所示。 (1) sws_alloc_context():为SwsContext结构体分配内存。 (2) av_opt_set_XXX():通过av_opt_set_int(),av_opt_set()…等等一系列方法设置SwsContext结构体的值。在这里需要注意,SwsContext结构体的定义看不到,所以不能对其中的成员变量直接进行赋值,必须通过av_opt_set()这类的API才能对其进行赋值。 (3) sws_init_context():初始化SwsContext结构体。 这种复杂的方法可以配置一些sws_getContext()配置不了的参数。比如说设置图像的YUV像素的取值范围是JPEG标准(Y、U、V取值范围都是0-255)还是MPEG标准(Y取值范围是16-235,U、V的取值范围是16-240)。

通过av_pix_fmt_desc_get()可以获得指定像素格式的AVPixFmtDescriptor结构体。 通过AVPixFmtDescriptor结构体可以获得不同像素格式的一些信息。例如av_get_bits_per_pixel(),通过该函数可以获得指定像素格式每个像素占用的比特数(Bit Per Pixel)。

图像拉伸 SWS_BICUBIC性能比较好;SWS_FAST_BILINEAR在性能和速度之间有一个比好好的平衡。

下面看一下运行效果图: 源代码:

#include <stdio.h> #define __STDC_CONSTANT_MACROS #ifdef _WIN32 //Windows extern "C" { #include "libswscale/swscale.h" #include "libavutil/opt.h" #include "libavutil/imgutils.h" }; #else //Linux... #ifdef __cplusplus extern "C" { #endif #include <libswscale/swscale.h> #include <libavutil/opt.h> #include <libavutil/imgutils.h> #ifdef __cplusplus }; #endif #endif int main(int argc, char* argv[]) { //Parameters FILE *src_file =fopen("ws.yuv", "rb"); const int src_w=1920,src_h=1080; AVPixelFormat src_pixfmt=AV_PIX_FMT_YUV420P; //该函数可以获得指定像素格式每个像素占用的比特数(Bit Per Pixel),av_pix_fmt_desc_get()可以获得指定像素格式的AVPixFmtDescriptor结构体。 int src_bpp=av_get_bits_per_pixel(av_pix_fmt_desc_get(src_pixfmt)); FILE *dst_file = fopen("ws.rgb", "wb"); const int dst_w=1280,dst_h=720; AVPixelFormat dst_pixfmt=AV_PIX_FMT_RGB24; int dst_bpp=av_get_bits_per_pixel(av_pix_fmt_desc_get(dst_pixfmt));//av_pix_fmt_desc_get()可以获得指定像素格式的AVPixFmtDescriptor结构体。 //Structures uint8_t *src_data[4]; int src_linesize[4]; uint8_t *dst_data[4]; int dst_linesize[4]; int rescale_method=SWS_BICUBIC; struct SwsContext *img_convert_ctx; uint8_t *temp_buffer=(uint8_t *)malloc(src_w*src_h*src_bpp/8); int frame_idx=0; int ret=0; ret= av_image_alloc(src_data, src_linesize,src_w, src_h, src_pixfmt, 1);//分配资源控件 if (ret< 0) { printf( "Could not allocate source image\n"); return -1; } ret = av_image_alloc(dst_data, dst_linesize,dst_w, dst_h, dst_pixfmt, 1); if (ret< 0) { printf( "Could not allocate destination image\n"); return -1; } //----------------------------- img_convert_ctx =sws_alloc_context();//为SwsContext结构体分配内存。 //Show AVOption av_opt_show2(img_convert_ctx,stdout,AV_OPT_FLAG_VIDEO_PARAM,0); //Set Value av_opt_set_int(img_convert_ctx,"sws_flags",SWS_BICUBIC|SWS_PRINT_INFO,0); av_opt_set_int(img_convert_ctx,"srcw",src_w,0); av_opt_set_int(img_convert_ctx,"srch",src_h,0); av_opt_set_int(img_convert_ctx,"src_format",src_pixfmt,0); //'0' for MPEG (Y:0-235);'1' for JPEG (Y:0-255) av_opt_set_int(img_convert_ctx,"src_range",1,0); av_opt_set_int(img_convert_ctx,"dstw",dst_w,0); av_opt_set_int(img_convert_ctx,"dsth",dst_h,0); av_opt_set_int(img_convert_ctx,"dst_format",dst_pixfmt,0); av_opt_set_int(img_convert_ctx,"dst_range",1,0); sws_init_context(img_convert_ctx,NULL,NULL);//对SwsContext中的各种变量进行赋值 while(1) { if (fread(temp_buffer, 1, src_w*src_h*src_bpp/8, src_file) != src_w*src_h*src_bpp/8){ break; } switch(src_pixfmt){ case AV_PIX_FMT_GRAY8:{ memcpy(src_data[0],temp_buffer,src_w*src_h); break; } case AV_PIX_FMT_YUV420P:{ memcpy(src_data[0],temp_buffer,src_w*src_h); //Y memcpy(src_data[1],temp_buffer+src_w*src_h,src_w*src_h/4); //U memcpy(src_data[2],temp_buffer+src_w*src_h*5/4,src_w*src_h/4); //V break; } case AV_PIX_FMT_YUV422P:{ memcpy(src_data[0],temp_buffer,src_w*src_h); //Y memcpy(src_data[1],temp_buffer+src_w*src_h,src_w*src_h/2); //U memcpy(src_data[2],temp_buffer+src_w*src_h*3/2,src_w*src_h/2); //V break; } case AV_PIX_FMT_YUV444P:{ memcpy(src_data[0],temp_buffer,src_w*src_h); //Y memcpy(src_data[1],temp_buffer+src_w*src_h,src_w*src_h); //U memcpy(src_data[2],temp_buffer+src_w*src_h*2,src_w*src_h); //V break; } case AV_PIX_FMT_YUYV422:{ memcpy(src_data[0],temp_buffer,src_w*src_h*2); //Packed break; } case AV_PIX_FMT_RGB24:{ memcpy(src_data[0],temp_buffer,src_w*src_h*3); //Packed break; } default:{ printf("Not Support Input Pixel Format.\n"); break; } } sws_scale(img_convert_ctx, src_data, src_linesize, 0, src_h, dst_data, dst_linesize);//转换像素 printf("Finish process frame ]\n",frame_idx); frame_idx++; switch(dst_pixfmt){ case AV_PIX_FMT_GRAY8:{ fwrite(dst_data[0],1,dst_w*dst_h,dst_file); break; } case AV_PIX_FMT_YUV420P:{ fwrite(dst_data[0],1,dst_w*dst_h,dst_file); //Y fwrite(dst_data[1],1,dst_w*dst_h/4,dst_file); //U fwrite(dst_data[2],1,dst_w*dst_h/4,dst_file); //V break; } case AV_PIX_FMT_YUV422P:{ fwrite(dst_data[0],1,dst_w*dst_h,dst_file); //Y fwrite(dst_data[1],1,dst_w*dst_h/2,dst_file); //U fwrite(dst_data[2],1,dst_w*dst_h/2,dst_file); //V break; } case AV_PIX_FMT_YUV444P:{ fwrite(dst_data[0],1,dst_w*dst_h,dst_file); //Y fwrite(dst_data[1],1,dst_w*dst_h,dst_file); //U fwrite(dst_data[2],1,dst_w*dst_h,dst_file); //V break; } case AV_PIX_FMT_YUYV422:{ fwrite(dst_data[0],1,dst_w*dst_h*2,dst_file); //Packed break; } case AV_PIX_FMT_RGB24:{ fwrite(dst_data[0],1,dst_w*dst_h*3,dst_file); //Packed break; } default:{ printf("Not Support Output Pixel Format.\n"); break; } } } sws_freeContext(img_convert_ctx); free(temp_buffer); fclose(dst_file); av_freep(&src_data[0]); av_freep(&dst_data[0]); return 0; }

2.使用libavfilter为视音频添加特效功能

关键函数如下所示:

avfilter_register_all():注册所有AVFilter。 avfilter_graph_alloc():为FilterGraph分配内存。 avfilter_graph_create_filter():创建并向FilterGraph中添加一个Filter。 avfilter_graph_parse_ptr():将一串通过字符串描述的Graph添加到FilterGraph中。 avfilter_graph_config():检查FilterGraph的配置。 av_buffersrc_add_frame():向FilterGraph中加入一个AVFrame。 av_buffersink_get_frame():从FilterGraph中取出一个AVFrame。

程序中提供了几种特效:

//const char *filter_descr = "lutyuv='u=128:v=128'"; //const char *filter_descr = "boxblur"; //const char *filter_descr = "hflip"; //const char *filter_descr = "hue='h=60:s=-3'"; //const char *filter_descr = "crop=2/3*in_w:2/3*in_h"; //const char *filter_descr = "drawbox=x=100:y=100:w=100:h=100:color=pink@0.5"; const char *filter_descr = "drawtext=fontfile=arial.ttf:fontcolor=red:fontsize=50:text='Shuo.Wang'";

输入ws.yuv,输出ws_output.yuv

原生效果如下:

const char *filter_descr = “lutyuv=’u=128:v=128’”;

const char *filter_descr = “drawtext=fontfile=arial.ttf:fontcolor=red:fontsize=50:text=’Shuo.Wang’”;

源代码如下:

#include <stdio.h> #define __STDC_CONSTANT_MACROS #ifdef _WIN32 #define snprintf _snprintf //Windows extern "C" { #include "libavfilter/avfiltergraph.h" #include "libavfilter/buffersink.h" #include "libavfilter/buffersrc.h" #include "libavutil/avutil.h" #include "libavutil/imgutils.h" }; #else //Linux... #ifdef __cplusplus extern "C" { #endif #include <libavfilter/avfiltergraph.h> #include <libavfilter/buffersink.h> #include <libavfilter/buffersrc.h> #include <libavutil/avutil.h> #include <libavutil/imgutils.h> #ifdef __cplusplus }; #endif #endif int main(int argc, char* argv[]) { int ret; AVFrame *frame_in; AVFrame *frame_out; unsigned char *frame_buffer_in; unsigned char *frame_buffer_out; AVFilterContext *buffersink_ctx; AVFilterContext *buffersrc_ctx; AVFilterGraph *filter_graph; static int video_stream_index = -1; //Input YUV FILE *fp_in=fopen("ws.yuv","rb+"); if(fp_in==NULL){ printf("Error open input file.\n"); return -1; } int in_width=1920; int in_height=1080; //Output YUV FILE *fp_out=fopen("ws_output.yuv","wb+"); if(fp_out==NULL){ printf("Error open output file.\n"); return -1; } //const char *filter_descr = "lutyuv='u=128:v=128'"; //const char *filter_descr = "boxblur"; //const char *filter_descr = "hflip"; //const char *filter_descr = "hue='h=60:s=-3'"; //const char *filter_descr = "crop=2/3*in_w:2/3*in_h"; //const char *filter_descr = "drawbox=x=100:y=100:w=100:h=100:color=pink@0.5"; const char *filter_descr = "drawtext=fontfile=arial.ttf:fontcolor=red:fontsize=50:text='Shuo.Wang'"; avfilter_register_all();//注册所有AVFilter。 char args[512]; AVFilter *buffersrc = avfilter_get_by_name("buffer"); AVFilter *buffersink = avfilter_get_by_name("ffbuffersink"); AVFilterInOut *outputs = avfilter_inout_alloc(); AVFilterInOut *inputs = avfilter_inout_alloc(); enum PixelFormat pix_fmts[] = { PIX_FMT_YUV420P, PIX_FMT_NONE }; AVBufferSinkParams *buffersink_params; filter_graph = avfilter_graph_alloc();//为FilterGraph分配内存。 /* buffer video source: the decoded frames from the decoder will be inserted here. */ snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", in_width,in_height,PIX_FMT_YUV420P, 1, 25,1,1); //创建并向FilterGraph中添加一个Filter。 ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, NULL, filter_graph); if (ret < 0) { printf("Cannot create buffer source\n"); return ret; } /* buffer video sink: to terminate the filter chain. */ buffersink_params = av_buffersink_params_alloc(); buffersink_params->pixel_fmts = pix_fmts; ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, buffersink_params, filter_graph); av_free(buffersink_params); if (ret < 0) { printf("Cannot create buffer sink\n"); return ret; } /* Endpoints for the filter graph. */ outputs->name = av_strdup("in"); outputs->filter_ctx = buffersrc_ctx; outputs->pad_idx = 0; outputs->next = NULL; inputs->name = av_strdup("out"); inputs->filter_ctx = buffersink_ctx; inputs->pad_idx = 0; inputs->next = NULL; //将一串通过字符串描述的Graph添加到FilterGraph中。 if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_descr, &inputs, &outputs, NULL)) < 0) return ret; //检查FilterGraph的配置。 if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) return ret; frame_in=av_frame_alloc(); frame_buffer_in=(unsigned char *)av_malloc(av_image_get_buffer_size(PIX_FMT_YUV420P, in_width,in_height,1)); av_image_fill_arrays(frame_in->data, frame_in->linesize,frame_buffer_in, PIX_FMT_YUV420P,in_width, in_height,1); frame_out=av_frame_alloc(); frame_buffer_out=(unsigned char *)av_malloc(av_image_get_buffer_size(PIX_FMT_YUV420P, in_width,in_height,1)); av_image_fill_arrays(frame_out->data, frame_out->linesize,frame_buffer_out, PIX_FMT_YUV420P,in_width, in_height,1); frame_in->width=in_width; frame_in->height=in_height; frame_in->format=PIX_FMT_YUV420P; while (1) { if(fread(frame_buffer_in, 1, in_width*in_height*3/2, fp_in)!= in_width*in_height*3/2){ break; } //input Y,U,V frame_in->data[0]=frame_buffer_in; frame_in->data[1]=frame_buffer_in+in_width*in_height; frame_in->data[2]=frame_buffer_in+in_width*in_height*5/4; //向FilterGraph中加入一个AVFrame。 if (av_buffersrc_add_frame(buffersrc_ctx, frame_in) < 0) { printf( "Error while add frame.\n"); break; } /* pull filtered pictures from the filtergraph 从FilterGraph中取出一个AVFrame。*/ ret = av_buffersink_get_frame(buffersink_ctx, frame_out); if (ret < 0) break; //output Y,U,V if(frame_out->format==PIX_FMT_YUV420P){ for(int i=0;i<frame_out->height;i++){ fwrite(frame_out->data[0]+frame_out->linesize[0]*i,1,frame_out->width,fp_out); } for(int i=0;i<frame_out->height/2;i++){ fwrite(frame_out->data[1]+frame_out->linesize[1]*i,1,frame_out->width/2,fp_out); } for(int i=0;i<frame_out->height/2;i++){ fwrite(frame_out->data[2]+frame_out->linesize[2]*i,1,frame_out->width/2,fp_out); } } printf("Process 1 frame!\n"); } fclose(fp_in); fclose(fp_out); av_frame_free(&frame_in); av_frame_free(&frame_out); avfilter_graph_free(&filter_graph); return 0; }
转载请注明原文地址: https://www.6miu.com/read-35616.html

最新回复(0)