FFMpeg的解码流程



1. 从基础谈起

先给出几个概念,以在后面的分析中方便理解

Container:在音视频中的容器,一般指的是一种特定的文件格式,里面指明了所包含的

音视频,字幕等相关信息

Stream:这个词有些微妙,很多地方都用到,比如TCP,SVR4系统等,其实在音视频,你

可以理解为单纯的音频数据或者视频数据等

Frames:这个概念不是很好明确的表示,指的是Stream中的一个数据单元,要真正对这

个概念有所理解,可能需要看一些音视频编码解码的理论知识

Packet:是Stream的raw数据

Codec:Coded + Decoded

其实这些概念在在FFmpeg中都有很好的体现,我们在后续分析中会慢慢看到



2.解码的基本流程

我很懒,于是还是选择了从<An ffmpeg and SDL Tutorial>中的流程概述:



10 OPEN video_stream FROM video.avi

20 READ packet FROM video_stream INTO frame

30 IF frame NOT COMPLETE GOTO 20

40 DO SOMETHING WITH frame

50 GOTO 20



这就是解码的全过程,一眼看去,是不是感觉不过如此:),不过,事情有深有浅,从浅

到深,然后从深回到浅可能才是一个有意思的过程,我们的故事,就从这里开始,展开

来讲。



3.例子代码

在<An ffmpeg and SDL Tutorial 1>中,给出了一个阳春版的解码器,我们来仔细看看

阳春后面的故事,为了方便讲述,我先贴出代码:

#include <ffmpeg/avcodec.h>

#include <ffmpeg/avformat.h>



#include <stdio.h>



void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {

FILE *pFile;

char szFilename[32];

int  y;



// Open file

sprintf(szFilename, "frame%d.ppm", iFrame);

pFile=fopen(szFilename, "wb");

if(pFile==NULL)

return;



// Write header

fprintf(pFile, "P6\n%d %d\n255\n", width, height);



// Write pixel data

for(y=0; y<height; y++)

fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);



// Close file

fclose(pFile);

}



int main(int argc, char *argv[]) {

AVFormatContext *pFormatCtx;

int             i, videoStream;

AVCodecContext  *pCodecCtx;

AVCodec         *pCodec;

AVFrame         *pFrame;

AVFrame         *pFrameRGB;

AVPacket        packet;

int             frameFinished;

int             numBytes;

uint8_t         *buffer;



if(argc < 2) {

printf("Please provide a movie file\n");

return -1;

}

// Register all formats and codecs

########################################

[1]

########################################

av_register_all();



// Open video file

########################################

[2]

########################################

if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)

return -1; // Couldn't open file



// Retrieve stream information

########################################

[3]

########################################

if(av_find_stream_info(pFormatCtx)<0)

return -1; // Couldn't find stream information



// Dump information about file onto standard error

dump_format(pFormatCtx, 0, argv[1], 0);



// Find the first video stream

videoStream=-1;

for(i=0; i<pFormatCtx->nb_streams; i++)

if(pFormatCtx->streams[i]->codec->codec_type==CODEC_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;



// 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_open(pCodecCtx, pCodec)<0)

return -1; // Could not open codec



// Allocate video frame

pFrame=avcodec_alloc_frame();



// Allocate an AVFrame structure

pFrameRGB=avcodec_alloc_frame();

if(pFrameRGB==NULL)

return -1;



// 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);



// Read frames and save first five frames to disk

########################################

[4]

########################################

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_video(pCodecCtx, pFrame, &frameFinished,

packet.data, packet.size);



// Did we get a video frame?

if(frameFinished) {

// Convert the image from its native format to RGB

img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24,

(AVPicture*)pFrame, pCodecCtx->pix_fmt,

pCodecCtx->width,

pCodecCtx->height);



// 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);

}



// 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;

}



代码注释得很清楚,没什么过多需要讲解的,关于其中的什么YUV420,RGB,PPM等格式

