码迷,mamicode.com
首页 > 其他好文 > 详细

最简单的基于FFMPEG的转码程序分析 +ffmpga代码简析(转 +总结)

时间:2015-06-09 15:27:21      阅读:2078      评论:0      收藏:0      [点我收藏+]

标签:

  模块:

    libavcodec    - 编码解码器
        libavdevice   - 输入输出设备的支持
        libavfilter   - 视音频滤镜支持
        libavformat   - 视音频等格式的解析
        libavutil     - 工具库
        libpostproc   - 后期效果处理
        libswscale    - 图像颜色、尺寸转换

1. ffmpga代码简析

1.1 av_log()

  av_log()是FFmpeg中输出日志的函数。随便打开一个FFmpeg的源代码文件,就会发现其中遍布着av_log()函数。一般情况下FFmpeg类库的源代码中是不允许使用printf()这种的函数的,所有的输出一律使用av_log()。av_log()的声明位于libavutil\log.h,如下所示。                        void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4); 

这个函数的声明有两个地方比较特殊:

(1)函数最后一个参数是“…”。
在C语言中,在函数参数数量不确定的情况下使用“…”来代表参数。例如printf()的原型定义如下:  int printf (const char*, ...);  

 

(2)它的声明后面有一个av_printf_format(3, 4)。有关这个地方的左右还没有深入研究,网上资料中说它的作用是按照printf()的格式检查av_log()的格式。

av_log()每个字段的含义如下:
avcl:指定一个包含AVClass的结构体。
level:log的级别
fmt:和printf()一样。

由此可见,av_log()和printf()的不同主要在于前面多了两个参数。其中第一个参数指定该log所属的结构体,例如AVFormatContext、AVCodecContext等等。第二个参数指定log的级别,源代码中定义了如下几个级别:AV_LOG_PANIC,AV_LOG_FATAL,AV_LOG_ERROR,AV_LOG_WARNING,AV_LOG_INFO,AV_LOG_VERBOSE,AV_LOG_DEBUG。 每个级别定义的数值代表了严重程度,数值越小代表越严重。默认的级别是AV_LOG_INFO。此外,还有一个级别不输出任何信息,即 AV_LOG_QUIET。

当前系统存在着一个“Log级别”。所有严重程度高于该级别的Log信息都会输出出来。例如当前的Log级别是 AV_LOG_WARNING,则会输出AV_LOG_PANIC,AV_LOG_FATAL,AV_LOG_ERROR,AV_LOG_WARNING 级别的信息,而不会输出AV_LOG_INFO级别的信息。可以通过av_log_get_level()获得当前Log的级别,通过另一个函数 av_log_set_level()设置当前的Log级别。

 

1.1 int _tmain(int argc, _TCHAR* argv[])

  用过C的人都知道每一个C的程序都会有一个main(),但有时看别人写的程序发现主函数不是int main(),而是int _tmain(),而且头文件也不是<iostream.h>而是<stdafx.h>,会困惑吧?首先,这个_tmain()是为了支持unicode所使用的main一个别名而已,既然是别名,应该有宏定义过的,在哪里定义的呢?就在那个让你困惑的<stdafx.h>里,有这么两行
  #include <stdio.h>
  #include <tchar.h>
我们可以在头文件<tchar.h>里找到_tmain的宏定义
  #define _tmain main
所以,经过预编译以后, _tmain就变成main了

//_TCHAR类型是宽字符型字符串,和我们一般常用的字符串不同,它是32位或者更 高的操作系统中所使用的类型.

 

1.2 源码——通用部分

(1). av_register_all (),     avcodec_register_all ()

  avcodec_register_all() : 注册 hwaccel,encoder,decoder,parser,bitstream
    av_register_all() : 注册 muxer,demuxer,protocol,在所有基于ffmpeg的应用程序中几乎第一个被调用的。只有调用了该函数,才能使用复用器,编码器等
    avfilter_register_all() : 注册 滤镜filter

技术分享

