Linux下基于ffmpeg音视频解码
1.ffmpeg简介
FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。
Fmpeg 是领先的多媒体框架,能够解码、编码、转码、混合、解密、流媒体、过滤和播放人类和机器创造的几乎所有东西。它支持最晦涩的古老格式,直到最尖端的格式。无论它们是由某个标准委员会、社区还是公司设计的。它还具有高度的便携性。2.ffmpeg八大库介绍
avutil工具库avutil: 工具库(大部分库都需要这个库的支持),如AVLog日志输出、AVOption (AVClass)选项设置、AVDictionary键值对存储、ParseUtil字符串解析等;
avcodec编解码库avcodec: 编解码(最重要的库) 。其中AVCodec是存储编解码器信息的结构体,包含了解协议,解封装,解码操作;
AVCodec结构体中重点参数说明: 复制const char *name:编解码器的名字,比较短 const char *long_name:编解码器的名字,全称,比较长 enum AVMediaType type:指明了类型,是视频,音频,还是字幕 enum AVCodecID id:ID,音视频流ID信息,枚举类型 const AVRational *supported_framerates:支持的帧率(仅视频) const enum AVPixelFormat *pix_fmts:支持的像素格式(仅视频) const int *supported_samplerates:支持的采样率(仅音频) const enum AVSampleFormat *sample_fmts:支持的采样格式(仅音频) const uint64_t *channel_layouts:支持的声道数(仅音频) int priv_data_size:私有数据的大小 avformat: 封装格式处理 AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体。 复制AVIOContext *pb;字节流IO上下文 unsigned int nb_streams;音视频流个数 AVStream **streams;音视频流 char filename[1024];输入输出文件名 int64_t duration;流持续时间,即总时长,单位为us int bit_rate:比特率(码率)(单位bps,转换为kbps需要除以1000) AVDictionary *metadata:元数据 avdevice: 各种设备的输入输出可以读取电脑(或其他设备上)的多媒体设备的数据,或者输出到指定的多媒体设备上。
avfilter: 滤镜特效处理libavfilter提供了一个通用的音视频filter框架。使用avfilter可以对音视频数据做一些效果处理如去色调、模糊、水平翻转、裁剪、加方框、叠加文字等功能。
swscale: 视频像素数据格式转换libswscale 是 FFmpeg 中完成图像尺寸缩放和像素格式转换的库。用户可以编写程序,调用 libswscale 提供的 API 来进行图像尺寸缩放和像素格式转换。
libswscale中常用函数: 复制sws_getContext();初始化一个SwsContext。 sws_scale();处理图像数据。 sws_freeContext();释放一个SwsContext。 swresample: 音频采样数据格式转换libswresample库功能主要包括高度优化的音频重采样、rematrixing和样本格式转换操作。
postproc:后加工(同步、时间计算的简单算法)用于后期效果处理;
3.ffmpeg解码流程
打开文件avformat_open_input; 寻找解码器avformat_find_stream_info; 寻找音视频流信息av_find_best_stream; 寻找音视频解码器avcodec_find_decoder; 配置音频参数:声道、采样格式、采样率、样本数量、通道个数,进行音频重采样swr_alloc_set_opts(音频格式转码); 配置视频解码参数:分配视频帧,申请存放图像数据空间,计算一帧空间大小,进行图像转码sws_getContext; 初始化SDL,实现图像渲染和音频播放; 读取数据包av_read_frame,实现视频解析和音频解析;4.ffmpeg解码示例
开发环境 复制开发平台: ubuntu18.04.6 ffmpeg版本: 4.2.5 SDL版本: 2.0.14 工程示例 复制#include #include “libavcodec/avcodec.h” #include “libavformat/avformat.h” #include “libswscale/swscale.h” #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FILE_NAME “123.flv” typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef enum { false, true, }bool; uint8_t *out_buffer; #define MAX_AUDIO_FRAME_SIZE 1024*100 static Uint8* audio_chunk; static unsigned int audio_len=0; static unsigned char *audio_pos; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//互斥锁 //保存音频数据链表 struct AUDIO_DATA { unsigned char* audio_buffer; int audio_size; struct AUDIO_DATA *next; }; //定义一个链表头 struct AUDIO_DATA *list_head=NULL; struct AUDIO_DATA *List_CreateHead(struct AUDIO_DATA *head);//创建链表头 void List_AddNode(struct AUDIO_DATA *head,unsigned char* audio_buffer,int audio_size);//添加节点 void List_DelNode(struct AUDIO_DATA *head,unsigned char* audio_buffer);//删除节点 int List_GetNodeCnt(struct AUDIO_DATA *head);//遍历 int List_GetNode(struct AUDIO_DATA *head,char *audio_buff,int *audio_size); int file_stat=1; void AudioCallback(void *userdata, Uint8 * stream,int len) { SDL_memset(stream, 0,len); if(audio_len<=0) { return ; } len=(len>audio_len?audio_len:len); SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME); audio_pos+=len; audio_len-=len; //printf(“len=%dn”,len); } void *Audio_decode(void *arg) { int res; int audio_size; char audio_buff[4096*3]; while(1) { res=List_GetNode(list_head,audio_buff,&audio_size); if(res==0) { audio_chunk = audio_buff; //指向音频数据 (PCM data) while(audio_len>0){}//等待数据处理完 audio_len =audio_size;//音频长度 audio_pos = audio_buff;//当前播放位置 } } } int main(int argc,char *argv[]) { if(argc!=2) { printf(“格式:./app 文件名n”); return 0; } char *file_name=argv[1]; /*SDL初始化*/ SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER); printf(“pth:%sn”,avcodec_configuration());/*获取ffmpeg配置信息*/ /*初始化所有组件*/ //av_register_all(); /*打开文件*/ AVCodecContext *pCodecCtx;//解码器上下文 AVFormatContext *ps=NULL;//音视频封装格式结构体信息 printf(“name:%sn”,file_name); int res=avformat_open_input(&ps,file_name,NULL,NULL); if(res!=0) { printf(“open err: %dn”,res); return 0; } /*寻找解码信息*/ avformat_find_stream_info(ps,NULL); int64_t time=ps->duration; printf(“time:%ld sn”,time/1000000); /*打印有关输入或输出格式的详细信息*/ av_dump_format(ps,0,file_name,0); /*寻找视频流信息*/ int videostream=-1; int audiostream=-1; AVCodec *vcodec; videostream=av_find_best_stream(ps,AVMEDIA_TYPE_VIDEO,-1,-1,NULL, 0); printf(“video=%dn”,videostream); /*寻找音频流信息*/ audiostream=av_find_best_stream(ps,AVMEDIA_TYPE_AUDIO,-1,-1,NULL, 0); printf(“audio=%dn”,audiostream); AVStream *stream; int frame_rate; if(videostream!=-1)//判断是否找到视频流数据 { /*寻找视频解码器*/ AVStream *stream = ps->streams[videostream]; vcodec=avcodec_find_decoder(stream->codecpar->codec_id); if(!vcodec) { printf(“未找到视频解码器n”); return -1; }/*申请AVCodecContext空间。需要传递一个编码器,也可以不传,但不会包含编码器。*/ res=avcodec_open2(stream->codec,vcodec,NULL); if(res) { printf(“打开解码器失败n”); return -1; } frame_rate=stream->avg_frame_rate.num/stream->avg_frame_rate.den;//每秒多少帧 printf(“fps=%dn”,frame_rate); printf(“视频流ID=%#xn”,vcodec->id);//音频流 } /*音频流数据处理*/ AVCodec *audcodec; AVStream *audstream; SwrContext *swrCtx;//保存重采样数据,即解码的信息 uint64_t out_channel_layout;//声道 int out_sample_fmt;//采样格式 int out_sample_rate;//采样率 int out_nb_samples;//样本数量 int out_channels;//通道数量 uint64_t in_channel_layout;//输入音频声道 SDL_AudioSpec desired;//SDL音频格式信息 AVFrame *audioframe;//保存音频数据 int out_buffer_size;//音频缓冲区大小 if(audiostream>=0)//判断是否有音频流 { /*寻找音频解码器*/ audstream = ps->streams[audiostream]; audcodec=avcodec_find_decoder(audstream->codecpar->codec_id); if(!audcodec) { printf(“audcodec failedn”); return -1; } /*申请音频AVCodecContext空间。需要传递一个编码器,也可以不传,但不会包含编码器。*/ pCodecCtx=audstream->codec;//解码器上下文 res=avcodec_open2(audstream->codec,audcodec,NULL); if(res) { printf(“未找到音频解码器n”); return -1; } printf(“音频流ID=%#xn”,audcodec->id);//音频流 printf(“配置音频参数n”); //输出音频参数 out_channel_layout = AV_CH_LAYOUT_STEREO; //声道格式 out_sample_fmt=AV_SAMPLE_FMT_S16;//AV_SAMPLE_FMT_S32;//;//采样格式 printf(“pCodecCtx->sample_rate=%dn”,pCodecCtx->sample_rate); out_sample_rate =pCodecCtx->sample_rate;//采样率,多为44100 /*样本数量*/ printf(“frame_size=%dn”,pCodecCtx->frame_size); if(pCodecCtx->frame_size>0)out_nb_samples=pCodecCtx->frame_size; else if(audcodec->id == AV_CODEC_ID_AAC) out_nb_samples=1024;/*样本数量nb_samples: AAC-1024 MP3-1152 格式大小 */ else if(audcodec->id == AV_CODEC_ID_MP3)out_nb_samples=1152; else out_nb_samples=1024; out_channels=av_get_channel_layout_nb_channels(out_channel_layout);//通道个数 out_buffer_size=av_samples_get_buffer_size(NULL,out_channels,out_nb_samples,out_sample_fmt,1);//获取缓冲区大小 out_buffer=(uint8_t*)av_malloc(MAX_AUDIO_FRAME_SIZE); memset(out_buffer,0,out_buffer_size); printf(“声道格式:%dn”,out_channel_layout); printf(“采样格式:%dn”,out_sample_fmt); printf(“样本数量:%dn”,out_nb_samples); printf(“采样率:%dn”,out_sample_rate); printf(“通道个数:%dn”,out_channels); printf(“缓冲区大小:%dn”,out_buffer_size); //输入音频参数 in_channel_layout=av_get_default_channel_layout(pCodecCtx->channels);//输入声道格式 swrCtx = swr_alloc(); /*对解码数据进行重采样*/ swrCtx=swr_alloc_set_opts(swrCtx,out_channel_layout,out_sample_fmt,out_sample_rate,/*输入音频格式*/ in_channel_layout,pCodecCtx->sample_fmt,pCodecCtx->sample_rate,/*输出音频格式*/ 0,NULL); swr_init(swrCtx);//初始化swrCtx printf(“输入音频格式:%dn”,in_channel_layout); printf(“输入采样格式:%dn”,pCodecCtx->sample_fmt); printf(“输入采样率:%dn”,pCodecCtx->sample_rate); /*设置音频数据格式*/ desired.freq=out_sample_rate;/*采样率*/ desired.format=AUDIO_S16SYS;/*无符号16位*/ desired.channels=out_channels;/*声道*/ desired.samples=out_nb_samples;/*样本数1024*/ desired.silence=0;/*静音值*/ desired.callback=AudioCallback; SDL_OpenAudio(&desired,NULL); SDL_PauseAudio(0);/*开始播放音频,1为播放静音值*/ //分配内存 audioframe=av_frame_alloc();/*分配音频帧*/ printf(“音频数据初始化完成”); } //视频解码 AVFrame *frame=av_frame_alloc();/*分配视频帧*/ AVFrame *frameYUV=av_frame_alloc();/*申请yuv空间*/ /*分配空间,进行图像转换*/ int width=ps->streams[videostream]->codecpar->width; int height=ps->streams[videostream]->codecpar->height; int fmt=ps->streams[videostream]->codecpar->format;/*流格式*/ printf(“fmt=%dn”,fmt); int size=avpicture_get_size(AV_PIX_FMT_RGB24, width,height); unsigned char *buff=NULL; printf(“w=%d,h=%d,size=%dn”,width,height,size); buff=av_malloc(size); /*计算一帧空间大小*/ avpicture_fill((AVPicture *)frameYUV,buff,AV_PIX_FMT_RGB24,width,height); /*转换上下文*/ struct SwsContext *swsctx=sws_getContext(width,height, fmt,width,height, AV_PIX_FMT_RGB24,SWS_BICUBIC,NULL,NULL,NULL); /*读帧*/ int go=0; int go_audio; list_head=List_CreateHead(list_head);//创建链表头 /*创建音频处理线程*/ pthread_t pthid; pthread_create(&pthid,NULL,Audio_decode,(void *)ps); pthread_detach(pthid);//设置为分离属性 /*创建窗口*/ SDL_Window *window=SDL_CreateWindow(“SDL_VIDEO”, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,1280,720,SDL_WINDOW_SHOWN); /*创建渲染器*/ SDL_Renderer *render=SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED); /*清空渲染器*/ SDL_RenderClear(render); /*创建纹理*/ SDL_Texture *sdltext=SDL_CreateTexture(render,SDL_PIXELFORMAT_RGB24,SDL_TEXTUREACCESS_STREAMING,width,height); bool quit=true; SDL_Event event; printf(“read fream buffn”); //初始化转码器 AVPacket *packet=av_malloc(sizeof(AVPacket));/*分配包*/ av_init_packet(packet);//初始化 int i=0; int index=0; long video_pts_time=0; long audio_pts_time=0;//音频解码时间 time=(1000000/frame_rate-10000);//时间 printf(“time=%dn”,time); while((av_read_frame(ps,packet)>=0) && (quit)) { SDL_PollEvent(&event); if(event.type==SDL_QUIT) { quit=false; continue; } if(packet->stream_index == videostream)/*判断是否为视频*/ { res=avcodec_send_packet(ps->streams[videostream]->codec,packet); if(res) { av_packet_unref(packet);//释放这个pkt continue; } res=avcodec_receive_frame(ps->streams[videostream]->codec,frame); if(res) { av_packet_unref(packet);//释放这个pkt continue; } sws_scale(swsctx,(const uint8_t **)frame->data,frame->linesize,0,height,(const uint8_t **)frameYUV->data,frameYUV->linesize); video_pts_time=packet->pts; //printf(“视频=%ldn”,video_pts_time); SDL_UpdateTexture(sdltext,NULL,buff, width*3); SDL_RenderCopy(render, sdltext, NULL, NULL); // 拷贝纹理到渲染器 SDL_RenderPresent(render); //渲染 usleep(time); } if(packet->stream_index == audiostream) //如果为音频标志 { if(audiostream<0)continue; res=avcodec_send_packet(pCodecCtx,packet); if(res) { printf(“avcodec_send_packet failed,res=%dn”,res); av_packet_unref(packet);//释放这个pkt continue; } res=avcodec_receive_frame(pCodecCtx,audioframe); if(res) { printf(“avcodec_receive_frame failed,res=%dn”,res); av_packet_unref(packet);//释放这个pkt continue; } //数据格式转换 res=swr_convert(swrCtx,&out_buffer,out_buffer_size,/*重采样之后的数据*/ (const uint8_t **)audioframe->data,audioframe->nb_samples/*重采样之前数据*/ ); audio_pts_time=packet->pts; //printf(“音频:%ldn”,audio_pts_time); if(res>0) { //audio_chunk =out_buffer; //指向音频数据 (PCM data) //while(audio_len>0){}//等待数据处理完 //audio_len =audioframe->nb_samples;//out_buffer_size;//音频长度 //audio_pos =out_buffer;//当前播放位置 List_AddNode(list_head,out_buffer,out_buffer_size);//添加节点 } } //释放数据包 av_packet_unref(packet); } sws_freeContext(swsctx); av_frame_free(&frame); av_frame_free(&frameYUV); avformat_free_context(ps); return 0; } /*创建链表头*/ struct AUDIO_DATA *List_CreateHead(struct AUDIO_DATA *head) { if(head==NULL) { head=malloc(sizeof(struct AUDIO_DATA)); head->next=NULL; } return head; } /*添加节点*/ void List_AddNode(struct AUDIO_DATA *head,unsigned char* audio_buffer,int audio_size) { struct AUDIO_DATA *tmp=head; struct AUDIO_DATA *new_node; pthread_mutex_lock(&mutex); /*找到链表尾部*/ while(tmp->next) { tmp=tmp->next; } /*插入新的节点*/ new_node=malloc(sizeof(struct AUDIO_DATA)); new_node->audio_size=audio_size; new_node->audio_buffer=malloc(audio_size);//分配保存音频数据大小空间 memcpy(new_node->audio_buffer,audio_buffer,audio_size); new_node->next=NULL; /*将新节点接入到链表*/ tmp->next=new_node; pthread_mutex_unlock(&mutex); } /* 函数功能:删除节点 */ void List_DelNode(struct AUDIO_DATA *head,unsigned char* audio_buffer) { struct AUDIO_DATA *tmp=head; struct AUDIO_DATA *p; /*找到链表中要删除的节点*/ pthread_mutex_lock(&mutex); while(tmp->next) { p=tmp; tmp=tmp->next; if(tmp->audio_buffer==audio_buffer) { p->next=tmp->next; free(tmp->audio_buffer); free(tmp); break; } } pthread_mutex_unlock(&mutex); } /* 函数功能:遍历链表,得到节点总数量 */ int List_GetNodeCnt(struct AUDIO_DATA *head) { int cnt=0; struct AUDIO_DATA *tmp=head; pthread_mutex_lock(&mutex); while(tmp->next) { tmp=tmp->next; cnt++; } pthread_mutex_unlock(&mutex); return cnt; } /* 从链表头取数据 */ int List_GetNode(struct AUDIO_DATA *head,char *audio_buff,int *audio_size) { struct AUDIO_DATA *tmp=head; struct AUDIO_DATA *ptemp=head; pthread_mutex_lock(&mutex); while(tmp->next!=NULL) { ptemp=tmp; tmp=tmp->next; if(tmp!=NULL) { *audio_size=tmp->audio_size; memcpy(audio_buff,tmp->audio_buffer,tmp->audio_size); ptemp->next=tmp->next; free(tmp->audio_buffer); free(tmp); pthread_mutex_unlock(&mutex); return 0; } } pthread_mutex_unlock(&mutex); return -1; } Makefile文件 复制OBJ=main.o CFLAGS=-I/home/wbyq/src_pack/ffmpeg-4.2.5/_install/include -L/home/wbyq/src_pack/ffmpeg-4.2.5/_install/lib -I/home/wbyq/src_pack/SDL2-2.0.14/_install/include -I/home/wbyq/src_pack/SDL2-2.0.14/_install/include/SDL2 -L/home/wbyq/src_pack/SDL2-2.0.14/_install/lib -lSDL2 -lpthread -lm -ldl -lavcodec -lavfilter -lavutil -lswresample -lavdevice -lavformat -lpostproc -lswscale -lpthread -lstdc++ -lm -lasound -lx264 app:$(OBJ) gcc -o $@ $^ $(CFLAGS) 运行示例免责声明:文章内容来自互联网,本站不对其真实性负责,也不承担任何法律责任,如有侵权等情况,请与本站联系删除。
转载请注明出处:Linux下基于ffmpeg音视频解码-linux查看视频编码格式命令 https://www.yhzz.com.cn/a/7170.html