当前位置:文档之家› ffmpeg学习笔记

ffmpeg学习笔记

准备工作:1.登录/builds/下载对应的开发版本如xp32位系统下载FFmpeg 32-bit Dev Versions与FFmpeg 32-bit Shared Versions中dev版本里有头文件与静态lib库,在开发的时候包含进工程,share版本里有程序运行发布时候需要的dll动态库。

2.在vs2010中配置inclue与lib工程→属性→配置属性→c/c++→常规→附加包含目录→添加dev版本中的include路径工程→属性→配置属性→链接器→常规→附加库目录→添加dev版本中的lib 路径工程→属性→配置属性→链接器→输入→附加依赖项→添加dev版本中的lib文件,具体包括:avcodec.lib,avformat.lib,avdevice.lib,avfilter.lib,avutil.lib,postproc.lib,swresample.lib,swscale.lib3.在工程中引用ffmpeg头文件,因为ffmepeg是纯C程序,因此,在C++中包含其头文件需要加上extern “C”标识符,如:extern"C"{#include"libavformat/avformat.h"#include"libavcodec/avcodec.h"}4.工程调试运行时需要将dll添加至工程目录,具体包括:avcodec-55.dll,avdevice-55.dll,avfilter-3.dll,avformat-55.dll,avutil-52.dll,postproc-52.dll,swresample-0.dll,swscale-2.dll5.程序编译会报无法打开包括文件:“inttypes.h”: No such file or directory原因是因为VS2010不支持inttypes.h。

解决方法删除,并在之前添加如下代码#if defined(WIN32)&&!defined(__MINGW32__)&&!defined(__CYGWIN__)# define CONFIG_WIN32#endif#if defined(WIN32)&&!defined(__MINGW32__)&&!defined(__CYGWIN__)&&!defined(EMULATE_INTTYPES)#define EMULATE_INTTYPES#endif#ifndef EMULATE_INTTYPES#include#elsetypedef signed char int8_t;typedef signed short int16_t;typedef signed int int32_t;typedef unsigned char uint8_t;typedef unsigned short uint16_t;typedef unsigned int uint32_t;#ifdef CONFIG_WIN32typedef signed__int64int64_t;typedef unsigned__int64uint64_t;#elsetypedef signed long long int64_t;typedef unsigned long long uint64_t;#endif#endif6.打开一个视频文件:首先调用av_register_all()初始化并注册视频文件格式与编解码库,注意只需要初始化一次。

然后调用avformat_open_input()打开具体的视频文件如:AVFormatContext*pFormatCtx=NULL;const char*filename="a.avi";int ret;if((ret=avformat_open_input(&pFormatCtx,filename,NULL,NULL))<0) {abort();}其中avformat_open_input具体实现与参数含义avformat_open_input[cpp]view plaincopy1.//参数ps包含一切媒体相关的上下文结构,有它就有了一切,本函数如果打开媒体成功,2.//会返回一个AVFormatContext的实例.3.//参数filename是媒体文件名或URL.4.//参数fmt是要打开的媒体格式的操作结构,因为是读,所以是inputFormat.此处可以5.//传入一个调用者定义的inputFormat,对应命令行中的 -f xxx段,如果指定了它,6.//在打开文件中就不会探测文件的实际格式了,以它为准了.7.//参数options是对某种格式的一些操作,是为了在命令行中可以对不同的格式传入8.//特殊的操作参数而建的,为了了解流程,完全可以无视它.9.int avformat_open_input(AVFormatContext **ps,10.const char *filename,11. AVInputFormat *fmt,12. AVDictionary **options)13.{14. AVFormatContext *s = *ps;15.int ret = 0;16. AVFormatParameters ap = { { 0 } };17. AVDictionary *tmp = NULL;18.19.//创建上下文结构20.if (!s && !(s = avformat_alloc_context()))21.return AVERROR(ENOMEM);22.//如果用户指定了输入格式,直接使用它23.if (fmt)24. s->iformat = fmt;25.26.//忽略27.if (options)28. av_dict_copy(&tmp, *options, 0);29.30.if ((ret = av_opt_set_dict(s, &tmp)) < 0)31.goto fail;32.33.//打开输入媒体(如果需要的话),初始化所有与媒体读写有关的结构们,比如34.//AVIOContext,AVInputFormat等等35.if ((ret = init_input(s, filename)) < 0)36.goto fail;37.//执行完此函数后,s->pb和s->iformat都已经指向了有效实例.pb是用于读写数据的,它38.//把媒体数据当做流来读写,不管是什么媒体格式,而iformat把pb读出来的流按某种媒体格39.//式进行分析,也就是说pb在底层,iformat在上层.40.41.//很多静态图像文件格式,都被当作一个格式处理,比如要打开.jpeg文件,需要的格式42.//名为image2.此处还不是很了解具体细节,作不得准哦.43./* check filename in case an image number is expected */44.if (s->iformat->flags & AVFMT_NEEDNUMBER) {45.if (!av_filename_number_test(filename)) {46. ret = AVERROR(EINVAL);47.goto fail;48. }49. }50.51. s->duration = s->start_time = AV_NOPTS_VALUE;52.//上下文中保存下文件名53. av_strlcpy(s->filename, filename, sizeof(s->filename));54.55./* allocate private data */56.//为当前格式分配私有数据,主要用于某格式的读写操作时所用的私有结构.57.//此结构的大小在定义AVInputFormat时已指定了.58.if (s->iformat->priv_data_size > 0) {59.if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {60. ret = AVERROR(ENOMEM);61.goto fail;62. }63.//这个可以先不必管它64.if (s->iformat->priv_class) {65. *(const AVClass**) s->priv_data = s->iformat->priv_class;66. av_opt_set_defaults(s->priv_data);67.if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)68.goto fail;69. }70. }71.72./* e.g. AVFMT_NOFILE formats will not have a AVIOContext */73.//从mp3文件中读ID3数据并保存之.74.if (s->pb)75. ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC);76.77.//读一下媒体的头部,在read_header()中主要是做某种格式的初始化工作,比如填充自己的78.//私有结构,根据流的数量分配流结构并初始化,把文件指针指向数据区开始处等.79.if (!(s->flags & AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)80.if ((ret = s->iformat->read_header(s, &ap)) < 0)81.goto fail;82.83.//保存数据区开始的位置84.if (!(s->flags & AVFMT_FLAG_PRIV_OPT) && s->pb && !s->data_offset)85. s->data_offset = avio_tell(s->pb);86.87. s->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;88.89.if (options) {90. av_dict_free(options);91. *options = tmp;92. }93. *ps = s;94.//执行成功95.return 0;96.97.//执行失败98. fail: av_dict_free(&tmp);99.if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))100. avio_close(s->pb);101. avformat_free_context(s);102. *ps = NULL;103.return ret;104.}init_input[cpp]view plaincopy1.//打开输入媒体并填充其AVInputFormat结构2.static int init_input(AVFormatContext *s, const char *filename)3.{4.int ret;5. AVProbeData pd = { filename, NULL, 0 };6.7.//当调用者已指定了pb(数据取得的方式)--一般不会这样.8.if (s->pb) {9. s->flags |= AVFMT_FLAG_CUSTOM_IO;10.if (!s->iformat)11.//如果已指定了pb但没指定iformat,以pb读取媒体数据进行探测,取得.取得iformat.12.return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0,0);13.else if (s->iformat->flags & AVFMT_NOFILE)14.//如果已指定pb也指定了iformat,但是又指定了不需要文件(也包括URL指定的地址),这就矛盾了,15.//此时应是不需要pb的,因为不需操作文件,提示一下吧,也不算错.16. av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and"17."will be ignored with AVFMT_NOFILE format.\n");18.return 0;19. }20.21.//一般会执行到这里22.if ((s->iformat && s->iformat->flags & AVFMT_NOFILE)23. || (!s->iformat && (s->iformat = av_probe_input_format(&pd, 0))))24.//如果已指定了iformat并且不需要文件,也就不需要pb了,可以直接返回25.//如果没指定iformat,但是可以从文件名中猜出iformat,也成功.26.return 0;27.28.//如果从文件名中也猜不出媒体格式,则只能打开这个文件进行探测了,先打开文件29.if ((ret = avio_open(&s->pb, filename, AVIO_FLAG_READ)) < 0)30.return ret;31.if (s->iformat)32.return 0;33.//再探测之34.return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, 0);35.}avio_open[cpp]view plaincopy1.//打开一个地址指向的媒体2.int avio_open(AVIOContext **s, const char *filename, int flags)3.{4.//URLContext代表一个URL地址指向的媒体文件,本地路径也算一种.它封装了5.//操作一个媒体文件的相关数据,最重要的是prot变量,是URLProtocol型的.6.//prot代表一个特定的协义和协议操作函数们,URLContext包含不同的prot,7.//就可以通过URLContext使用不同的协议读写媒体数据,比如tcp,http,本地8.//文件用file协议.9. URLContext *h;10.int err;11.12.//创建并初始化URLContext,其prot通过文件名确定.然后打开这个媒体文件13. err = ffurl_open(&h, filename, flags);14.if (err < 0)15.return err;16.//其实文件已经在上边真正打开了.这里只是填充AVIOContext.使它记录下17.//URLContext,以及填充读写数据的函数指针.18. err = ffio_fdopen(s, h);19.if (err < 0) {20. ffurl_close(h);21.return err;22. }23.return 0;24.}av_probe_input_buffer[cpp]view plaincopy1.int av_probe_input_buffer(AVIOContext *pb,2. AVInputFormat **fmt,3.const char *filename,4.void *logctx,5. unsigned int offset,6. unsigned int max_probe_size)7.{8. AVProbeData pd = { filename ? filename : "", NULL, -offset };9. unsigned char *buf = NULL;10.int ret = 0, probe_size;11.12.//计算最多探测数据的字节数13.if (!max_probe_size) {14. max_probe_size = PROBE_BUF_MAX;15. } else if (max_probe_size > PROBE_BUF_MAX) {16. max_probe_size = PROBE_BUF_MAX;17. } else if (max_probe_size < PROBE_BUF_MIN) {18.return AVERROR(EINVAL);19. }20.21.if (offset >= max_probe_size) {22.return AVERROR(EINVAL);23. }24.25.//循环直到探测完指定的数据26.for (probe_size = PROBE_BUF_MIN;27. probe_size <= max_probe_size && !*fmt;28. probe_size =29. FFMIN(probe_size<<1, FFMAX(max_probe_size, probe_size+1))) {30.int score = probe_size < max_probe_size ? AVPROBE_SCORE_MAX / 4 : 0;31.int buf_offset = (probe_size == PROBE_BUF_MIN) ? 0 : probe_size >> 1;32.void *buftmp;33.34.if (probe_size < offset) {35.continue;36. }37.38./* read probe data */39.//分配读取数据存放的缓冲40. buftmp = av_realloc(buf, probe_size + AVPROBE_PADDING_SIZE);41.if (!buftmp) {42. av_free(buf);43.return AVERROR(ENOMEM);44. }45. buf = buftmp;46.//利用pb读数据到缓冲的剩余空间中47.if ((ret = avio_read(pb, buf + buf_offset, probe_size - buf_offset))48. < 0) {49./* fail if error was not end of file, otherwise, lower score */50.if (ret != AVERROR_EOF) {51. av_free(buf);52.return ret;53. }54. score = 0;55. ret = 0; /* error was end of file, nothing read */56. }57. pd.buf_size += ret;58. pd.buf = &buf[offset];59.60.//缓冲中没有数据的部分要清061. memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE);62.63./* guess file format */64.//从一个打开的文件只探测媒体格式65. *fmt = av_probe_input_format2(&pd, 1, &score);66.if (*fmt) {67.if (score <= AVPROBE_SCORE_MAX / 4) { //this can only be true inthe last iteration68. av_log(69. logctx,70. AV_LOG_WARNING,71."Format %s detected only with low score of %d, misdetection possible!\n",72. (*fmt)->name, score);73. } else74. av_log(logctx, AV_LOG_DEBUG,75."Format %s probed with size=%d and score=%d\n",76. (*fmt)->name, probe_size, score);77. }78.//不成功,继续79. }80.81.if (!*fmt) {82. av_free(buf);83.return AVERROR_INVALIDDATA;84. }85.86./* rewind. reuse probe buffer to avoid seeking */87.//把探测时读入的数据保存到pb中,为的是真正读时直接利用之.88.if ((ret = ffio_rewind_with_probe_data(pb, buf, pd.buf_size)) < 0)89. av_free(buf);90.91.return ret;92.}7.获取流信息int videoStreamIndex=0;for(;videoStreamIndex<pFormatCtx->nb_streams;++videoStreamIndex) {if(pFormatCtx->streams[videoStreamIndex]->codec->coder_type== AVMEDIA_TYPE_VIDEO){break;}}if(videoStreamIndex==pFormatCtx->nb_streams){//没有找到视频流abort();}8.解码视频AVCodecContext*pCodeCtx=pFormatCtx->streams[videoStreamIndex]->codec;//获?取¨?解a码?器¡ÂAVCodec*pCodec=avcodec_find_decoder(pCodeCtx->codec_id);if(pCodec==NULL){abort();}if(pCodec->capabilities&CODEC_CAP_TRUNCATED){pCodeCtx->flags|=CODEC_FLAG_TRUNCATED;}if(avcodec_open2(pCodeCtx,pCodec,NULL)<0){abort();}//给解码后视频帧预分配存储空间?AVFrame*pFrame=NULL;pFrame=avcodec_alloc_frame();//预分配存储空间用来存储转换后的帧?AVFrame*pFrameRGB=NULL;pFrameRGB=avcodec_alloc_frame();int numBytes=avpicture_get_size(PIX_FMT_RGB24,pCodeCtx->width,pCodeCtx->height);//指向的内存关联起来uint8_t*buffer=(uint8_t*)av_malloc(numBytes);avpicture_fill((AVPicture*)pFrameRGB,buffer,PIX_FMT_RGB24,pCodeCtx->width,pCodeCtx->height);AVPacket pPacket;int num=0;char*fileEx=".bmp";while(av_read_frame(pFormatCtx,&pPacket)>=0){//判断是否为视频流if(pPacket.stream_index==videoStreamIndex){int decFinished;avcodec_decode_video2(pCodeCtx,pFrame,&decFinished,&pPacket);if(decFinished){struct SwsContext*img_convert_ctx=NULL;img_convert_ctx=sws_getCachedContext(img_convert_ctx,pCodeCtx->width,pCodeCtx->height,pCodeCtx->pix_fmt,pCodeCtx->width,pCodeCtx->height,PIX_FMT_RGB24,SWS_ BICUBIC,NULL,NULL,NULL);if(img_convert_ctx==NULL){abort();}//将YUV12转换为RGB24sws_scale(img_convert_ctx,(const uint8_t*const*)pFrame->data,pFrame->linesize,0,pCodeCtx->height,pFrameRGB->data,pFrameRGB->linesize);char temp[10];_itoa_s(num,temp,10);char path[256]="bmp/";strcat_s(path,sizeof(path),temp);strcat_s(path,sizeof(path),fileEx);++num;BMP_SaveFile(path,pFrameRGB->data[0],pCodeCtx->width,pCodeCtx->height ,24);}}av_free_packet(&pPacket);}//回收资源av_free(buffer);av_free(pFrameRGB);av_free(pFrame);avcodec_close(pCodeCtx);avformat_close_input(&pFormatCtx);return0;}bool BMP_SaveFile(const char*szFile,const void*pBmp,int width,int height, int bitCount){FILE*pFile=NULL;fopen_s(&pFile,szFile,"wb");ASSERT(pFile!=NULL);int bmp_size=width*height*(bitCount/8);BITMAPFILEHEADER bmpHeader;bmpHeader.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) +bmp_size;bmpHeader.bfType=0x4D42;bmpHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);bmpHeader.bfReserved1=0;bmpHeader.bfReserved2=0;fwrite(&bmpHeader,sizeof(bmpHeader),1,pFile);BITMAPINFOHEADER bmiHeader;bmiHeader.biSize=sizeof(bmiHeader);bmiHeader.biWidth=width;bmiHeader.biHeight=height;bmiHeader.biPlanes=1;bmiHeader.biBitCount=bitCount;bmiHeader.biCompression=0;bmiHeader.biSizeImage=bmp_size;bmiHeader.biXPelsPerMeter=0;bmiHeader.biYPelsPerMeter=0;bmiHeader.biClrUsed=0;bmiHeader.biClrImportant=0;fwrite(&bmiHeader,sizeof(bmiHeader),1,pFile);reverseChar((char**)&pBmp,bmp_size);fwrite(pBmp,bmp_size,1,pFile);fclose(pFile);return true;}void reverseChar(char**strs,int nSize){//定义一个指针指向strs的内存地址char*p=*strs;char tmp;for(unsigned int i=0;i<nSize/2;i++){tmp=*(p+i);*(p+i)=*(p+nSize-1-i);*(p+nSize-1-i)=tmp;}}。

相关主题