x264ffmpeg編譯
1. 關於ffmpeg的問題
通常相應庫的配置階段用命令
./configure --program-transform-name=新庫名
然後
make
但還要自己去處理下游包依賴的兼容性了,下游也許要改腳本。因為下游的配置腳本是按標准名稱寫的。
2. 在ubuntu12.04LTS下成功編譯安裝帶有h264和aac編碼的ffmpeg的完整過程(命令行)
先下載最少yasm、ffmpeg、x264的源碼,
具體步驟和這個差不多。
https://trac.ffmpeg.org/wiki/UbuntuCompilationGuide
h.264通常只用x264一種。
aac編碼器有fdk,visualon,faac,aacplus,內置的等5種選擇。
用fdk最好,其餘任一也夠。
先編譯組件,通常是
./configure 具體包的設置選項
make install
編譯好各個組件後,最後再設置和編譯ffmpeg。
c編譯器的選項CFLAGS最好一致。可以針對自己的cpu優化。比如加入
「-Ofast 」只為速度
-mtune core2 使用core2的優化。
3. FFmpeg視頻編碼 YUV420P編碼H264
//第一步:注冊組件->編碼器、解碼器等等…
av_register_all();
//第二步:初始化封裝格式上下文->視頻編碼->處理為視頻壓縮數據格式
AVFormatContext *avformat_context = avformat_alloc_context();
//注意事項:FFmepg程序推測輸出文件類型->視頻壓縮數據格式類型
const char *coutFilePath = [outFilePath UTF8String];
//得到視頻壓縮數據格式類型(h264、h265、mpeg2等等...)
AVOutputFormat *avoutput_format = av_guess_format(NULL, coutFilePath, NULL);
//指定類型
avformat_context->oformat = avoutput_format;
//第三步:打開輸出文件
//參數一:輸出流
//參數二:輸出文件
//參數三:許可權->輸出到文件中
if (avio_open(&avformat_context->pb, coutFilePath, AVIO_FLAG_WRITE) < 0) {
NSLog(@"打開輸出文件失敗");
return;
}
//第四步:創建輸出碼流->創建了一塊內存空間->並不知道他是什麼類型流->希望他是視頻流
AVStream *av_video_stream = avformat_new_stream(avformat_context, NULL);
//第五步:查找視頻編碼器
//1、獲取編碼器上下文
AVCodecContext *avcodec_context = av_video_stream->codec;
//2、設置編解碼器上下文參數->必需設置->不可少
//目標:設置為是一個視頻編碼器上下文->指定的是視頻編碼器
//上下文種類:視頻解碼器、視頻編碼器、音頻解碼器、音頻編碼器
//2.1 設置視頻編碼器ID
avcodec_context->codec_id = avoutput_format->video_codec;
//2.2 設置編碼器類型->視頻編碼器
//視頻編碼器->AVMEDIA_TYPE_VIDEO
//音頻編碼器->AVMEDIA_TYPE_AUDIO
avcodec_context->codec_type = AVMEDIA_TYPE_VIDEO;
//2.3 設置讀取像素數據格式->編碼的是像素數據格式->視頻像素數據格式->YUV420P(YUV422P、YUV444P等等...)
//注意:這個類型是根據你解碼的時候指定的解碼的視頻像素數據格式類型
avcodec_context->pix_fmt = AV_PIX_FMT_YUV420P;
//2.4 設置視頻寬高->視頻尺寸
avcodec_context->width = 640;
avcodec_context->height = 352;
//2.5 設置幀率->表示每秒25幀
//視頻信息->幀率 : 25.000 fps
//f表示:幀數
//ps表示:時間(單位:每秒)
avcodec_context->time_base.num = 1;
avcodec_context->time_base.den = 25;
//2.6 設置碼率
//2.6.1 什麼是碼率?
//含義:每秒傳送的比特(bit)數單位為 bps(Bit Per Second),比特率越高,傳送數據速度越快。
//單位:bps,"b"表示數據量,"ps"表示每秒
//目的:視頻處理->視頻碼率
//2.6.2 什麼是視頻碼率?
//含義:視頻碼率就是數據傳輸時單位時間傳送的數據位數,一般我們用的單位是kbps即千位每秒
//視頻碼率計算如下?
//基本的演算法是:【碼率】(kbps)=【視頻大小 - 音頻大小】(bit位) /【時間】(秒)
//例如:Test.mov時間 = 24,文件大小(視頻+音頻) = 1.73MB
//視頻大小 = 1.34MB(文件佔比:77%) = 1.34MB * 1024 * 1024 * 8 = 位元組大小 = 468365位元組 = 468Kbps
//音頻大小 = 376KB(文件佔比:21%)
//計算出來值->碼率 : 468Kbps->表示1000,b表示位(bit->位)
//總結:碼率越大,視頻越大
avcodec_context->bit_rate = 468000;
//2.7 設置GOP->影響到視頻質量問題->畫面組->一組連續畫面
//MPEG格式畫面類型:3種類型->分為->I幀、P幀、B幀
//I幀->內部編碼幀->原始幀(原始視頻數據)
// 完整畫面->關鍵幀(必需的有,如果沒有I,那麼你無法進行編碼,解碼)
// 視頻第1幀->視頻序列中的第一個幀始終都是I幀,因為它是關鍵幀
//P幀->向前預測幀->預測前面的一幀類型,處理數據(前面->I幀、B幀)
// P幀數據->根據前面的一幀數據->進行處理->得到了P幀
//B幀->前後預測幀(雙向預測幀)->前面一幀和後面一幀
// B幀壓縮率高,但是對解碼性能要求較高。
//總結:I只需要考慮自己 = 1幀,P幀考慮自己+前面一幀 = 2幀,B幀考慮自己+前後幀 = 3幀
// 說白了->P幀和B幀是對I幀壓縮
//每250幀,插入1個I幀,I幀越少,視頻越小->默認值->視頻不一樣
avcodec_context->gop_size = 250;
//2.8 設置量化參數->數學演算法(高級演算法)->不講解了
//總結:量化系數越小,視頻越是清晰
//一般情況下都是默認值,最小量化系數默認值是10,最大量化系數默認值是51
avcodec_context->qmin = 10;
avcodec_context->qmax = 51;
//2.9 設置b幀最大值->設置不需要B幀
avcodec_context->max_b_frames = 0;
//第二點:查找編碼器->h264
//找不到編碼器->h264
//重要原因是因為:編譯庫沒有依賴x264庫(默認情況下FFmpeg沒有編譯進行h264庫)
//第一步:編譯h264庫
AVCodec *avcodec = avcodec_find_encoder(avcodec_context->codec_id);
if (avcodec == NULL) {
NSLog(@"找不到編碼器");
return;
}
NSLog(@"編碼器名稱為:%s", avcodec->name);
//第六步:打開h264編碼器
//缺少優化步驟?
//編碼延時問題
//編碼選項->編碼設置
AVDictionary *param = 0;
if (avcodec_context->codec_id == AV_CODEC_ID_H264) {
//需要查看x264源碼->x264.c文件
//第一個值:預備參數
//key: preset
//value: slow->慢
//value: superfast->超快
av_dict_set(¶m, "preset", "slow", 0);
//第二個值:調優
//key: tune->調優
//value: zerolatency->零延遲
av_dict_set(¶m, "tune", "zerolatency", 0);
}
if (avcodec_open2(avcodec_context, avcodec, ¶m) < 0) {
NSLog(@"打開編碼器失敗");
return;
}
//第七步:寫入文件頭信息
avformat_write_header(avformat_context, NULL);
//第8步:循環編碼yuv文件->視頻像素數據(yuv格式)->編碼->視頻壓縮數據(h264格式)
//8.1 定義一個緩沖區
//作用:緩存一幀視頻像素數據
//8.1.1 獲取緩沖區大小
int buffer_size = av_image_get_buffer_size(avcodec_context->pix_fmt,
avcodec_context->width,
avcodec_context->height,
1);
//8.1.2 創建一個緩沖區
int y_size = avcodec_context->width * avcodec_context->height;
uint8_t *out_buffer = (uint8_t *) av_malloc(buffer_size);
//8.1.3 打開輸入文件
const char *cinFilePath = [inFilePath UTF8String];
FILE *in_file = fopen(cinFilePath, "rb");
if (in_file == NULL) {
NSLog(@"文件不存在");
return;
}
//8.2.1 開辟一塊內存空間->av_frame_alloc
//開辟了一塊內存空間
AVFrame *av_frame = av_frame_alloc();
//8.2.2 設置緩沖區和AVFrame類型保持一直->填充數據
av_image_fill_arrays(av_frame->data,
av_frame->linesize,
out_buffer,
avcodec_context->pix_fmt,
avcodec_context->width,
avcodec_context->height,
1);
int i = 0;
//9.2 接收一幀視頻像素數據->編碼為->視頻壓縮數據格式
AVPacket *av_packet = (AVPacket *) av_malloc(buffer_size);
int result = 0;
int current_frame_index = 1;
while (true) {
//8.1 從yuv文件裡面讀取緩沖區
//讀取大小:y_size * 3 / 2
if (fread(out_buffer, 1, y_size * 3 / 2, in_file) <= 0) {
NSLog(@"讀取完畢...");
break;
}else if (feof(in_file)) {
break;
}
//8.2 將緩沖區數據->轉成AVFrame類型
//給AVFrame填充數據
//8.2.3 void * restrict->->轉成->AVFrame->ffmpeg數據類型
//Y值
av_frame->data[0] = out_buffer;
//U值
av_frame->data[1] = out_buffer + y_size;
//V值
av_frame->data[2] = out_buffer + y_size * 5 / 4;
av_frame->pts = i;
//注意時間戳
i++;
//總結:這樣一來我們的AVFrame就有數據了
//第9步:視頻編碼處理
//9.1 發送一幀視頻像素數據
avcodec_send_frame(avcodec_context, av_frame);
//9.2 接收一幀視頻像素數據->編碼為->視頻壓縮數據格式
result =avcodec_receive_packet(avcodec_context, av_packet);
//9.3 判定是否編碼成功
if (result == 0) {
//編碼成功
//第10步:將視頻壓縮數據->寫入到輸出文件中->outFilePath
av_packet->stream_index = av_video_stream->index;
result =av_write_frame(avformat_context, av_packet);
NSLog(@"當前是第%d幀", current_frame_index);
current_frame_index++;
//是否輸出成功
if (result < 0) {
NSLog(@"輸出一幀數據失敗");
return;
}
}
}
//第11步:寫入剩餘幀數據->可能沒有
flush_encoder(avformat_context, 0);
//第12步:寫入文件尾部信息
av_write_trailer(avformat_context);
//第13步:釋放內存
avcodec_close(avcodec_context);
av_free(av_frame);
av_free(out_buffer);
av_packet_free(&av_packet);
avio_close(avformat_context->pb);
avformat_free_context(avformat_context);
fclose(in_file);
4. 如何讓FFmpeg在iOS上完美編譯
找到問題了編譯的時候首先要連接libx264 ,le-libx264 --enable-gpl --extra-cflags=-I/usr/local/include --extra-ldflags=-L/usr/local/lib 然後要打開這個編碼器--enable-encoder=libx264,我就是這一步搞錯了一直找不到,我在編譯的時候禁掉了所有的編碼器,終於可以繼續走下去了 查看原帖>>
5. FFmpeg-視頻編碼-YUV編碼出H264
編碼出來的h264數據可以直接使⽤ffplay播放
int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align);
函數的作⽤是通過指定像素格式、圖像寬、圖像⾼來計算所需的內存⼤⼩
重點說明⼀個參數 align :此參數是設定內存對⻬的對⻬數,也就是按多⼤的位元組進⾏內存對⻬:
av_image_alloc()是這樣定義的。此函數的功能是按照指定的寬、⾼、像素格式來 分配圖像內存 。
int av_image_alloc(uint8_t *pointers[4], int linesizes[4], int w, int h, enum AVPixelFormat pix_fmt, int align);
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4], const uint8_t *src, enum AVPixelFormat pix_fmt, int width, int height, int align);
av_image_fill_arrays()函數 ⾃身不具備內存申請的功能 ,此函數類似於格式化已經申請的內存,即通過 av_malloc()函數申請的內存空間,或者av_frame_get_buffer()函數申請的內存空間。
av_image_fill_arrays()中 參數具體說明 :
視頻碼率是視頻數據(包含視頻⾊彩量、亮度量、像素量)每秒輸出的位數。⼀般⽤的單位是kbps。
在視頻會議應用中,視頻質量和網路帶寬佔用是矛盾的,通常情況下視頻流佔用的帶寬越高則視頻質量也越高;如要求高質量的視頻效果,那麼需要的網路帶寬也越大;解決這一矛盾的鑰匙當然是視頻編解碼技術。評判一種視頻編解碼技術的優劣,是比較在相同的帶寬條件下,哪個視頻質量更好;在相同的視頻質量條件下,哪個佔用的網路帶寬更少。
是不是視頻碼率越高,質量越好呢?理論上是這樣的,然而在我們肉眼分辨的范圍內,當碼率高到一定程度,感覺沒有什麼差別。所以碼率設置有它的最優值,H.264(也叫AVC或X.264)的文檔中,視頻的建議碼率如下:
鑒於x264的參數眾多,各種參數的配合復雜,為了使⽤者⽅便,x264建議如⽆特別需要可使 ⽤preset和tune設置。這套開發者推薦的參數較為合理,可在此基礎上在調整⼀些具體參數以符合⾃⼰需要,⼿動設定的參數會覆蓋preset和tune⾥的參數。
使⽤ ffmpeg -h encoder=libx264 命令查詢相關⽀持的參數
x264是⼀個 H.264/MPEG4 AVC 編碼器,本指南將指導新⼿如何創建⾼質量的H.264視頻。 對於普通⽤戶通常有兩種碼率控制模式:CRF(Constant Rate Factor)和Two pass ABR。碼率控制是⼀種決定為每⼀個視頻幀分配多少⽐特數的⽅法,它將決定⽂件的⼤⼩和質量的分配。
如果你在編譯和安裝libx264 ⽅⾯需要幫助,請查看ffmpeg和x264編譯指南: http://ffmpeg.org/trac/ffmpeg/wiki/CompilationGuide
量化⽐例的范圍為0~51,其中0為⽆損模式, 23 為預設值,51可能是最差的。該數字越⼩,圖像質量越 好。從主觀上講,18~28是⼀個合理的范圍。18往往被認為從視覺上看是⽆損的,它的輸出視頻質量和輸 ⼊視頻⼀模⼀樣或者說相差⽆⼏。但從技術的⻆度來講,它依然是有損壓縮。
若CRF值加6,輸出碼率⼤概減少⼀半;若CRF值減6,輸出碼率翻倍。通常是在保證可接受視頻質量的前提下選擇⼀個最⼤的CRF值,如果輸出視頻質量很好,那就嘗試⼀個更⼤的值,如果看起來很糟,那就嘗 試⼀個⼩⼀點值。
預設是⼀系列參數的集合,這個集合能夠在編碼速度和壓縮率之間做出⼀個權衡。⼀個編碼速度稍慢的預 設會提供更⾼的壓縮效率(壓縮效率是以⽂件⼤⼩來衡量的)。這就是說,假如你想得到⼀個指定⼤⼩的⽂ 件或者采⽤恆定⽐特率編碼模式,你可以采⽤⼀個較慢的預設來獲得更好的質量。同樣的,對於恆定質量編碼模式,你可以通過選擇⼀個較慢的預設輕松地節省⽐特率。
如果你很有耐⼼,通常的建議是使⽤最慢的預設。⽬前所有的預設按照編碼速度降序排列為:
tune是x264中重要性僅次於preset的選項,它是視覺優化的參數,tune可以理解為視頻偏好(或者視頻類型),tune不是⼀個單⼀的參數,⽽是由⼀組參數構成 -tune 來改變參數設置。當前的 tune包括:
如果你不確定使⽤哪個選項或者說你的輸⼊與所有的tune皆不匹配,你可以忽略--tune 選項。 你可以使⽤-tune來查看tune列表,也可以通過x264 --fullhelp來查看tune所采⽤的參數配置。
另外⼀個可選的參數是-profile:v,它可以將你的輸出限制到⼀個特定的 H.264 profile。⼀些⾮常⽼的或者 要被淘汰的設備僅⽀持有限的選項,⽐如只⽀持baseline或者main。
所有的profile 包括:
查找指定的編碼器
初始化
設置編碼器參數
將codec_ctx和codec進行綁定
分配pkt和frame
計算出每一幀的數據 像素格式 * 寬 * 高
讀取YUV數據
格式化已經申請的內存,將YUV數據,格式化放入frame中
發送YUV數據進編碼器
從編碼器中獲取encode的packet數據
encode的packet數據寫入文件
6. linux下編譯ffmpeg時關於configure的問題.
你准備工作沒做好吧!!
先編譯安裝Yasm。
然後編譯安裝H.264 (也就是x264)。
再編譯安裝AAC audio encoder (fdk-aac)。
編譯安裝libmp3lame (MP3 audio encoder)。
編譯安裝libopus (Opus audio decoder and encoder)。
編譯安裝libvpx (VP8/VP9 video encoder and decoder)。
做後編譯安裝ffmpeg。
其中1-6你可以選擇編譯安裝,也可以使用源直接安裝。安裝後了再編譯安裝ffmpeg
你使用 sudo ldconfig -p |grep libx264 看看你的libx264是否正確安裝.
你編譯安裝x264的時候可以使用2中方式都安裝。
先
cd ../x264
./configure --enable-shared
make
make install
最後
cd ../x264
make distclean
./configure --enable-static
make
make install
7. 如何使用FFMPEG+H264實現RTP傳輸數據
開發環境:
WINDOWS7 32bit
MINGW
eclipse juno cdt
1、首先你要編譯好FFMPEG,
a) 方法一:可以去官網下載源碼,用MINGW編譯(編譯時記得支持H264,當然,事先得下載並編譯好libx264,視頻技術論壇里有很多介紹)
b) 方法二:更加省心省力的方法是,下載別人已經編譯好的資源,如ZeranoeFFmpeg的,下載他的dev版本,包含了頭文件,鏈接庫等必須的東西,當然,這東西已經是支持H264的了。
2、以下的就是代碼部分了:
a) 先聲明必要的變數:
AVFormatContext *fmtctx;
AVStream *video_st;
AVCodec *video_codec;
const int FPS = 25; /* 25 images/s */
const char *RDIP = 「127.0.0.1」;
unsigned int RDPORT = 5678;
const unsigned int OUTWIDTH = 720;
const unsigned int OUTHEIGHT = 480;
av_register_all();
avformat_network_init();
b) 初始化AV容器
fmtctx = avformat_alloc_context();
c) 獲得輸出格式,這里是RTP網路流
fmtctx->oformat = av_guess_format("rtp", NULL, NULL);
d)打開網路流
snprintf(fmtctx->filename, sizeof(fmtctx->filename),"rtp://%s:%d",RDIP,RDPORT);
avio_open(&fmtctx->pb,fmtctx->filename, AVIO_FLAG_WRITE)
e) 開始添加H264視頻流
video_st = NULL;video_st = add_video_stream(fmtctx, &video_codec, AV_CODEC_ID_H264);
其中,add_video_stream函數為:
add_video_stream(AVFormatContext *oc,AVCodec **codec, enum AVCodecID codec_id)
{
AVCodecContext *c;
AVStream *st;
/* find the video encoder */
*codec = avcodec_find_encoder(codec_id);
st = avformat_new_stream(oc, *codec);
c = st->codec;
avcodec_get_context_defaults3(c, *codec);
c->codec_id = codec_id;
c->width = OUTWIDTH;
c->height = OUTHEIGHT;
c->time_base.den = FPS;
c->time_base.num = 1;
c->pix_fmt = PIX_FMT_YUV420P;
if(oc->oformat->flags & AVFMT_GLOBALHEADER)
c->flags|= CODEC_FLAG_GLOBAL_HEADER;
av_opt_set(c->priv_data, "preset", "ultrafast", 0);
av_opt_set(c->priv_data, "tune","stillimage,fastdecode,zerolatency",0);
av_opt_set(c->priv_data, "x264opts","crf=26:vbv-maxrate=728:vbv-bufsize=364:keyint=25",0);return st;}
// OPEN THE CODE
avcodec_open2(video_st->codec, video_codec, NULL);
/* Write the stream header, if any. */
avformat_write_header(fmtctx, NULL);
f) 現在,就可以不斷的編碼數據,並發生數據了
AVFrame* m_pYUVFrame = avcodec_alloc_frame();
while(1) //這里設置成無限循環,你可以設置成250,或其他數進行測試,觀看結果
{
fill_yuv_image(m_pYUVFrame, video_st->codec->frame_number,OUTWIDTH, OUTHEIGHT);
/* encode the image */
AVPacket pkt;
int got_output = 0;
av_init_packet(&pkt);
pkt.data = NULL; // packet data will be allocated by the encoder
pkt.size = 0;
pkt.pts = AV_NOPTS_VALUE;
pkt.dts =AV_NOPTS_VALUE;
m_pYUVFrame->pts = video_st->codec->frame_number;
ret = avcodec_encode_video2(c, &pkt,frame, &got_output);
if (ret < 0) {fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret));
exit(1);
}
/* If size is zero, it means the image was buffered. */
if (got_output)
{
if (c->coded_frame->key_frame)pkt.flags |= AV_PKT_FLAG_KEY;
pkt.stream_index = st->index;
if (pkt.pts != AV_NOPTS_VALUE )
{
pkt.pts = av_rescale_q(pkt.pts,video_st->codec->time_base, video_st->time_base);
}
if(pkt.dts !=AV_NOPTS_VALUE )
{
pkt.dts = av_rescale_q(pkt.dts,video_st->codec->time_base, video_st->time_base);
}
/* Write the compressed frame to the media file. */
ret = av_interleaved_write_frame(oc,&pkt);
}
else {
ret = 0;
}
}
g) Fill_yuv_image函數:
/* Prepare a mmy image. */
static void fill_yuv_image(AVPicture *pict,int frame_index,int width, int height)
{
int x, y, i;
i = frame_index;
/* Y */
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
pict->data[0][y * pict->linesize[0] +x] = x + y + i * 3;
/* Cb and Cr */
for (y = 0; y < height / 2; y++)
{
for (x = 0; x < width / 2; x++)
{
pict->data[1][y * pict->linesize[1] +x] = 128 + y + i * 2;
pict->data[2][y * pict->linesize[2] +x] = 64 + x + i * 5;
}
}
}
h) 列印sdp信息,僅需一次,列印的sdp信息,用在VLC播放器結束網路視頻流時用到
//列印sdp信息
char sdp[2048];
av_sdp_create(&fmtctx,1, sdp, sizeof(sdp));
printf("%s\n",sdp);
fflush(stdout);
i)最後,做一些清理工作
avcodec_free_frame(&m_pYUVFrame);
av_write_trailer(fmtctx);
/* Free the streams. */
for (unsigned int i = 0; i< fmtctx->nb_streams;i++)
{
av_freep(&fmtctx->streams->codec);
av_freep(&fmtctx->streams);
}
if(!(fmtctx->oformat->flags& AVFMT_NOFILE))
/* Close the output file. */
avio_close(fmtctx->pb);
/*free the stream */
av_free(fmtctx);
3、編譯代碼,記得添加庫文件,運行一次代碼,不用死循環,設置不用循環,因為是要讓他列印出sdp文件的信息。得到sdp信息,比如我精簡成如下:
c=IN IP4 127.0.0.1
m=video 56782 RTP/AVP 96
a=rtpmap:96 H264/90000
a=framerate:25
a=fmtp:96 packetization-mode=1
把這些信息保存到一個文本文件,並改名為sdp後綴,如mySDP.sdp。
4、從官網下載VLC播放器,重新運行上述的代碼,這一次要循環,具體循環多久,你自己決定,這一次是正式測試了。代碼跑起來後,把剛剛的sdp文件用VLC打開,直接把sdp文件拖到VLC播放器中就行了。等待緩沖,就可以看到效果了。
5、代碼中剩掉了出錯檢查部分,請自行添加。
6、關於IP地址,這里是127.0.0.1,是供本機測試,可以改成制定的接受數據的電腦IP地址,或者廣播地址IP地址。
8. Windows下 ffmpeg+X264 編譯,解決「libx264 not found」問題
【問題】
【解決方案】
通過設置x264 的 PKG_CONFIG_PATH,用以解決x264頭文件的路徑,解決x264依賴庫的位置路徑問題。
