多媒体的时代,得多了解点编解码的技术才行,而ffmpeg为我们提供了一系列多媒体编解码的接口,如何用好这些接口达到自己所需要的目的,这也是一门重要的学问。

要是了解得不够,总是会遇到一堆又一堆问题;网上关于ffmpeg的讲解,说少也不少,说多也不多,由于版本更新又更新,能找着的资料基本上都不大能对得上,需要进行一定量的修改才能正常工作;所以,我也借着这个机会,重新走一遍ffmpeg的入门,然后理清同步等问题。

本文主要讲的是ffmpeg解码最基本的步骤,以及其用到的接口,另附有完整的实例代码。

使用工具:FFMPEG(2.0.1)、VS2010

平台:WINDOWS

下面就开始吧……

步骤一:初始化ffmpeg(必须的)

av_register_all();
avcodec_register_all();

  使用以上语句初始化ffmpeg的功能,简单,没什么好讲的。

步骤二:打开多媒体文件,检查头部信息

AVFormatContext *pFormatCtx = avformat_alloc_context();;
// Open video file
if(avformat_open_input(&pFormatCtx, filename, NULL, NULL)!=0)
return -1; // Couldn't open file

  avformat_open_input()这个函数读取文件的头部并且把信息保存到我们给的AVFormatContext结构体中,注意要先调用avformat_alloc_context()为我们的结构体申请空间,否则读取文件的信息无法保存,avformat_open_input会失败。

步骤三:检查文件中的流的信息

// Retrieve stream information
if(av_find_stream_info(pFormatCtx)<0)
return -1; // Couldn't find stream information
// Dump information about file onto standard error
av_dump_format(pFormatCtx, 0, filename, 0);

  av_find_stream_info()这个函数为pFormatCtx->streams填充上正确的信息。av_dump_format()是一个手工调试的函数,能使我们看到pFormatCtx->streams里面有什么内容。

步骤四:找出文件中的视频流

int i;
AVCodecContext *pCodecCtx;
// Find the first video stream
int videoStream=-1;
for(i=0; i<pFormatCtx->nb_streams; i++) {
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
videoStream=i;
break;
}
}
if(videoStream==-1)
return -1; // Didn't find a video stream
// Get a pointer to the codec context for the video stream
pCodecCtx=pFormatCtx->streams[videoStream]->codec;

  pFormatCtx->streams仅仅是一组大小为pFormatCtx->nb_streams的指针,所以让我们可以使用它找到一个视频流,并记录其在stream中的下标号(后面根据此标号可以与该视频匹配)。当然,视频流有可能不止一个,如果想要把所有视频流都找到,可以使用数组记录,就不需要break了。

AVCodecContext,流中关于编解码器的信息就是被我们叫做"codec context"(编解码器上下文)的东西。这里面包含了流中所使用的关于编解码器的所有信息,现在我们有了一个指向他的指针。

步骤五:找到该视频流真正的编解码器并且打开它

AVCodec *pCodec;
// Find the decoder for the video stream
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL) {
fprintf(stderr, "Unsupported codec!\n");
return -1; // Codec not found
}
// Open codec
if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
return -1; // Could not open codec

  avcodec_find_decoder()根据找到的视频流格式,找到想应的编解码器;avcodec_open2()可以开启我们的解码器使其处于随时可工作的状态,因为ffmpeg版本更新过许多次,所以有些Function名字都会带一些数字。

步骤六:申请数据空间(保存帧数据用)

AVFrame *pFrame, *pFrameRGB;
// Allocate video frame
pFrame=avcodec_alloc_frame();
// Allocate an AVFrame structure
pFrameRGB=avcodec_alloc_frame();
if(pFrameRGB==NULL)
return -1;
uint8_t *buffer;
int numBytes;
// Determine required buffer size and allocate buffer
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
pCodecCtx->height);
buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
// Assign appropriate parts of buffer to image planes in pFrameRGB
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
// of AVPicture
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height);

  AVFrame就是用来保存帧数据的,之所以定义了两个,是因为解码后帧数据一般是YUV格式,我们转换成RGB的帧数据,还需要一个存储的地方。我们使用avpicture_get_size()来获得我们需要的大小,然后用av_malloc手工申请内存空间,avpicture_fill来把帧和我们新申请的内存来结合。

步骤七:读取数据并解码

int frameFinished;
AVPacket packet;
i=0;
while(av_read_frame(pFormatCtx, &packet)>=0) {
// Is this a packet from the video stream?
if(packet.stream_index==videoStream) {
// Decode video frame
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
// Did we get a video frame?
if(frameFinished) {
// Convert the image from its native format to RGB
static struct SwsContext *img_convert_ctx;
img_convert_ctx = sws_getCachedContext(img_convert_ctx,
pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24,
SWS_FAST_BILINEAR, NULL, NULL, NULL);
if (!pCodecCtx)
{
printf("Cannot initialize sws conversion context\r\n");
return NULL;
}
sws_scale(img_convert_ctx,
(const uint8_t* const*)pFrame->data, pFrame->linesize, 0,
pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); // Save the frame to disk
if(++i<=5)
SaveFrame(pFrameRGB, pCodecCtx->width,
pCodecCtx->height, i);
}
}
// Free the packet that was allocated by av_read_frame
av_free_packet(&packet);
}

  这个循环过程是比较简单的:av_read_frame()读取一个包并且把它保存到AVPacket结构体中。注意我们仅仅申请了一个包的结构体 ――ffmpeg为我们申请了内部的数据的内存并通过packet.data指针来指向它。这些数据可以在后面通过av_free_packet()来释放。

