对于FFMPEG是只闻其名,不见其人,各个大厂比如QQ影音,Bilibili等等都是以它为基础的。(好多都是从雷神那里直接Copy的在此,致敬雷神)
对于如何编译FFMPEG,网上有很多方法,百度即可,我用linux编译的,mac编译遇到了一个问题,应该是环境的问题。所以mac没有编译出来,不过大家按照网上的一步一步来,基本没有问题的,除了个别莫名问题。
编译完成之后有这么几个so,这就是我们需要用的东西了。
1、解协议(http,rtsp,rtmp,mms)
AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”)
2、 解封装(flv,avi,rmvb,mp4)
AVFormatContext主要存储视音频封装格式中包含的信息;AVInputFormat存储输入视音频使用的封装格式。每种视音频封装格式都对应一个AVInputFormat 结构。
3、 解码(h264,mpeg2,aac,mp3)
每个AVStream存储一个视频/音频流的相关数据;每个AVStream对应一个AVCodecContext,存储该视频/音频流使用解码方式的相关数据;每个AVCodecContext中对应一个AVCodec,包含该视频/音频对应的解码器。每种解码器都对应一个AVCodec结构。
4、存数据
视频的话,每个结构一般是存一帧;音频可能有好几帧
解码前数据:AVPacket
AVPacket是存储压缩编码数据相关信息的结构体
解码后数据:AVFrame
一般用于存储原始数据
AVFrame:
一般用于存储原始数据(即非压缩数据,例如对视频来说是YUV,RGB,对音频来说是PCM)
1.data[]
对于packed格式的数据(例如RGB24),会存到data[0]里面。
对于planar格式的数据(例如YUV420P),则会分开成data[0],data[1],data[2]...(YUV420P中data[0]存Y,data[1]存U,data[2]存V)
2.pict_type
包含以下类型:
enum AVPictureType { AV_PICTURE_TYPE_NONE = 0, ///
3.sample_aspect_ratio
宽高比是一个分数,FFMPEG中用AVRational表达分数:
/** * rational number numerator/denominator */
typedef struct AVRational{ int num; ///
AVPacket
AVPacket是存储压缩编码数据相关信息的结构体
uint8_t *data:压缩编码的数据。
例如对于H.264来说。1个AVPacket的data通常对应一个NAL。
因此在使用FFMPEG进行视音频处理的时候,常常可以将得到的AVPacket的data数据直接写成文件,从而得到视音频的码流文件。
int size:data的大小
int64_t pts:显示时间戳
int64_t dts:解码时间戳
int stream_index:标识该AVPacket所属的视频/音频流。
AVStream
是存储每一个视频/音频流信息的结构体
int index:标识该视频/音频流
AVCodecContext *codec:指向该视频/音频流的AVCodecContext(它们是一一对应的关系)
AVRational time_base:时基。通过该值可以把PTS,DTS转化为真正的时间。FFMPEG其他结构体中也有这个字段,但是根据雷神的经验,只有AVStream中的time_base是可用的。PTS*time_base=真正的时间
int64_t duration:该视频/音频流长度
AVDictionary *metadata:元数据信息
AVRational avg_frame_rate:帧率(注:对视频来说,这个挺重要的)
AVPacket attached_pic:附带的图片。比如说一些MP3,AAC音频文件附带的专辑封面。
AVFormatContext
AVFormatContext是包含码流参数较多的结构体,AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体。
struct AVInputFormat *iformat:输入数据的封装格式
AVIOContext *pb:输入数据的缓存
unsigned int nb_streams:视音频流的个数
AVStream **streams:视音频流
char filename[1024]:文件名
int64_t duration:时长(单位:微秒us,转换为秒需要除以1000000)
int bit_rate:比特率(单位bps,转换为kbps需要除以1000)
AVDictionary *metadata:元数据
AVCodecContext
enum AVMediaType codec_type:编解码器的类型(视频,音频...)
struct AVCodec *codec:采用的解码器AVCodec(H.264,MPEG2...)
int bit_rate:平均比特率
uint8_t *extradata; int extradata_size:针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)
AVRational time_base:根据该参数,可以把PTS转化为实际的时间(单位为秒s)
int width, height:如果是视频的话,代表宽和高
int refs:运动估计参考帧的个数(H.264的话会有多帧,MPEG2这类的一般就没有了)
int sample_rate:采样率(音频)
int channels:声道数(音频)
enum AVSampleFormat sample_fmt:采样格式
int profile:型(H.264里面就有,其他编码标准应该也有)
int level:级(和profile差不太多)
在这里需要注意:AVCodecContext中很多的参数是编码的时候使用的,而不是解码的时候使用的。
codec_type:
enum AVMediaType {AVMEDIA_TYPE_UNKNOWN = -1, ///
sample_fmt:
enum AVSampleFormat {AV_SAMPLE_FMT_NONE = -1,AV_SAMPLE_FMT_U8, ///
profile
在FFMPEG中型有以下几种,可以看出AAC,MPEG2,H.264,VC-1,MPEG4都有型的概念
#define FF_PROFILE_UNKNOWN -99
#define FF_PROFILE_RESERVED -100#define FF_PROFILE_AAC_MAIN 0
#define FF_PROFILE_AAC_LOW 1
#define FF_PROFILE_AAC_SSR 2
#define FF_PROFILE_AAC_LTP 3
#define FF_PROFILE_AAC_HE 4
#define FF_PROFILE_AAC_HE_V2 28
#define FF_PROFILE_AAC_LD 22
#define FF_PROFILE_AAC_ELD 38#define FF_PROFILE_DTS 20
#define FF_PROFILE_DTS_ES 30
#define FF_PROFILE_DTS_96_24 40
#define FF_PROFILE_DTS_HD_HRA 50
#define FF_PROFILE_DTS_HD_MA 60#define FF_PROFILE_MPEG2_422 0
#define FF_PROFILE_MPEG2_HIGH 1
#define FF_PROFILE_MPEG2_SS 2
#define FF_PROFILE_MPEG2_SNR_SCALABLE 3
#define FF_PROFILE_MPEG2_MAIN 4
#define FF_PROFILE_MPEG2_SIMPLE 5#define FF_PROFILE_H264_CONSTRAINED (1<<9) // 8&#43;1; constraint_set1_flag
#define FF_PROFILE_H264_INTRA (1<<11) // 8&#43;3; constraint_set3_flag#define FF_PROFILE_H264_BASELINE 66
#define FF_PROFILE_H264_CONSTRAINED_BASELINE (66|FF_PROFILE_H264_CONSTRAINED)
#define FF_PROFILE_H264_MAIN 77
#define FF_PROFILE_H264_EXTENDED 88
#define FF_PROFILE_H264_HIGH 100
#define FF_PROFILE_H264_HIGH_10 110
#define FF_PROFILE_H264_HIGH_10_INTRA (110|FF_PROFILE_H264_INTRA)
#define FF_PROFILE_H264_HIGH_422 122
#define FF_PROFILE_H264_HIGH_422_INTRA (122|FF_PROFILE_H264_INTRA)
#define FF_PROFILE_H264_HIGH_444 144
#define FF_PROFILE_H264_HIGH_444_PREDICTIVE 244
#define FF_PROFILE_H264_HIGH_444_INTRA (244|FF_PROFILE_H264_INTRA)
#define FF_PROFILE_H264_CAVLC_444 44#define FF_PROFILE_VC1_SIMPLE 0
#define FF_PROFILE_VC1_MAIN 1
#define FF_PROFILE_VC1_COMPLEX 2
#define FF_PROFILE_VC1_ADVANCED 3#define FF_PROFILE_MPEG4_SIMPLE 0
#define FF_PROFILE_MPEG4_SIMPLE_SCALABLE 1
#define FF_PROFILE_MPEG4_CORE 2
#define FF_PROFILE_MPEG4_MAIN 3
#define FF_PROFILE_MPEG4_N_BIT 4
#define FF_PROFILE_MPEG4_SCALABLE_TEXTURE 5
#define FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION 6
#define FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE 7
#define FF_PROFILE_MPEG4_HYBRID 8
#define FF_PROFILE_MPEG4_ADVANCED_REAL_TIME 9
#define FF_PROFILE_MPEG4_CORE_SCALABLE 10
#define FF_PROFILE_MPEG4_ADVANCED_CODING 11
#define FF_PROFILE_MPEG4_ADVANCED_CORE 12
#define FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE 13
#define FF_PROFILE_MPEG4_SIMPLE_STUDIO 14
#define FF_PROFILE_MPEG4_ADVANCED_SIMPLE 15
AVCodec
AVCodec是存储编解码器信息的结构体
const char *name&#xff1a;编解码器的名字&#xff0c;比较短
const char *long_name&#xff1a;编解码器的名字&#xff0c;全称&#xff0c;比较长
enum AVMediaType type&#xff1a;指明了类型&#xff0c;是视频&#xff0c;音频&#xff0c;还是字幕
enum AVCodecID id&#xff1a;
ID&#xff0c;为了识别二进制数据流
具有相同ID的两个解码器可以解码相同的流。
具有相同ID的两个编码器可以编码兼容的流。
*supported_framerates&#xff1a;支持的帧率&#xff08;仅视频&#xff09;
const enum AVPixelFormat *pix_fmts&#xff1a;支持的像素格式&#xff08;仅视频&#xff09;
const int *supported_samplerates&#xff1a;支持的采样率&#xff08;仅音频&#xff09;
const enum AVSampleFormat *sample_fmts&#xff1a;支持的采样格式&#xff08;仅音频&#xff09;
const uint64_t *channel_layouts&#xff1a;支持的声道数&#xff08;仅音频&#xff09;
int priv_data_size&#xff1a;私有数据的大小
enum AVMediaType {AVMEDIA_TYPE_UNKNOWN &#61; -1, ///
enum AVCodecID {AV_CODEC_ID_NONE,/* video codecs */AV_CODEC_ID_MPEG1VIDEO,AV_CODEC_ID_MPEG2VIDEO, ///
enum AVPixelFormat {AV_PIX_FMT_NONE &#61; -1,AV_PIX_FMT_YUV420P, ///
enum AVSampleFormat {AV_SAMPLE_FMT_NONE &#61; -1,AV_SAMPLE_FMT_U8, ///
每一个编解码器对应一个该结构体&#xff0c;查看一下ffmpeg的源代码&#xff0c;我们可以看一下H.264解码器的结构体如下所示&#xff08;h264.c&#xff09;&#xff1a;
AVCodec ff_h264_decoder &#61; {.name &#61; "h264",.type &#61; AVMEDIA_TYPE_VIDEO,.id &#61; CODEC_ID_H264,.priv_data_size &#61; sizeof(H264Context),.init &#61; ff_h264_decode_init,.close &#61; ff_h264_decode_end,.decode &#61; decode_frame,.capabilities &#61; /*CODEC_CAP_DRAW_HORIZ_BAND |*/ CODEC_CAP_DR1 | CODEC_CAP_DELAY |CODEC_CAP_SLICE_THREADS | CODEC_CAP_FRAME_THREADS,.flush&#61; flush_dpb,.long_name &#61; NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),.init_thread_copy &#61; ONLY_IF_THREADS_ENABLED(decode_init_thread_copy),.update_thread_context &#61; ONLY_IF_THREADS_ENABLED(decode_update_thread_context),.profiles &#61; NULL_IF_CONFIG_SMALL(profiles),.priv_class &#61; &h264_class,
};
JPEG2000解码器结构体&#xff08;j2kdec.c&#xff09;
AVCodec ff_jpeg2000_decoder &#61; {.name &#61; "j2k",.type &#61; AVMEDIA_TYPE_VIDEO,.id &#61; CODEC_ID_JPEG2000,.priv_data_size &#61; sizeof(J2kDecoderContext),.init &#61; j2kdec_init,.close &#61; decode_end,.decode &#61; decode_frame,.capabilities &#61; CODEC_CAP_EXPERIMENTAL,.long_name &#61; NULL_IF_CONFIG_SMALL("JPEG 2000"),.pix_fmts &#61;(const enum PixelFormat[]) {PIX_FMT_GRAY8, PIX_FMT_RGB24, PIX_FMT_NONE}
};
下面简单介绍一下遍历ffmpeg中的解码器信息的方法&#xff08;这些解码器以一个链表的形式存储&#xff09;&#xff1a;
1.注册所有编解码器&#xff1a;av_register_all();
2.声明一个AVCodec类型的指针&#xff0c;比如说AVCodec* first_c;
3.调用av_codec_next()函数&#xff0c;即可获得指向链表下一个解码器的指针&#xff0c;循环往复可以获得所有解码器的信息。注意&#xff0c;如果想要获得指向第一个解码器的指针&#xff0c;则需要将该函数的参数设置为NULL。
AVIOContext
是FFMPEG管理输入输出数据的结构体
unsigned char *buffer&#xff1a;缓存开始位置
int buffer_size&#xff1a;缓存大小&#xff08;默认32768&#xff09;
unsigned char *buf_ptr&#xff1a;当前指针读取到的位置
unsigned char *buf_end&#xff1a;缓存结束的位置
void *opaque&#xff1a;URLContext结构体
在解码的情况下&#xff0c;buffer用于存储ffmpeg读入的数据。例如打开一个视频文件的时候&#xff0c;先把数据从硬盘读入buffer&#xff0c;然后在送给解码器用于解码。
分界线
从雷神那里把好多资料Copy了过来&#xff0c;为什么要列这么多呢?因为你不了解这些&#xff0c;基本是懵逼的。
我是先懵逼的敲几遍代码&#xff0c;熟悉一下流程&#xff0c;然后再来理解这些东西的含义&#xff0c;真心感觉NDK的东西&#xff0c;需要是啃出来的&#xff0c;真心是懵逼。