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

模块:

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

 

时间: 2024-10-11 02:00:29

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

最简单的基于FFMPEG的转码程序

本文介绍一个简单的基于FFmpeg的转码器.转码器在视音频编解码处理的程序中,属于一个比较复杂的东西.因为它结合了视频的解码和编码.一个视频播放器,一般只包含解码功能:一个视频编码工具,一般只包含编码功能:而一个视频转码器,则需要先对视频进行解码,然后再对视频进行编码,因而相当于解码器和编码器的结合.下图例举了一个视频的转码流程.输入视频的封装格式是FLV,视频编码标准是H.264,音频编码标准是AAC:输出视频的封装格式是AVI,视频编码标准是MPEG2,音频编码标准是MP3.从流程中可以看出

最简单的基于FFMPEG的转码程序 [转]

本文介绍一个简单的基于FFmpeg的转码器.它可以将一种视频格式(包括封转格式和编码格式)转换为另一种视频格式.转码器在视音频编解码处理的 程序中,属于一个比较复杂的东西.因为它结合了视频的解码和编码.一个视频播放器,一般只包含解码功能:一个视频编码工具,一般只包含编码功能:而一个视 频转码器,则需要先对视频进行解码,然后再对视频进行编码,因而相当于解码器和编码器的结合.下图例举了一个视频的转码流程.输入视频的封装格式是 FLV,视频编码标准是H.264,音频编码标准是AAC:输出视频的封装格式

最简单的基于FFMPEG的转码程序 [转] —— 分析

模块:  libavcodec    - 编码解码器         libavdevice   - 输入输出设备的支持         libavfilter   - 视音频滤镜支持         libavformat   - 视音频等格式的解析         libavutil     - 工具库         libpostproc   - 后期效果处理         libswscale    - 图像颜色.尺寸转换 1. 主函数分析: int_tmain(int argc, 

最简单的基于FFmpeg的内存读写的例子:内存转码器

上篇文章记录了一个基于FFmpeg的内存播放器,可以使用FFmpeg读取并播放内存中的数据.这篇文章记录一个基于FFmpeg的内存转码器.该转码器可以使用FFmpeg读取内存中的数据,转码为H.264之后再将数据输出到内存.关于如何从内存读取数据,以及如何将数据输出到内存,可以参考文章: ffmpeg 从内存中读取数据(或将数据输出到内存) FFmpeg读写内存的关键点有2个:1.       初始化自定义的AVIOContext,指定自定义的回调函数.2.       自己写回调函数.注意函数

最简单的基于FFmpeg的内存读写的例子:内存播放器

打算记录两个最简单的FFmpeg进行内存读写的例子.之前的所有有关FFmpeg的例子都是对文件进行操作的.例如<100行代码实现最简单的基于FFMPEG+SDL的视频播放器>播放的是一个视频的文件.而<最简单的基于FFMPEG的转码程序>也是将一个视频文件转换为另一个视频文件.<最简单的基于FFmpeg的视频编码器(YUV编码为H.264)>也是最后编码得到一个H.264视频文件.实际上,并不是所有视频的编码,解码都是针对文件进行处理的.有的时候需要的解码的视频数据在一

(转)最简单的基于FFmpeg的内存读写的例子:内存播放器

ffmpeg内存播放解码 目录(?)[+] ===================================================== 最简单的基于FFmpeg的内存读写的例子系列文章列表: 最简单的基于FFmpeg的内存读写的例子:内存播放器 最简单的基于FFmpeg的内存读写的例子:内存转码器 ===================================================== 打算记录两个最简单的FFmpeg进行内存读写的例子.之前的所有有关FFmpe

最简单的基于FFmpeg的移动端例子:Android 转码器

本文记录一个安卓平台下基于FFmpeg的视频转码器.该转码器实际上移植自ffmpeg工程中的ffmpeg.c源代码.有关ffmpeg.c的源代码可以参考文章<ffmpeg.c函数结构简单分析(画图)>,在这里就不重复记录了. 源代码 项目的目录结构如图所示.Java源代码位于src目录,而C代码位于jni目录. Android程序Java端代码位于src\com\leixiaohua1020\sffmpegandroidtranscoder\MainActivity.java,如下所示. /*

最简单的基于FFmpeg的移动端样例:IOS 视频转码器

===================================================== 最简单的基于FFmpeg的移动端样例系列文章列表: 最简单的基于FFmpeg的移动端样例:Android HelloWorld 最简单的基于FFmpeg的移动端样例:Android 视频解码器 最简单的基于FFmpeg的移动端样例:Android 视频解码器-单个库版 最简单的基于FFmpeg的移动端样例:Android 推流器 最简单的基于FFmpeg的移动端样例:Android 视频转

最简单的基于FFmpeg的移动端例子:IOS 视频转码器

本文记录iOS平台下基于FFmpeg的视频转码器.该转码器实际上移植自ffmpeg工程中的ffmpeg.c源代码.有关ffmpeg.c的源代码可以参考文章<ffmpeg.c函数结构简单分析(画图)>,在这里就不重复记录了. 源代码 项目的目录结构如图所示. 下列C语言文件拷贝自FFmpeg源代码: cmdutils.ccmdutils.hcmdutils_common_opts.hconfig.hffmpeg.hffmpeg_filter.cffmpeg_opt.c 此外在编译ffmpeg.c