zoukankan      html  css  js  c++  java
  • ffmpeg API 笔记:使用libavcodec/libavformat/libswscale ffmpeg例子

    ffmpeg API 笔记:使用libavcodec/libavformat/libswscale
    December 11, 2009

    Update 2010.1.5: 其实研究ffmpeg不用找什么教程,第一步应该是下载ffmpeg的源码包。下面提到的An FFmpeg and SDL Tutorial确实有讲解,但是教程总是跟不上代码的变化的,所以直接看可工作代码最好;ffmpeg的结构很分明,后台是几个库:libxxx,前台是三个程序ffmpeg, ffplay, ffserver,那篇教程说的就是ffplay的实现。一个播放器,其实重点不是解码,解码的东西是lib去做的,主要是做声音视频的时钟同步。ffplay的代码可以说是一个可用播放器最简单的实现了,源码里面有个output_example.c,可以说是最基本的api示范吧。ffmpeg是转换编码解码转换程序,因为涉及重新采样等等,所以代码量也不少的。


    这两天"调研"了下ffmpeg的API,不得不承认被雷倒:ffmpeg又是一个很geek的项目,纯社区开发,基于逆向,功能强大,但是文档极度有限,想了解API?看源码去…… 网上关于ffmpeg API的资料,无非是ffmpeg文档里面的两个链接,Using libavformat and libavcodec by
    Martin Böhme(以及其Update,介绍了新引入的API)跟An FFmpeg and SDL Tutorial
    by Stephen Dranger;两个tutorial基于ffmpeg 0.4.8,现在ffmpeg发布的版本是0.5.0,好像数值相差不大,不过0.4.8是5年前的了(相比之下wine用了15年版本号才到达1.0,有过之余无不及),两个教程里面的代码在0.5.0下一编译,哇,一堆错误,可不仅有些api函数变了,有些结构成员压根就没了,头文件的位置更是不一样(各个库分家了)……所以我调试了好几个小时,终于把例子的代码弄好(其实Martin Böhme那篇有一段09年加入的更新说明,链接了有相关的解决办法,我一开始没注意,几个小时自己解决,不过也有收获)。

    最后我调试好的代码流程:打开一个视频文件,抓取前5帧保存为文件;
    基于Stephen Dranger的Tutorial1源码在此:GoogleCode

    av_register_all();//初始化ffmpeg库,如果系统里面的ffmpeg没配置好这里会出错
    av_open_input_file();
    av_find_stream_info();//查找文件的流信息
    dump_format();//dump只是个调试函数,输出文件的音、视频流的基本信息了,帧率、分辨率、音频采样等等
    for(...);//遍历文件的各个流,找到第一个视频流,并记录该流的编码信息
    sws_getContext();//根据编码信息设置渲染格式
    avcodec_find_decoder();//在库里面查找支持该格式的解码器
    avcodec_open();//打开解码器
    pFrame=avcodec_alloc_frame();//分配一个帧指针,指向解码后的原始帧
    pFrameRGB=avcodec_alloc_frame();//分配一个帧指针,指向存放转换成RGB后的帧
    avpicture_fill(pFrameRGB);//给pFrameRGB帧加上分配的内存;
    while true{
         av_read_frame();//读取一个帧(到最后帧则break)
         avcodec_decode_video();//解码该帧
         sws_scale();//把该帧转换(渲染)成RGB
         SaveFrame();//对前5帧保存成ppm图形文件(这个是自定义函数,非API)
         av_free_packet();//释放本次读取的帧内存
    }
    avcodec_close();
    av_close_input_file();

    用到的API就这么多,当然实际代码稍复杂一点;ppm图像是类似BMP的非压缩格式,SaveFrame就是相当于把pFrameRGB的内存拷贝进文件,写文件并不复杂;

    调试过程的问题,首先是头文件,ffmpeg 0.5.0的API已经拆分成好几个独立的库,用pacman -Ql ffmpeg看了下文件分布,在include下好几个目录都是它的,看名字可以大概猜出他们的功能:

    libavcodec:CODEC其实是Coder/Decoder的缩写,也就是编码解码器;
    libavdevice:对输出输入设备的支持;
    libavformat:对音频视频格式的解析
    libavutil:集项工具;
    libpostproc:后期效果处理;
    libswscale:视频场景比例缩放、色彩映射转换;

    修改好头文件包含,终于少了些not declared错误;

    Martin Böhme那篇教程的代码是使用g++编译的,虽然代码是C风格;在我修改了头文件以及一些错误之后,居然链接出错,av_register_all什么的函数统统undefined reference,想到ffmpeg是纯C实现,以及以前用g++编译GTK出现回呼函数找不到的经历,相信又是g++的function mangling搞的,Google了一下,解决方法是把几个头文件包含在exterc "C"里面

    代码里面的错误还涉及一些结构成员的变化,比如

    pFormatCtx->streams[i]->codec.codec_type==CODEC_TYPE_VIDEO

    就有个类型错误,因为0.5.0里面的codec已经是指针,而不是结构了,要把.换成->,而相应地获得解码器指针,不再需要&:

    pCodecCtx=&pFormatCtx->streams[videoStream]->codec;

    原教程提到一个视频码率的rate的Hack:

        // Hack to correct wrong frame rates that seem to be generated by some codecs<br />    if(pCodecCtx->frame_rate>1000 && pCodecCtx->frame_rate_base==1)<br />        pCodecCtx->frame_rate_base=1000;

    好吧现在frame_rate跟frame_rate_base压根就没了,去掉算了;

    最麻烦的变化还是原代码里面的img_convert,就是解码出一个帧的数据后,需要转换成RGB格式才能写入文件,然而这个函数在0.5.0里面彻底没了。尝试把img_convert完全注释掉,把原始帧img_convert写入文件,还算可喜的是能够看到图形,只是被分成三个画面的通道图形罢了;

    Google了一番,还是回到 Stephen Dranger的An FFmpeg and SDL Tutorial第八节,介绍了swscale的接口,虽然里面的例子是转换成SDL所用的YUV,而不是RGB;注意到其使用的参数PIX_FMT_YUV420P,跟img_convert所用的PIX_FMT_RGB24有相同前序,就试试照样花虎了;
    给sws_getContext传入源格式的H/W,格式,输出格式的H/W,PIX_FMT_RGB24格式,其中有个参数flags的解释是 specify which algorithm and options to use for rescaling,是选择在缩放过程中是使用线性还是双立方等算法(参数文档没说,要找看源码去),这里照抄了例子里面的SWS_BICUBIC。获得这个SwsContext,类似python的re.compile,再用这个转换器去转换每一个帧,所以后面每次解码了帧后,调用sws_scale,跟原来的img_convert倒是挺像;

    也就是说,以后如果需要对视频进行4:3跟16:9的转换,就是在sws_getContext的参数里面做设置了;

    最后整个程序正常,会把视频的前5帧抓成图像了,只是会有个小警告:

    [swscaler @ 0x1d8f670]No accelerated colorspace conversion found.

    估计是转换成RGB,swscale里面没有特别优化的算法?

    目前发现程序运行的时候CPU占用很高,是因为程序还没有控制帧速的时钟,只会用尽CPU的性能不断的读取解码;

    PT修改过可编译通过(ffmpeg 5.0/gcc 4.4.2/ubuntu 9.10)的前三个Tutorial代码可在Google Code查看下载。(编译方法请看各个文件头部的注释说明)

  • 相关阅读:
    【LeetCode每日一题】2020.6.28 209. 长度最小的子数组
    【《你不知道的JS(中卷①)》】三、原生函数
    【《你不知道的JS(中卷①)》】二、值
    【《你不知道的JS(中卷①)》】一、类型
    【LeetCode每日一题】2020.6.26 面试题 02.01. 移除重复节点
    【LeetCode周赛】第194场周赛
    【LeetCode每日一题】2020.6.25 139. 单词拆分
    ios网络编程(HTTP socket)
    objective c 代码块blocks完整总结二
    objective c 代码块blocks完整总结一
  • 原文地址:https://www.cnblogs.com/moonvan/p/2221292.html
Copyright © 2011-2022 走看看