,如果不理解,麻烦还是google一下,也可以参考:http://barrypopy.cublog.cn/里面

的相关文章



其实这部分代码,很好了Demo了怎么样去抓屏功能的实现,但我们得去看看魔术师在后

台的一些手法,而不只是简单的享受其表演。



4.背后的故事

真正的难度,其实就是上面的[1],[2],[3],[4],其他部分,都是数据结构之间的转换,

如果你认真看代码的话,不难理解其他部分。



[1]:没什么太多好说的,如果不明白,看我转载的关于FFmepg框架的文章



[2]:先说说里面的AVFormatContext *pFormatCtx结构,字面意思理解AVFormatContext

就是关于AVFormat(其实就是我们上面说的Container格式)的所处的Context(场景),自

然是保存Container信息的总控结构了,后面你也可以看到,基本上所有的信息,都可

以从它出发而获取到



我们来看看av_open_input_file()都做了些什么:

[libavformat/utils.c]

int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,

AVInputFormat *fmt,

int buf_size,

AVFormatParameters *ap)

{

......

if (!fmt) {

/* guess format if no file can be opened */

fmt = av_probe_input_format(pd, 0);

}



......

err = av_open_input_stream(ic_ptr, pb, filename, fmt, ap);

......

}



这样看来,只是做了两件事情:

1). 侦测容器文件格式

2). 从容器文件获取Stream的信息



这两件事情,实际上就是调用特定文件的demuxer以分离Stream的过程:



具体流程如下:



av_open_input_file

|

+---->av_probe_input_format从first_iformat中遍历注册的所有demuxer以

|     调用相应的probe函数

|

+---->av_open_input_stream调用指定demuxer的read_header函数以获取相关

流的信息ic->iformat->read_header



如果反过来再参考我转贴的关于ffmpeg框架的文章,是否清楚一些了呢:)



[3]:简单从AVFormatContext获取Stream的信息,没什么好多说的



[4]:先简单说一些ffmpeg方面的东西,从理论角度说过来,Packet可以包含frame的部

分数据,但ffmpeg为了实现上的方便,使得对于视频来说,每个Packet至少包含一

frame,对于音频也是相应处理,这是实现方面的考虑,而非协议要求.

因此,在上面的代码实际上是这样的:

从文件中读取packet,从Packet中解码相应的frame;

从帧中解码;

if(解码帧完成)

do something();



我们来看看如何获取Packet,又如何从Packet中解码frame的。



av_read_frame

|

+---->av_read_frame_internal

|

+---->av_parser_parse调用的是指定解码器的s->parser->parser_parse函数以从raw packet中重构frame



avcodec_decode_video

|

+---->avctx->codec->decode调用指定Codec的解码函数



因此,从上面的过程可以看到,实际上分为了两部分:



一部分是解复用(demuxer),然后是解码(decode)



使用的分别是:

av_open_input_file()            ---->解复用



av_read_frame()                  |

|    ---->解码

avcodec_decode_video()     |



5.后面该做些什么

结合这部分和转贴的ffmepg框架的文章,应该可以基本打通解码的流程了,后面的问题则是针对具体容器格式和具体编码解码器的分析,后面我们继续





参考:

[1]. <An ffmpeg and SDL Tutorial>

http://www.dranger.com/ffmpeg/tutorial01.html



[2]. <FFMpeg框架代码阅读>

http://blog.csdn.net/wstarx/archive/2007/04/20/1572393.aspx

