1. 程式人生 > >x264 ffmpeg編解碼引數筆記

x264 ffmpeg編解碼引數筆記

X264 ffmpeg

1、位元速率:
碼流(Data Rate),是指視訊檔案在單位時間內使用的資料流量

三種可選的位元速率控制方法(bitrate, CQP,CRF), 選擇的順序是 bitrate > QP > CRF
QP是固定量化引數,bitrate是固定檔案大小,crf則是固定“質量”,abr(ABR平均位元速率,VBR是動態位元速率。CBR是靜態位元速率。),crf(限制位元速率),cqp(固定質量)
–qp, –bitrate, –crf
X264_RC_CQP 動態位元率
X264_RC_CRF 恆定位元率
X264_RC_ABR 平均位元率

480P解析度僅需1-2M的位元速率即可達到遠超AVI格式的畫質,一部電影大小僅為1G;720P解析度也只需要3-4M位元速率和2-3G的體積
720P ,碼流大約在2到4或者5M;
1080P 大約在4到8M

高位元速率編碼(如下 中位元速率 = 高位元速率/2, 極高位元速率 = 高位元速率*2,低位元速率 = 中位元速率/2
480P –> 250kbps 720P –> 500kbps 1080P –> 1mps 極低位元速率
480P –> 500kbps 720P –> 1mbps 1080P –> 2mps 低位元速率
480P –> 1mbps 720P –> 2mbps 1080P –> 4mps 中位元速率
480P –> 2mbps 720P –> 4mbps 1080P –> 8mps 高位元速率
480P –> 4mbps 720P –> 8mbps 1080P –> 16mps 極高位元速率
通常高編碼位元速率:
480P 720X480 1800Kbps
720P 1280X720 3500Kbps
1080P 1920X1080 8500Kbps

ffmpeg中編碼器引數設定:

1.如果設定了AVCodecContext中bit_rate的大小,則採用abr的控制方式;
2.如果沒有設定AVCodecContext中的bit_rate,則預設按照crf方式編碼,crf預設大小為23(此值類似於qp值,同樣表示視訊質量);
3.如果使用者想自己設定,則需要藉助av_opt_set函式設定AVCodecContext的priv_data引數。下面給出三種控制方式的實現程式碼:
int bpsValue; //碼流控制方式的對應值
int bpsMode; //碼流控制方式,0表示平均位元速率(abr),1表示固定位元速率(crf),2表示固定質量(cqp)
AVCodecContext* pCodecCtx;
…….

//位元速率控制方式
string modeValue = int2String(bpsValue);
switch (bpsMode) {
case 0:
pCodecCtx->bit_rate = bpsValue*1000;
break;
case 1:
av_opt_set(pCodecCtx->priv_data,”crf”,modeValue.c_str(),AV_OPT_SEARCH_CHILDREN);
break;
case 2:
av_opt_set(pCodecCtx->priv_data,”qp”,modeValue.c_str(),AV_OPT_SEARCH_CHILDREN);
break;
default:
pCodecCtx->bit_rate = bpsValue;
break;
}

ffmpeg中VBR(可變率控制)的設定:
c->flags |= CODEC_FLAG_QSCALE;
c->rc_min_rate =min;
c->rc_max_rate = max;
c->bit_rate = br;

ffmpeg中CBR(固定位元速率控制)的設定:
c->bit_rate = br;
c->rc_min_rate =br;
c->rc_max_rate = br;
c->bit_rate_tolerance = br;
c->rc_buffer_size=br;
c->rc_initial_buffer_occupancy = c->rc_buffer_size*3/4;
c->rc_buffer_aggressivity= (float)1.0;
c->rc_initial_cplx= 0.5;

  //AVCodecContext 相當於虛基類,需要用具體的編碼器實現來給他賦值
    pCodecCtxEnc = video_st->codec;

    //編碼器的ID號,這裡我們自行指定為264編碼器,實際上也可以根據video_st裡的codecID 引數賦值
    pCodecCtxEnc->codec_id = AV_CODEC_ID_H264;

    //編碼器編碼的資料型別
    pCodecCtxEnc->codec_type = AVMEDIA_TYPE_VIDEO;

    //目標的位元速率,即取樣的位元速率;顯然,取樣位元速率越大,視訊大小越大
    pCodecCtxEnc->bit_rate = 200000;

    //固定允許的位元速率誤差,數值越大,視訊越小
    pCodecCtxEnc->bit_rate_tolerance = 4000000;

    //編碼目標的視訊幀大小,以畫素為單位
    pCodecCtxEnc->width = 640;
    pCodecCtxEnc->height = 480;

    //幀率的基本單位,我們用分數來表示,
    //用分數來表示的原因是,有很多視訊的幀率是帶小數的eg:NTSC 使用的幀率是29.97
    pCodecCtxEnc->time_base.den = 30;
    pCodecCtxEnc->time_base = (AVRational){1,25};
    pCodecCtxEnc->time_base.num = 1;

    //畫素的格式,也就是說採用什麼樣的色彩空間來表明一個畫素點
    pCodecCtxEnc->pix_fmt = PIX_FMT_YUV420P;

    //每250幀插入1個I幀,I幀越少,視訊越小

    [cpp] view plain copy
    pCodecCtxEnc->gop_size = 250;

    //兩個非B幀之間允許出現多少個B幀數
    //設定0表示不使用B幀
    //b 幀越多,圖片越小
    pCodecCtxEnc->max_b_frames = 0;

    //運動估計
    pCodecCtxEnc->pre_me = 2;

    //設定最小和最大拉格朗日乘數
    //拉格朗日乘數 是統計學用來檢測瞬間平均值的一種方法
    pCodecCtxEnc->lmin = 1;
    pCodecCtxEnc->lmax = 5;

    //最大和最小量化係數
    pCodecCtxEnc->qmin = 10;
    pCodecCtxEnc->qmax = 50;

    //因為我們的量化係數q是在qmin和qmax之間浮動的,
    //qblur表示這種浮動變化的變化程度,取值範圍0.0~1.0,取0表示不削減
    pCodecCtxEnc->qblur = 0.0;

    //空間複雜度的masking力度,取值範圍 0.0-1.0
    pCodecCtxEnc->spatial_cplx_masking = 0.3;

    //運動場景預判功能的力度,數值越大編碼時間越長
    pCodecCtxEnc->me_pre_cmp = 2;

    //採用(qmin/qmax的比值來控制碼率,1表示區域性採用此方法,)
    pCodecCtxEnc->rc_qsquish = 1;

    //設定 i幀、p幀與B幀之間的量化係數q比例因子,這個值越大,B幀越不清楚
    //B幀量化係數 = 前一個P幀的量化係數q * b_quant_factor + b_quant_offset
    pCodecCtxEnc->b_quant_factor = 1.25;

    //i幀、p幀與B幀的量化係數便宜量,便宜越大,B幀越不清楚
    pCodecCtxEnc->b_quant_offset = 1.25;

    //p和i的量化係數比例因子,越接近1,P幀越清楚
    //p的量化係數 = I幀的量化係數 * i_quant_factor + i_quant_offset
    pCodecCtxEnc->i_quant_factor = 0.8;
    pCodecCtxEnc->i_quant_offset = 0.0;

    //位元速率控制測率,巨集定義,查API
    pCodecCtxEnc->rc_strategy = 2;

    //b幀的生成策略
    pCodecCtxEnc->b_frame_strategy = 0;

    //消除亮度和色度門限
    pCodecCtxEnc->luma_elim_threshold = 0;
    pCodecCtxEnc->chroma_elim_threshold = 0;

    //DCT變換演算法的設定,有7種設定,這個演算法的設定是根據不同的CPU指令集來優化的取值範圍在0-7之間
    pCodecCtxEnc->dct_algo = 0;

    //這兩個引數表示對過亮或過暗的場景作masking的力度,0表示不作
    pCodecCtxEnc->lumi_masking = 0.0;
    pCodecCtxEnc->dark_masking = 0.0;

x264編碼時延問題:
a:
avcodec_encode_video2函式輸出的延時僅僅跟max_b_frames的設定有關,進行實時編碼,將max_b_frames設定為0便沒有編碼延時了

b:使用264的API設定編碼速度
// ultrafast,superfast, veryfast, faster, fast, medium slow, slower, veryslow, placebo. 這是x264編碼速度的選項
av_opt_set(m_context->priv_data,”preset”,”ultrafast”,0);

2、ffmpeg中的時間單位

AV_TIME_BASE
ffmpeg中的內部計時單位(時間基),ffmepg中的所有時間都是於它為一個單位,
比如AVStream中的duration即以為著這個流的長度為duration個AV_TIME_BASE。AV_TIME_BASE定義為:

#define         AV_TIME_BASE   1000000

AV_TIME_BASE_Q
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物件指標。

時間基轉換公式
timestamp(ffmpeg內部時間戳) = AV_TIME_BASE * time(秒)
time(秒) = AV_TIME_BASE_Q * timestamp(ffmpeg內部時間戳)
所以當需要把視訊跳轉到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,來把時間戳從一個時基調整到另外一個時基。在進行時基轉換的時候,
我們應該首選這個函式,因為它可以避免溢位的情況發生。

  1. 視訊時間戳
    pkt.pts= inc++ *(1000/fps); 其中inc是一個靜態的,初始值為0,每次打完時間戳inc加1.
    ffmpeg程式碼:
    pkt.pts= m_nVideoTimeStamp++ * (pctx->time_base.num * 1000 / pctx->time_base.den);

  2. 音訊時間戳
    pts = inc++ * (frame_size * 1000 / sample_rate)
    ffmpeg程式碼:
    pkt.pts= m_nAudioTimeStamp++ * (m_ACtx->frame_size * 1000 / m_ACtx->sample_rate);

取樣頻率是指將模擬聲音波形進行數字化時,每秒鐘抽取聲波幅度樣本的次數。

呼叫av_rescale_q(pts, timebase1, timebase2)或者av_rescale_q_rnd(pts, timebase1, timebase2, XX)時,其實就是按照下面的公式計算:
x = pts * (timebase1.num / timebase1.den )* (timebase2.den / timebase2.num); //這個x就是轉換後的時間戳。