多媒体的时代,得多了解点编解码的技术才行,而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. 一致性哈希与java实现

    一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具体的节点上,如果采用普通的hash方法,将数据映射到具体的节点上,如key%N,key是数据的key,N是机器节点数 ...

  2. rsync+inotify实时同步方案

    rsync+inotify实时同步,inotify可以实时监控本地文件或目录变化,当检测到本地文件变化,执行rsync同步命令,将变化的文件同步到其他服务器节点. 1.配置环境 3.在服务节点1.服务 ...

  3. 一个支持实时预览的在线 Markdown 编辑器 - Markdoc

    最近组内需要为一些项目和系统写文档,发表在公司内的文档平台上,这个平台并不支持markdown,所以打算做一个在线markdown编辑器,支持实时预览,并且可以很容易的迁移发表到公司文档平台上,所以就 ...

  4. github多人协作

    1.字符串处理(编码原理) git clone git@github.com:lookphp/LaravelCms.git git add . git commit -m "修改的内容-需要 ...

  5. LeetCode_Set Matrix Zeroes

    Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place. 很挫的一个想 ...

  6. AOP技术基础

    1.引言 2.AOP技术基础 3.Java平台AOP技术研究 4..Net平台AOP技术研究 2.1 AOP技术起源 AOP技术的诞生并不算晚,早在1990年开始,来自Xerox Palo Alto ...

  7. rsyslog 收集系统日志

    <pre name="code" class="html">nginx 服务器配置: jrhwpt01:/root# cat /etc/rsyslo ...

  8. html 表单初步学习

    <html> <head> <title> 静态页面</title> </head> <body> 这是一个静态页面<br ...

  9. poj 3180 The Cow Prom(tarjan+缩点 easy)

    Description The N ( <= N <= ,) cows are so excited: it's prom night! They are dressed in their ...

  10. PHP初识

    1. php是一种跨平台的语言,支持几乎全部的数据库. 以前觉得PHP与MYSQL是黄金组合,对于PHP能否支持MSSQL没有过了解,PHP支持几乎全部的数据库,也支持MSSQL(5.2.X版本可以用 ...