FFMpeg的解码流程相关推荐

  1. 264编码基本概念 FFMpeg的解码流程

    下面转自http://topic.csdn.net/u/20081020/16/7156e0b2-dbfb-4b4f-af59-2be04cf9a420.html 的8楼 1.NAL.Slice与fr ...

  2. FFmpeg音频解码流程详解及简单demo参考

    本文主要讲解FFmpeg的音频解码具体流程,API使用.最后再以一个非常简单的demo演示将一个mp3格式的音频文件解码为原始数据pcm文件. 本文主要基于FFmpeg音频解码新接口. 一.FFmpe ...

  3. 视频直播流程以及ffmpeg编解码流程

    去年最流行的非视频直播莫属,出现了几百家的直播平台,毕竟自己也是在直播平台做开发的,然后就写一篇关于直播的文章.这里只说流程,具体的实现就不说了,应付面试应该还是可以的. 先上一张简单的流程图: 上面 ...

  4. ffmpeg 编解码流程

    去年最流行的非视频直播莫属,出现了几百家的直播平台,毕竟自己也是在直播平台做开发的,然后就写一篇关于直播的文章.这里只说流程,具体的实现就不说了,应付面试应该还是可以的. 先上一张简单的流程图: 上面 ...

  5. FFmpeg解码流程简介

    本文基于雷神的<基于 FFmpeg + SDL 的视频播放器的制作>课程的视频 ,本文就是基于该系列文章的学习后,总结出来的学习经验.如果想细致了解更多方法的使用,可以参考雷神的FFmpe ...

  6. ffmpeg解码流程 turorial5详解

    From: http://www.360doc.com/content/11/1117/09/8050095_165108638.shtml FFMPEG解码流程 1. 注册所有容器格式和CODEC: ...

  7. 【FFmpeg视频播放器开发】解封装解码流程、常用API和结构体简介(一)

    一.前言 在正式编写 FFmpeg 播放器前,我们需要先简单了解下所要用到的 FFmpeg 库.播放与解码流程.函数和相关结构体. 二.FFmpeg 库简介 库 介绍 avcodec 音视频编解码核心 ...

  8. ffmpeg学习——基本的解码流程

    由于工作需要,所以基本了解了一下视频的解码流程. 参考教程为: 1.王纲的<跟我一起学FFmpeg>系列 2.雷霄骅雷神的博客 原理部分暂时没有整理,后期可以补充一下知识. ffmepg的 ...

  9. FFMPEG解码流程

    FFMPEG解码流程:   1. 注册所有容器格式和CODEC: av_register_all()   2. 打开文件: av_open_input_file()   3. 从文件中提取流信息: a ...

最新文章

  1. MYSQL触发器记录用户操作的命令
  2. 虚拟机ubuntu启动代码界面_Windows10下使用虚拟机安装Ubuntu18.04
  3. linux和android调试概要
  4. HiveSQL常用数据处理语句
  5. MySQL外键关联(一对多)MySQL连接查询
  6. 8.10模拟:贪心、最优化思路
  7. 网站安全之为Web项目添加验证码功能(一)
  8. 基于遗传算法的水力发电厂的优化(Matlab代码实现)
  9. 台式计算机声音输出方式在哪里,电脑声音无法找到输出设备怎么办?
  10. VBA在工作中的应用-批量添加工资条表头的VBA代码
  11. 我,开了五年快递站,现在还没回本
  12. 舞蹈课 (C++堆的优先级与重载)
  13. android虚拟手机云之二:应用多开
  14. php csv文件转json,php csv如何转json
  15. 图片字节数组的获取,字节数组图片的保存
  16. vuex四大核心元素
  17. 二维码扫码登录的原理
  18. 计算机网络第一章知识总结
  19. HDU 5374 模拟俄罗斯方块
  20. 夸计算机老师的成语,四字夸奖老师的成语

热门文章

  1. SwiftUI学习笔记之异步数据请求
  2. 如何彻底删除微信聊天记录?原来删除好友并不能彻底清除!
  3. 融合计算机科学统计学脑神经学,乐橙 TP7S 摄像机:AI 智能识别,实现更多智能...
  4. JavaScript版拼图小游戏
  5. Auto CAD 2021安装教程【64位】
  6. Java学习之操作符(三)
  7. python中深拷贝与浅拷贝的区别
  8. python 安装Crypto库
  9. audio常用的事件及方法整理
  10. sis防屏蔽程序_手机安全防“四害”