(2).内存分配:av_malloc(),av_realloc(),av_mallocz(),av_calloc(),av_free(),av_freep()

  内存操作常见函数位于  libavutil\mem.中:

av_malloc()——简单封装了系统的malloc(),并做错误检查工作;

av_realloc()——简单封装了系统的realloc(),用于对申请的内存大小进行调整;

av_mallocz()——av_mallocz()中调用了av_malloc()之后,又调用memset()将分配的内存设置为0

av_calloc()——简单封装了av_mallocz();

av_freep()——释放申请的内存;

av_freep()——简单封装了av_free(),并且在释放内存之后将目标指针设置为null。

 

(3).ffmpeg结构体

技术分享

a) 解协议(http,rtsp,rtmp,mms)

AVIOContext,URLProtocol,URLContext 主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注 意:FFMPEG中文件也被当做一种协议“file”)

b)解封装(flv,avi,rmvb,mp4)

AVFormatContext主要存储视音频封装格式中包含的信息;AVInputFormat存储输入视音频使用的封装格式。每种视音频封装格式都对应一个AVInputFormat 结构。

c) 解码(h264,mpeg2,aac,mp3)

每个AVStream存储一个视频/音频流的相关数据;每个AVStream对应一个AVCodecContext,存储该视频/音频流使用解码方式的相关数据;每个AVCodecContext中对应一个AVCodec,包含该视频/音频对应的解码器。每种解码器都对应一个AVCodec结构。

d) 存数据

视频的话,每个结构一般是存一帧;音频可能有好几帧

解码前数据:AVPacket

解码后数据:AVFrame

 

技术分享

AVFormatContext:统领全局的基本结构体。主要用于处理封装格式(FLV/MKV/RMVB等)

AVIOContext:输入输出对应的结构体,用于输入输出(读写文件,RTMP协议等)。

AVStream,AVCodecContext:视音频流对应的结构体,用于视音频编解码。

AVFrame:存储非压缩的数据(视频对应RGB/YUV像素数据,音频对应PCM采样数据)

AVPacket:存储压缩数据(视频对应H.264等码流数据,音频对应AAC/MP3等码流数据)

 

 

(4). avio_open2()

该函数用于打开FFmpeg的输入输出文件。avio_open2()的声明位于libavformat\avio.h文件中 int avio_open2(AVIOContext **s, const char *url, int flags,  const AVIOInterruptCB *int_cb, AVDictionary **options) 

  s:函数调用成功之后创建的AVIOContext结构体。
  url:输入输出协议的地址(文件也是一种“广义”的协议,对于文件来说就是文件的路径)。
  flags:打开地址的方式。AVIO_FLAG_READ:只读;AVIO_FLAG_WRITE:只写;AVIO_FLAG_READ_WRITE:读写。                                                

(5). avcodec_find_encoder()和avcodec_find_decoder() 

查找编码器 和解码器,实质就是遍历AVCodec链表并且获得符合条件的元素,声明位于libavcodec\avcodec.h:

  AVCodec *avcodec_find_encoder(enum AVCodecID id);

  AVCodec *avcodec_find_decoder(enum AVCodecID id);

 

技术分享

技术分享

 

 

 

技术分享

(6). avcodec_open2()

初始化一个视音频编解码器的AVCodecContext。avcodec_open2()的声明位于libavcodec\avcodec.h

int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);

  avctx:需要初始化的AVCodecContext;
  codec:输入的AVCodec;
  options:一些选项。例如使用libx264编码的时候,“preset”,“tune”等都可以通过该参数设置

avcodec_open2() 函数的主要工作:

  1)为各种结构体分配内存(通过各种av_malloc()实现)
  2)将输入的AVDictionary形式的选项设置到AVCodecContext
  3)其他一些零零碎碎的检查,比如说检查编解码器是否处于“实验”阶段
  4)如果是编码器,检查输入参数是否符合编码器的要求
  5)调用AVCodec的init()初始化具体的解码器。

 