函数avcodec_decode_video2()把包转换为帧。然而当解码一个包的时候,我们可能没有得到我们需要的关于帧的信息。因此,当我们得到下一帧的时候,avcodec_decode_video2()为我们设置了帧结束标志frameFinished。

然后,我们使用sws_getCachedContext()函数设置格式转换器,sws_scale()函数来把帧从原始格式(pCodecCtx->pix_fmt)转换成为RGB格式。最后,我们把帧和高度宽度信息传递给我们的SaveFrame()函数,保存成PPM的图片。

步骤八:清理一切,程序结束

// Free the RGB image
av_free(buffer);
av_free(pFrameRGB);
// Free the YUV frame
av_free(pFrame);
// Close the codec
avcodec_close(pCodecCtx);
// Close the video file
av_close_input_file(pFormatCtx);
return 0;

  一旦我们开始读取完视频流,退出前需要释放所有申请的空间。

以上,就是ffmpeg解码的基本流程。最近也比较忙,时间也比较晚了,累了犯困,就不在此赘述环境的配置了,附上工程下载地址有兴趣的自行研究吧!

同时,欢迎广大志同道合的朋友讨论交流,要知道,You are not alone !

DownLoad_SourceCode_ClickHere (免分哦,亲!)

FFMPEG视音频解码【一】的更多相关文章

  1. [转]FFMPEG视音频编解码零基础学习方法

    在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“ ...

  2. [总结]FFMPEG视音频编解码零基础学习方法--转

    ffmpeg编解码学习   目录(?)[-] ffmpeg程序的使用ffmpegexeffplayexeffprobeexe 1 ffmpegexe 2 ffplayexe 3 ffprobeexe ...

  3. FFMPEG视音频编解码零基础学习方法

    在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“ ...

  4. FFMPEG视音频编解码零基础学习方法-b

    感谢大神分享,虽然现在还看不懂,留着大家一起看啦 PS:有不少人不清楚“FFmpeg”应该怎么读.它读作“ef ef em peg” 0. 背景知识 本章主要介绍一下FFMPEG都用在了哪里(在这里仅 ...

  5. [总结]FFMPEG视音频编解码零基础学习方法

    在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“ ...

  6. 【转】[总结]FFMPEG视音频编解码零基础学习方法

    在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“ ...

  7. FFMPEG视音频编解码零基础学习方法 【荐】

    在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频 编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在 ...

  8. [转载] FFMPEG视音频编解码零基础学习方法

    在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“ ...

  9. 转[总结]FFMPEG视音频编解码零基础学习方法 .

    http://blog.csdn.net/leixiaohua1020/article/details/15811977 在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视 ...

随机推荐

  1. 表格table常见的边框设置和初步的立体效果

    做网页时经常会遇到表格,常见的表格如下: <style type="text/css"> .tbtest0 { width:500px; height:200px; b ...

  2. css 溢出文本显示省略号

    这个标题其实已经是一个老生常谈的问题了.很多时候,比如网站最基本的文章列表,标题会很长,而显示列表的区域宽度却没有这么宽,这时候最正常的做法就是 让超出宽度的部分文字用省略号(…)来表示.通常做法是网 ...

  3. Gizmos绘制塔防游戏网格

    1.画线脚本写法及地面调整 首先,新建脚本GridMap 新建绘制网格方法(初始数值为10*10) 在类中创建变量 //定义地图宽度 public int m_mapWidth = 10; //地图高 ...

  4. AFNetworking了解

    AFNetworking了解   AFNetworking是一个讨人喜欢的网络库,适用于iOS以及Mac OS X. 它构建于在NSURLConnection, NSOperation, 以及其他熟悉 ...

  5. 提交 应用ID 证书

    https://developer.apple.com/account/ios/profile/profileCreate.action?formID=960914622

  6. HDU 2457 DNA repair (AC自动机+DP)

    题意:给N个串,一个大串,要求在最小的改变代价下,得到一个不含上述n个串的大串. 思路:dp,f[i][j]代表大串中第i位,AC自动机上第j位的最小代价. #include<algorithm ...

  7. 用continue语句的时候,要千万小心内存泄漏,当然还有return和break也是

    疑惑了大半年的内存泄漏,居然是因为这个原因- 有空学学QT的指针使用,可以使得代码更简洁.更不容易内存泄漏-

  8. SDL入门教程(一):3、MinGW 下的安装与设置

    作者:龙飞 3.1:MinGW 是什么? MinGW 提供了一套简单方便的Windows下的基于GCC 程序开发环境.MinGW 收集了一系列免费的Windows 使用的头文件和库文件:同时整合了GN ...

  9. sign starfieldtech

    signtool sign /f certfile.pfx /p password /tr http://tsa.starfieldtech.com /td SHA256 mycode.exe htt ...

  10. Android 有缓存功能的请求封装接口

    /* * @Company 浙 江 鸿 程 计 算 机 系 统 有 限 公 司 * @URL http://www.zjhcsoft.com * @Address 杭州滨江区伟业路1号 * @Emai ...