本文分别为:FFmpeg简介FFmpeg音视频解码播放Clang编译FFmpeg常见问题 今天来说一下关于今天先说一下FFmpeg一些内容一丶FFmpeg简介1。简介 FFmpeg(FastForwardMpeg)是一款遵循GPL的开源软件,在音视频处理方面表现十分优秀,几乎囊括了现存所有的视音频格式的编码,解码、转码、混合、过滤及播放。同时也是一款跨平台的软件,完美兼容Linux、Windows、MacOSX等平台。其实它由3大部件组成: FFmpeg:由命令行组成,用于多媒体格式转换FFplay:基于FFmpeg开源代码库libraries做的多媒体播放器FFprobe:基于FFmpeg做的多媒体流分析器 【更多音视频学习资料,点击下方链接免费领取,先码住不迷路】 音视频开发基础知识和资料包 2。FFmpeg两个强大功能 1。命令功能2。api功能2。1命令功能应用程序使用方法 ffmpegyiinputvcodeccopyanoutput。avi 其中y表示覆盖同名文件,i表示输入文件即bus。avi,vcodec表示编码方式,后面的copy表示用原来的编码方式,即不重新编码,an表示去除音频,后面的busv。avi表示分离出的视频文件。 同理将视频中的音频文件分离出来的命令行为: ffmpegibus。aviacodeccopyvnbusa。wav 上面举例说明了应用程序的用法,应用程序的命令行相对代码要简单很多,也能实现例如音视频分离、转码、播放等各种功能,如视频转码的命令行为:ffmpegyiinput。mp4vcodeclibx264acodeccopyoutput。mp4 这个命令用于剪切视频,ss表示从第几秒开始,如上实例为从第5秒开始,t代表剪持续几秒长度的视频,如上实例就是剪10秒长度的视频,copy表示视频编码格式和音频编码格式与原视频统一。ffmpegss0:0:5t0:0:10iinput。avivcodeccopyacodeccopyoutput。avi 分离视频音频流ffmpegiinputfilevcodeccopyanoutputfilevideo分离视频流 ffmpegiinputfileacodeccopyvnoutputfileaudio分离音频流 视频解复用ffmpegitest。mp4vcodeccopyanfm4vtest。264 ffmpegitest。avivcodeccopyanfm4vtest。264 视频转码ffmpegitest。mp4vcodech264s352278anfm4vtest。264转码为码流原始文件 ffmpegitest。mp4vcodech264bf0g25s352278anfm4vtest。264转码为码流原始文件 ffmpegitest。avivcodecmpeg4vtagxvidqsametestxvid。avi转码为封装文件 视频封装ffmpegivideofileiaudiofilevcodeccopyacodeccopyoutputfile 视频剪切ffmpegitest。avir1fimage2image3d。jpeg提取图片 ffmpegss0:1:30t0:0:20iinput。avivcodeccopyacodeccopyoutput。avi 剪切视频 r提取图像的频率,ss开始时间,t持续时间 视频录制ffmpegirtsp:192。168。3。205:5555testvcodeccopyout。avi YUV序列播放ffplayfrawvideovideosize1920x1080input。yuv YUV序列转AVIffmpegswhpixfmtyuv420piinput。yuvvcodecmpeg4output。avi常用参数说明: 主要参数:i设定输入流f设定输出格式ss开始时间视频参数:b设定视频流量,默认为200Kbitsr设定帧速率,默认为25s设定画面的宽与高aspect设定画面的比例vn不处理视频vcodec设定视频编解码器,未设定时则使用与输入流相同的编解码器音频参数:ar设定采样率ac设定声音的Channel数acodec设定声音编解码器,未设定时则使用与输入流相同的编解码器an不处理音频 【更多音视频学习资料,点击下方链接免费领取,先码住不迷路】 C程序员必看,抓住音视频开发的大浪潮!冲击年薪60万 二丶FFmpeg音视频解码播放前言 通常情况下,媒体文件以如MP4,MKV、FLV等等格式存在我们的计算机,手机等设备中,而这些文件格式都属于封装格式,就是把音视频数据按照相应的规范,打包成文件。1。FFmpeg音视频解码流程 平常我们播放媒体文件时,通常需要经过以下几个步骤 2。FFmpeg音视频解码原理2。1。解协议 将流媒体协议的数据,解析为标准的相应的封装格式数据。视音频在网络上传播的时候,常常采用各种流媒体协议,例如HTTP,RTMP,或是MMS等等。这些协议在传输视音频数据的同时,也会传输一些信令数据。这些信令数据包括对播放的控制(播放,暂停,停止),或者对网络状态的描述等。 解协议的过程中会去除掉信令数据而只保留视音频数据。例如,采用RTMP协议传输的数据,经过解协议操作后,输出FLV格式的数据。2。2。解封装 将输入的封装格式的数据,分离成为音频流压缩编码数据和视频流压缩编码数据。封装格式种类很多,例如MP4,MKV,RMVB,TS,FLV,AVI等等,它的作用就是将已经压缩编码的视频数据和音频数据按照一定的格式放到一起。例如,FLV格式的数据,经过解封装操作后,输出H。264编码的视频码流和AAC编码的音频码流。2。3。解码 将视频音频压缩编码数据,解码成为非压缩的视频音频原始数据。解码是整个系统中最重要也是最复杂的一个环节。通过解码,压缩编码的视频数据输出成为非压缩的颜色数据,例如YUV420P,RGB等等;2。4。音视频同步 根据解封装模块处理过程中获取到的参数信息,同步解码出来的视频和音频数据,并将视频音频数据送至系统的显卡和声卡播放出来。2。5。FFmpeg音视频解码 通过前文,我们知道每一个媒体文件在被终端播放前主要经过了两个关键步骤,分别是解封装和解码。而在ffmpeg中,使用相关接口实现解封装和解码流程如下图: 由上图可知,我们需要重点关注下面这些FFmpeg的API接口:avregisterall():注册所有组件。avformatopeninput():打开输入视频文件。avformatfindstreaminfo():获取视频文件信息。avcodecfinddecoder():查找解码器。avcodecopen2():打开解码器。avreadframe():从输入文件读取一帧压缩数据。avcodecdecodevideo2():解码一帧压缩数据。 3。FFmpeg接口使用 在使用FFmpeg解码媒体文件之前,首先需要注册了容器和编解码器有关的组件。avregisterall() 如果我们需要播放网络多媒体,则可以加载socket库以及网络加密协议相关的库,为后续使用网络相关提供支持。avformatnetworkinit(); 我们通过avformatopeninput()来打开一个媒体文件,并获得媒体文件封装格式的上下文打开一个文件并解析。可解析的内容包括:视频流、音频流、视频流参数、音频流参数、视频帧索引 intresavformatopeninput(pAVFormatCtx,url,NULL,NULL); LOGI(avformatopeninputsd,url,res); if(res!0){ LOGE(cannotopenurl:s,url); callJavaonCallError(CHILDTHREAD,1001,cannotopenurl); exittrue; pthreadmutexunlock(initmutex); return; } 通过avformatfindstreaminfo()获取媒体文件中,提取流的上下文信息,分离出音视频流。解码时,作用是从文件中提取流信,将所有的Stream的MetaData信息填充好,先readpacket一段数据解码分析流数据 if(avformatfindstreaminfo(pAVFormatCtx,NULL)0){ LOGE(cannotfindstreamsfroms,url); callJavaonCallError(CHILDTHREAD,1002,cannotfindstreamsfromurl); exittrue; pthreadmutexunlock(initmutex); return; } 通过遍历找出文件中的音频流或视频流for(inti0;ipAVFormatCtxnbstreams;i){ if(pAVFormatCtxstreams〔i〕codecparcodectypeAVMEDIATYPEAUDIO){ 得到音频流 if(audioNULL){ audionewFFAudio(playstatus,pAVFormatCtxstreams〔i〕codecparsamplerate,callJava); audiostreamIndexi; audiocodecparpAVFormatCtxstreams〔i〕codecpar; audiodurationpAVFormatCtxdurationAVTIMEBASE; audiotimebasepAVFormatCtxstreams〔i〕timebase; durationaudioduration; avq2d(timebase)每个刻度是多少秒 LOGI(audiostreaminfo〔d〕,duration:d,timebaseden:d,samplerate:d,i,audioduration,audiotimebase。den,pAVFormatCtxstreams〔i〕codecparsamplerate); LOGI(audiostreaminfo〔d〕,durationlld,i,pAVFormatCtxduration); } }elseif(pAVFormatCtxstreams〔i〕codecparcodectypeAVMEDIATYPEVIDEO){ 得到视频流 if(videoNULL){ videonewFFVideo(playstatus,callJava); videostreamIndexi; videocodecparpAVFormatCtxstreams〔i〕codecpar; videotimebasepAVFormatCtxstreams〔i〕timebase; intnumpAVFormatCtxstreams〔i〕avgframerate。num; intdenpAVFormatCtxstreams〔i〕avgframerate。den; LOGI(videostreaminfo〔d〕,frameratenumd,dend,i,num,den); if(num!0den!0){ intfpsnumden;〔251〕 videodefaultDelayTime1。0fps; } LOGI(videostreaminfo〔d〕,defaultDelayTimeisf,i,videodefaultDelayTime); } } } 分离出音视频流之后,可以找到对应AVCodecContext,即编解码器的上下文,用来寻找对应的解码器并设置。 【更多音视频学习资料,点击下方链接免费领取,先码住不迷路】 音视频开发基础知识和资料包 查找对应的解码器存储编解码器信息的结构体 AVCodecavCodecavcodecfinddecoder(codecparcodecid);软解 avCodecavcodecfinddecoderbyname(mp3mediacodec);硬解 if(!avCodec){ LOGE(MFFmpeg::getCodecContextcannotfinddecoder!); callJavaonCallError(CHILDTHREAD,1003,cannotfinddecoder); exittrue; pthreadmutexunlock(initmutex); return1; } LOGI(getCodecContextcodecpar解码类型:d编码格式:s,codecparcodectype,avCodecname); 配置解码器 avCodecContextavcodecalloccontext3(avCodec); if(!avCodecContext){ LOGE(cannotallocnewdecodecctx); callJavaonCallError(CHILDTHREAD,1004,cannotallocnewdecodecctx); exittrue; pthreadmutexunlock(initmutex); return1; } 通过avcodecopen2()打开解码器,解码媒体文件。打开编解码器 if(avcodecopen2(avCodecContext,avCodec,0)!0){ LOGE(cantnotopenstrames); callJavaonCallError(CHILDTHREAD,1006,cantnotopenstrames); exittrue; pthreadmutexunlock(initmutex); return1; } 所以第2,3,4,5四个步骤使用的关系如下图 打开解码器之后,通过avreadframe()一帧一帧读取压缩数据。AVPacketavPacketavpacketalloc(); 读取具体的音视频帧数据 intretavreadframe(pAVFormatCtx,avPacket); if(ret0){ streamindex:标识该AVPacket所属的视频音频流 if(avPacketstreamindexaudiostreamIndex){ LOGI(audio解码第d帧DTS:lldPTS:lld,count,avPacketdts,avPacketpts); audioqueueputAVpacket(avPacket); }elseif(avPacketstreamindexvideostreamIndex){ LOGI(video解码第d帧DTS:lldPTS:lld,count,avPacketdts,avPacketpts); count; videoqueueputAVpacket(avPacket); }else{ avpacketfree(avPacket); avfree(avPacket); avPacketNULL; } } 通过avcodecdecodevideo2()avcodecdecodeaudio4解码一帧视频或者音压缩数据,通过AVPacketAVFrame得到视频像素数据。解码AVPacketAVFrame retavcodecdecodeaudio4(pCodeCtx,frame,gotframe,packet); 解码一帧视频压缩数据,得到视频像素数据 retavcodecdecodevideo2(pCodecCtx,pFrame,gotpicture,packet); 三丶Clang编译FFmpeg常见问题1、命令找不到 错误信息:。buildandroid。sh:line18:enableshared:commandnotfound 。buildandroid。sh:line20:disablestatic:commandnotfound 。buildandroid。sh:line22:disabledoc:commandnotfound 。buildandroid。sh:line24:disableffmpeg:commandnotfound 。buildandroid。sh:line26:disableffplay:commandnotfound 。buildandroid。sh:line28:disableffprobe:commandnotfound 。buildandroid。sh:line30:disableffserver:commandnotfound 。buildandroid。sh:line32:disableavdevice:commandnotfound 解决:如果是直接copy网上的shell脚本,可能会是dos格式,请使用dos2unixbuildandroid。sh转换一下,删掉多余空格(这一点非常重要dos2unix是一个工具,如果没有安装的话请先安装一下:brewinstalldos2unix,很快就完事。2。xmakefile文件没有生成 错误信息:。androidconfig。sh:line36:enableshared:commandnotfound Makefile:2:ffbuildconfig。mak:Nosuchfileordirectory Makefile:40:toolsMakefile:Nosuchfileordirectory Makefile:41:ffbuildcommon。mak:Nosuchfileordirectory Makefile:91:libavutilMakefile:Nosuchfileordirectory Makefile:91:ffbuildlibrary。mak:Nosuchfileordirectory Makefile:93:fftoolsMakefile:Nosuchfileordirectory Makefile:94:docMakefile:Nosuchfileordirectory Makefile:95:docexamplesMakefile:Nosuchfileordirectory Makefile:160:testsMakefile:Nosuchfileordirectory make:NoruletomaketargettestsMakefile。Stop。 Makefile:2:ffbuildconfig。mak:Nosuchfileordirectory Makefile:40:toolsMakefile:Nosuchfileordirectory Makefile:41:ffbuildcommon。mak:Nosuchfileordirectory Makefile:91:libavutilMakefile:Nosuchfileordirectory Makefile:91:ffbuildlibrary。mak:Nosuchfileordirectory Makefile:93:fftoolsMakefile:Nosuchfileordirectory Makefile:94:docMakefile:Nosuchfileordirectory Makefile:95:docexamplesMakefile:Nosuchfileordirectory Makefile:160:testsMakefile:Nosuchfileordirectory 解决:执行。configuredisablex86asm生成config。mak文件3。armlinxuandroideabigccisunableto createanexecutablefile 错误信息:Usersariadevandroidsdkndkbundletoolchainsarmlinuxandroideabi 4。9prebuiltdarwinx8664binarmlinuxandroideabigccisunabletocreatean executablefile。 原因:检查ndk版本,android官方从r18b开始,已经移除了gcc这个编译工具详情见ndkr18b修订内容解决:使用clang进行编译4。androidconfig。sh:line32:xxxxxNosuchfileordirectory 原因:。configure后面的命令不能有注释 解决:删除注释的哪一行代码5、staticdeclarationofxxxfollowsnonstaticdeclaration 解决:config。h搜索lrint、lrintf、round、roundf等对于的字符,将0修改为1defineHAVELLRINT1 defineHAVELLRINTF1 defineHAVELRINT1 defineHAVELRINTF1 defineHAVEROUND1 defineHAVEROUNDF1 defineHAVECBRT1 defineHAVECBRTF1 defineHAVECOPYSIGN1 defineHAVETRUNC1 defineHAVETRUNCF1 defineHAVERINT1 defineHAVEHYPOT1 defineHAVEERF1 或直接使用sed来修改config。h文件sediesdefineHAVELLRINT0defineHAVELLRINT1gconfig。h sediesdefineHAVELLRINTF0defineHAVELLRINTF1gconfig。h sediesdefineHAVELRINT0defineHAVELRINT1gconfig。h sediesdefineHAVELRINTF0defineHAVELRINTF1gconfig。h sediesdefineHAVEROUND0defineHAVEROUND1gconfig。h sediesdefineHAVEROUNDF0defineHAVEROUNDF1gconfig。h sediesdefineHAVECBRT0defineHAVECBRT1gconfig。h sediesdefineHAVECBRTF0defineHAVECBRTF1gconfig。h sediesdefineHAVECOPYSIGN0defineHAVECOPYSIGN1gconfig。h sediesdefineHAVETRUNC0defineHAVETRUNC1gconfig。h sediesdefineHAVETRUNCF0defineHAVETRUNCF1gconfig。h sediesdefineHAVERINT0defineHAVERINT1gconfig。h sediesdefineHAVEHYPOT0defineHAVEHYPOT1gconfig。h sediesdefineHAVEERF0defineHAVEERF1gconfig。h sediesdefineHAVEGMTIMER0defineHAVEGMTIMER1gconfig。h sediesdefineHAVELOCALTIMER0defineHAVELOCALTIMER1gconfig。h sediesdefineHAVEINETATON0defineHAVEINETATON1gconfig。h 6、xxxxxxxxxxerror:expected) 错误信息:definegetenv(x)NULL homecd008diskaandroidndkr9platformsandroid18archarmusrincludestdlib。h:54:14:note:inexpansionofmacrogetenv externchargetenv(constchar); 。config。h:17:19:error:expected)beforenumericconstant definegetenv(x)NULL homecd008diskaandroidndkr9platformsandroid18archarmusrincludestdlib。h:54:14:note:inexpansionofmacrogetenv externchargetenv(constchar); 解决:在config。h中注释掉definegetenv(x)NULLdefinegetenv(x)NULLsediesdefinegetenv(x)NULLdefinegetenv(x)NULLgconfig。h 7、armlinuxandroideabildWl,soname,libavutil。sounknownoption 错误信息:Usersariadevandroidsdkndkbundletoolchainsarmlinuxandroideabi 4。9prebuiltdarwinx8664binarmlinuxandroideabildWl,soname 原因:gcc构建。so的命令是sharedwl,soname,xxxx。so而clang的是sharedsonamexxx。so 解决:修改ffbuildconfig。mak文件,将SHFLAGSsharedWl,soname,(SLIBNAME)修改为SHFLAGSsharedsoname(SLIBNAME)