1.基本API
,NULL);
2、开始配置编码器上下文的参数
pCodecCtxEnc->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtxEnc->bit_rate = 200000;
pCodecCtxEnc->bit_rate_tolerance = 4000000;
pCodecCtxEnc->width = 640;
pCodecCtxEnc->height = 480;
pCodecCtxEnc->pix_fmt = PIX_FMT_YUV420P;
pCodecCtxEnc->qblur = 0.0;
pCodecCtxEnc->spatial_cplx_masking = 0.3;
pCodecCtxEnc->me_pre_cmp = 2;
pCodecCtxEnc->rc_qsquish = 1;
pCodecCtxEnc->i_quant_factor = 0.8;
pCodecCtxEnc->i_quant_offset = 0.0;
pCodecCtxEnc->rc_strategy = 2;
pCodecCtxEnc->b_frame_strategy = 0;
(1)x264编码时延问题
方法一:
av_opt_set(m_context->priv_data,"preset","ultrafast",0);
// Set Option
AVDictionary *param = 0;
//H.264
if(pCodecCtx->codec_id == AV_CODEC_ID_H264) {
av_dict_set(¶m, "preset", "slow", 0);
av_dict_set(¶m, "tune", "zerolatency", 0);
//av_dict_set(¶m, "profile", "main", 0);
}
//H.265
if(pCodecCtx->codec_id == AV_CODEC_ID_H265){
av_dict_set(¶m, "preset", "ultrafast", 0);
av_dict_set(¶m, "tune", "zero-latency", 0);
}
参考文章
http://blog.csdn.net/chance_yin/article/details/16335625
AVFormatContext
在使用FFMPEG进行开发的时候,AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体。下面看几个主要变量的作用(在这里考虑解码的情况),AVFormatContext:
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:元数据
视频的原数据(metadata)信息可以通过AVDictionary获取。元数据存储在AVDictionaryEntry结构体中,如下所示
在ffmpeg中通过av_dict_get()函数获得视频的原数据。
获取元数据:
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
A:时间戳一般是在编码的时候加入到媒体文件中的,所以在解码时可以从中分析出PTS。 |
AVRatioal的定义如下:
typedef struct AVRational{ int num; //numerator 分子 int den; //denominator 分母 } AVRational;
ffmpeg提供了一个把AVRatioal结构转换成double的函数:
static inline double av_q2d(AVRational a){ /** * Convert rational to double. * @param a rational to convert **/ return a.num / (double) a.den; }
对于一个电影,帧是这样来显示的:I B B P。现在我们需要在显示B帧之前知道P帧中的信息。因此,帧可能会按照这样的方式来存储:IPBB。这就是为什么我们会有一个解码时间戳和一个显示时间戳的原因。解码时间戳告诉我们什么时候需要解码,显示时间戳告诉我们什么时候需要显示。所以,在这种情况下,我们的流可以是这样的:
PTS: 1 4 2 3 DTS: 1 2 3 4 Stream: I P B B
通常PTS和DTS只有在流中有B帧的时候会不同。
比如:解码后的数据是IBBP,那要将这个数据编码的话,编码后的数据保存的格式就是IPBB
大家都知道一般解码出来的数据都是播放顺序,解码器是将编码顺序的数据重新按照解码后的播放顺序输出的。而编码器是把数据根据解码需要的顺序重新排序保存的。
当然,以上情况只在有帧的情况下才有用,否则只有IP帧的话解码和编码的顺序是一样的
音频和视频流都有一些关于以多快速度和什么时间来播放它们的信息在里面。音频流有采样,视频流有每秒的帧率。然而,如果我们只是简单的通过数帧和乘以帧率的方式来同步视频,那么就很有可能会失去同步。于是作为一种补充,在流中的包有种叫做DTS(解码时间戳)和PTS(显示时间戳)的机制。为了这两个参数,你需要了解电影存放的方式。像MPEG等格式,使用被叫做B帧(B表示双向bidrectional)的方式。另外两种帧被叫做I帧和P帧(I表示关键帧,P表示预测帧)。I帧包含了某个特定的完整图像。P帧依赖于前面的I帧和P帧并且使用比较或者差分的方式来编码。B帧与P帧有点类似,但是它是依赖于前面和后面的帧的信息的。这也就解释了为什么我们可能在调用avcodec_decode_video以后会得不到一帧图像。
ffmpeg中的内部计时单位(时间基),ffmepg中的所有时间都是于它为一个单位,比如AVStream中的duration即以为着这个流的长度为duration个AV_TIME_BASE。AV_TIME_BASE定义为:
#define AV_TIME_BASE 1000000
ffmpeg内部时间基的分数表示,实际上它是AV_TIME_BASE的倒数。从它的定义能很清楚的看到这点:
#define AV_TIME_BASE_Q (AVRational){1, AV_TIME_BASE}
AVRatioal的定义如下:
typedef struct AVRational{ int num; //numerator int den; //denominator } AVRational;
ffmpeg提供了一个把AVRatioal结构转换成double的函数:
static inline double av_q2d(AVRational a){ /** * Convert rational to double. * @param a rational to convert **/ return a.num / (double) a.den; }
现在可以根据pts来计算一桢在整个视频中的时间位置:
timestamp(秒) = pts * av_q2d(st->time_base)
计算视频长度的方法:
time(秒) = st->duration * av_q2d(st->time_base)
这里的st是一个AVStream对象指针。
所以当需要把视频跳转到N秒的时候可以使用下面的方法:
int64_t timestamp = N * AV_TIME_BASE;
2
av_seek_frame(fmtctx, index_of_video, timestamp, AVSEEK_FLAG_BACKWARD);
ffmpeg同样为我们提供了不同时间基之间的转换函数:
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
这个函数的作用是计算a * bq / cq,来把时间戳从一个时基调整到另外一个时基。在进行时基转换的时候,我们应该首选这个函数,因为它可以避免溢出的情况发生。
三、各个time_base之间转换
ffmpeg提供av_rescale_q函数用于time_base之间转换,av_rescale_q(a,b,c)作用相当于执行a*b/c,通过设置b,c的值,可以很方便的实现time_base之间转换。
例如:
1、InputStream(AV_TIME_BASE)到AVPacket(AVStream->time_base)