<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>apt-blog.net   无证程序员的PT桑 &#187; img_convert</title>
	<atom:link href="http://apt-blog.net/tag/img_convert/feed" rel="self" type="application/rss+xml" />
	<link>http://apt-blog.net</link>
	<description>潜逃中。</description>
	<lastBuildDate>Fri, 18 May 2012 11:25:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>ffmpeg API 笔记：使用libavcodec/libavformat/libswscale</title>
		<link>http://apt-blog.net/using_ffmpeg_api</link>
		<comments>http://apt-blog.net/using_ffmpeg_api#comments</comments>
		<pubDate>Fri, 11 Dec 2009 03:00:51 +0000</pubDate>
		<dc:creator>BOYPT</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[ffmpeg]]></category>
		<category><![CDATA[img_convert]]></category>
		<category><![CDATA[libavcodec]]></category>
		<category><![CDATA[libavformat]]></category>
		<category><![CDATA[libswscale]]></category>
		<guid isPermaLink="false">http://apt-blog.net/?p=673</guid>
		<description><![CDATA[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 byMartin Böhme（以及其Update，介绍了新引入的API）跟An FFmpeg and SDL Tutorialby 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&#40;&#41;;//初始化ffmpeg库，如果系统里面的ffmpeg没配置好这里会出错 av_open_input_file&#40;&#41;; av_find_stream_info&#40;&#41;;//查找文件的流信息 dump_format&#40;&#41;;//dump只是个调试函数，输出文件的音、视频流的基本信息了，帧率、分辨率、音频采样等等 for&#40;...&#41;;//遍历文件的各个流，找到第一个视频流，并记录该流的编码信息 sws_getContext&#40;&#41;;//根据编码信息设置渲染格式 avcodec_find_decoder&#40;&#41;;//在库里面查找支持该格式的解码器 avcodec_open&#40;&#41;;//打开解码器 pFrame=avcodec_alloc_frame&#40;&#41;;//分配一个帧指针，指向解码后的原始帧 pFrameRGB=avcodec_alloc_frame&#40;&#41;;//分配一个帧指针，指向存放转换成RGB后的帧 avpicture_fill&#40;pFrameRGB&#41;;//给pFrameRGB帧加上分配的内存; while true&#123; av_read_frame&#40;&#41;;//读取一个帧（到最后帧则break） avcodec_decode_video&#40;&#41;;//解码该帧 sws_scale&#40;&#41;;//把该帧转换（渲染）成RGB SaveFrame&#40;&#41;;//对前5帧保存成ppm图形文件(这个是自定义函数，非API) av_free_packet&#40;&#41;;//释放本次读取的帧内存 &#125; avcodec_close&#40;&#41;; av_close_input_file&#40;&#41;; 用到的API就这么多，当然实际代码稍复杂一点；ppm图像是类似BMP的非压缩格式，SaveFrame就是相当于把pFrameRGB的内存拷贝进文件，写文件并不复杂； 调试过程的问题，首先是头文件，ffmpeg 0.5.0的API已经拆分成好几个独立的库，用pacman -Ql ffmpeg看了下文件分布，在include下好几个目录都是它的，看名字可以大概猜出他们的功能： libavcodec：CODEC其实是Coder/Decoder的缩写，也就是编码解码器；libavdevice：对输出输入设备的支持；libavformat：对音频视频格式的解析libavutil：集项工具；libpostproc：后期效果处理；libswscale：视频场景比例缩放、色彩映射转换； 修改好头文件包含，终于少了些not [...]]]></description>
			<content:encoded><![CDATA[<p><b>Update 2010.1.5:</b> 其实研究ffmpeg不用找什么教程，第一步应该是下载ffmpeg的源码包。下面提到的<a href="http://www.dranger.com/ffmpeg/">An FFmpeg and SDL Tutorial</a>确实有讲解，但是教程总是跟不上代码的变化的，所以直接看可工作代码最好；ffmpeg的结构很分明，后台是几个库：libxxx，前台是三个程序ffmpeg, ffplay, ffserver，那篇教程说的就是ffplay的实现。一个播放器，其实重点不是解码，解码的东西是lib去做的，主要是做声音视频的时钟同步。ffplay的代码可以说是一个可用播放器最简单的实现了，源码里面有个output_example.c，可以说是最基本的api示范吧。ffmpeg是转换编码解码转换程序，因为涉及重新采样等等，所以代码量也不少的。<br />
<hr />
<p>这两天"调研"了下ffmpeg的API，不得不承认被雷倒：ffmpeg又是一个很geek的项目，纯社区开发，基于逆向，功能强大，但是文档极度有限，想了解API？看源码去…… 网上关于ffmpeg API的资料，无非是<a target="_blank" href="http://ffmpeg.org/documentation.html">ffmpeg文档</a>里面的两个链接，<a href="http://www.inb.uni-luebeck.de/%7Eboehme/using_libavcodec.html">Using libavformat and libavcodec</a> by<br />Martin Böhme（<a target="_blank" href="http://www.inb.uni-luebeck.de/%7Eboehme/libavcodec_update.html">以及其Update，介绍了新引入的API</a>）跟<a href="http://www.dranger.com/ffmpeg/">An FFmpeg and SDL Tutorial</a><br />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年加入的更新说明，<a target="_blank" href="http://web.me.com/dhoerl/Home/Tech_Blog/Entries/2009/1/22_Revised_avcodec_sample.c.html">链接了有相关的解决办法</a>，我一开始没注意，几个小时自己解决，不过也有收获）。</p>
<p>最后我调试好的代码流程：<span style="font-weight: bold;">打开一个视频文件，抓取前5帧保存为文件；</span><br />【<a target="_blank" href="http://www.dranger.com/ffmpeg/tutorial01.html">基于Stephen Dranger的Tutorial1</a>】<a target="_blank" href="http://ptcoding.googlecode.com/svn/trunk/ffmpeg_tutorial/tutorial01.c">源码在此：GoogleCode</a></p>
<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">av_register_all<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//初始化ffmpeg库，如果系统里面的ffmpeg没配置好这里会出错</span>
av_open_input_file<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
av_find_stream_info<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//查找文件的流信息</span>
dump_format<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//dump只是个调试函数，输出文件的音、视频流的基本信息了，帧率、分辨率、音频采样等等</span>
<span style="color: #b1b100;">for</span><span style="color: #009900;">&#40;</span>...<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//遍历文件的各个流，找到第一个视频流，并记录该流的编码信息</span>
sws_getContext<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//根据编码信息设置渲染格式</span>
avcodec_find_decoder<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//在库里面查找支持该格式的解码器</span>
avcodec_open<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//打开解码器</span>
pFrame<span style="color: #339933;">=</span>avcodec_alloc_frame<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//分配一个帧指针，指向解码后的原始帧</span>
pFrameRGB<span style="color: #339933;">=</span>avcodec_alloc_frame<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//分配一个帧指针，指向存放转换成RGB后的帧</span>
avpicture_fill<span style="color: #009900;">&#40;</span>pFrameRGB<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//给pFrameRGB帧加上分配的内存;</span>
<span style="color: #b1b100;">while</span> <span style="color: #000000; font-weight: bold;">true</span><span style="color: #009900;">&#123;</span>
     av_read_frame<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//读取一个帧（到最后帧则break）</span>
     avcodec_decode_video<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//解码该帧</span>
     sws_scale<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//把该帧转换（渲染）成RGB</span>
     SaveFrame<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//对前5帧保存成ppm图形文件(这个是自定义函数，非API)</span>
     av_free_packet<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//释放本次读取的帧内存</span>
<span style="color: #009900;">&#125;</span>
avcodec_close<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
av_close_input_file<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>
<p>用到的API就这么多，当然实际代码稍复杂一点；ppm图像是类似BMP的非压缩格式，SaveFrame就是相当于把pFrameRGB的内存拷贝进文件，写文件并不复杂；</p>
<p>调试过程的问题，首先是头文件，ffmpeg 0.5.0的API已经拆分成好几个独立的库，用pacman -Ql ffmpeg看了下文件分布，在include下好几个目录都是它的，看名字可以大概猜出他们的功能：<br />
<blockquote>libavcodec：CODEC其实是Coder/Decoder的缩写，也就是编码解码器；<br />libavdevice：对输出输入设备的支持；<br />libavformat：对音频视频格式的解析<br />libavutil：集项工具；<br />libpostproc：后期效果处理；<br />libswscale：视频场景比例缩放、色彩映射转换；</p></blockquote>
<p>修改好头文件包含，终于少了些not declared错误；</p>
<p>Martin Böhme那篇教程的代码是使用g++编译的，虽然代码是C风格；在我修改了头文件以及一些错误之后，居然链接出错，av_register_all什么的函数统统undefined reference，想到ffmpeg是纯C实现，以及以前用g++编译GTK出现回呼函数找不到的经历，相信又是g++的function mangling搞的，Google了一下，<a target="_blank" href="http://inming.net/?p=10">解决方法是把几个头文件包含在exterc "C"里面</a>。</p>
<p>代码里面的错误还涉及一些结构成员的变化，比如</p>
<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">pFormatCtx<span style="color: #339933;">-&gt;</span>streams<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">-&gt;</span>codec.<span style="color: #202020;">codec_type</span><span style="color: #339933;">==</span>CODEC_TYPE_VIDEO</pre></div></div>
<p>就有个类型错误，因为0.5.0里面的codec已经是指针，而不是结构了，要把.换成-&gt;，而相应地获得解码器指针，不再需要&amp;：</p>
<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">pCodecCtx<span style="color: #339933;">=&amp;</span>pFormatCtx<span style="color: #339933;">-&gt;</span>streams<span style="color: #009900;">&#91;</span>videoStream<span style="color: #009900;">&#93;</span><span style="color: #339933;">-&gt;</span>codec<span style="color: #339933;">;</span></pre></div></div>
<p>原教程提到一个视频码率的rate的Hack：</p>
<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">    <span style="color: #666666; font-style: italic;">// Hack to correct wrong frame rates that seem to be generated by some codecs&lt;br /&gt;    if(pCodecCtx-&gt;frame_rate&gt;1000 &amp;&amp; pCodecCtx-&gt;frame_rate_base==1)&lt;br /&gt;        pCodecCtx-&gt;frame_rate_base=1000;</span></pre></div></div>
<p>好吧现在frame_rate跟frame_rate_base压根就没了，去掉算了；</p>
<p>最麻烦的变化还是原代码里面的img_convert，就是解码出一个帧的数据后，需要转换成RGB格式才能写入文件，然而这个函数在0.5.0里面彻底没了。尝试把img_convert完全注释掉，把原始帧img_convert写入文件，还算可喜的是能够看到图形，只是被分成三个画面的通道图形罢了；</p>
<p>Google了一番，还是回到 Stephen Dranger的<a href="http://www.dranger.com/ffmpeg/tutorial08.html">An FFmpeg and SDL Tutorial第八节</a>，介绍了swscale的接口，虽然里面的例子是转换成SDL所用的YUV，而不是RGB；注意到其使用的参数PIX_FMT_YUV420P，跟img_convert所用的PIX_FMT_RGB24有相同前序，就试试照样花虎了；<br />给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倒是挺像；</p>
<p>也就是说，以后如果需要对视频进行4:3跟16:9的转换，就是在sws_getContext的参数里面做设置了；</p>
<p>最后整个程序正常，会把视频的前5帧抓成图像了，只是会有个小警告：<br />
<blockquote>[swscaler @ 0x1d8f670]No accelerated colorspace conversion found.</p></blockquote>
<p>估计是转换成RGB，swscale里面没有特别优化的算法？</p>
<p>目前发现程序运行的时候CPU占用很高，是因为程序还没有控制帧速的时钟，只会用尽CPU的性能不断的读取解码；</p>
<p>PT修改过可编译通过（ffmpeg 5.0/gcc 4.4.2/ubuntu 9.10）的前三个Tutorial代码可在<a target="_blank" href="http://code.google.com/p/ptcoding/source/browse/trunk/ffmpeg_tutorial/">Google Code查看下载</a>。（编译方法请看各个文件头部的注释说明）</p>
]]></content:encoded>
			<wfw:commentRss>http://apt-blog.net/using_ffmpeg_api/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