(7). avcodec_close()

该函数用于关闭编码器。avcodec_close()函数的声明位于libavcodec\avcodec.h

int avcodec_close(AVCodecContext *avctx);

 

1.3 源码——解码部分

(1). avformat_open_input()

技术分享

打开媒体的过程开始于avformat_open_input(),完成:

 

 

1).输入输出结构体AVIOContext的初始化;

 

 

2).输入数据的协议(例如RTMP,或者file)的识别(通过一套评分机制):

  判断文件名的后缀  + 读取文件头的数据进行比对

使用获得最高分的文件协议对应的URLProtocol,通过函数指针的方式,与

FFMPEG连接(非专业用词);

3).剩下的就是调用该URLProtocol的函数进行open,read等操作了

 

(2). avformat_close_input()

用于打开一个AVFormatContext,一般情况下是和avformat_open_input()成对使用的;

avformat_close_input()的声明位于libavformat\avformat.h:        void avformat_close_input(AVFormatContext **s);

函数功能:

  1)调用AVInputFormat的read_close()方法关闭输入流
  2)调用avformat_free_context()释放AVFormatContext
  3)调用avio_close()关闭并且释放AVIOContext

 

(3). avformat_find_stream_info()

 

该函数可以读取一部分视音频数据并且获得一些相关的信息。avformat_find_stream_info()的声明位于libavformat\avformat.h:

int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);     函数正常执行后返回值大于等于0;

  c:输入的AVFormatContext
  options:额外的选项

功能:

该函数主要用于给每个媒体流(音频/视频)的AVStream结构体赋值。它其实已经实现了解码器的查找,解码器的打开,视音频帧的读取,视音频帧的解码等工作。换句话说,该函数实际上已经“走通”的解码的整个流程。下面看一下除了成员变量赋值之外,该函数的几个关键流程:

  1).查找解码器:find_decoder()
  2).打开解码器:avcodec_open2()
  3).读取完整的一帧压缩编码的数据:read_frame_internal() 注:av_read_frame()内部实际上就是调用的read_frame_internal()。
  4).解码一些压缩编码数据:try_decode_frame()

 

(4). av_read_frame()

读取码流中的音频若干帧或者视频一帧。例如,解码视频的时候,每解码一个视频帧,需要先调用 av_read_frame()获得一帧视频的压缩数据,然后才能对该数据进行解码(例如H.264中一帧压缩数据通常对应一个NAL)。

 先参考了其他人对av_read_frame()的解释,在此做一个参考:通过av_read_packet(***),读取一个包,需要说明的是此函数必须是包含整数帧的,不存在半帧的情况,以ts流为例,是读取一个完整的 PES包(一个完整pes包包含若干视频或音频es包),读取完毕后,通过av_parser_parse2(***)分析出视频一帧(或音频若干帧), 返回,下次进入循环的时候,如果上次的数据没有完全取完,则st = s->cur_st;不会是NULL,即再此进入av_parser_parse2(***)流程,而不是下面的 av_read_packet(**)流程,这样就保证了,如果读取一次包含了N帧视频数据(以视频为例),则调用 av_read_frame(***)N次都不会去读数据,而是返回第一次读取的数据,直到全部解析完毕。

 注意av_read_frame - 新版本的ffmpeg用的是av_read_frame,而老版本的是av_read_packet ,区别是av_read_packet读出的是包,它可能是半帧或多帧,不保证帧的完整性。av_read_frame对 av_read_packet进行了封装,使读出的数据总是完整的帧   

av_read_frame()的声明位于libavformat\avformat.h:   int av_read_frame(AVFormatContext *s, AVPacket *pkt);

  s:输入的AVFormatContext

  pkt:输出的AVPacket/* 

 

 

 

 

 

 

 

 

最简单的基于FFMPEG的转码程序分析 +ffmpga代码简析(转 +总结)

标签:

原文地址:http://www.cnblogs.com/zxqstrong/p/4563202.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!