ffmpeg編譯x264
1. 如何使用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地址。
2. ios編譯ffmpeg時如何添加x264的庫
找到問題了編譯的時候首先要連接libx264 ,le-libx264 --enable-gpl --extra-cflags=-I/usr/local/include --extra-ldflags=-L/usr/local/lib 然後要打開這個編碼器--enable-encoder=libx264,我就是這一步搞錯了一直找不到,我在編譯的時候禁掉了所有的編碼器,終於可以繼續走下去了 到DEVDIV.COM網站查看回答詳情>>
3. android-ffmpeg-x264 怎麼用
Android內置的編解碼器實在太少,於是我們需要FFmpeg。Android提供了NDK,為我們使用FFmpeg這種C語言代碼提供了方便。
不過為了用NDK編譯FFmpeg,還真的花費了不少時間,也得到了很多人的幫助,最應該謝謝havlenapetr。我覺得我現在這些方法算是比較簡潔的了--
下面就盡量詳細的說一下我是怎麼在項目中使用FFmpeg的,但是基於我混亂的表達能力,有不明白的就問我。
你得了解JNI和Android NDK的基本用法,若覺得我的文章還不錯,可以看之前寫的JNI簡單入門和Android NDK入門
首先創建一個標準的Android項目vPlayer
android create project -n vPlayer -t 8 -p vPlayer -k me.abitno.vplayer -a PlayerView
然後在vPlayer目錄里
mkdir jni && cd jni
wget http://ffmpeg.org/releases/ffmpeg-0.6.tar.bz2
tar xf ffmpeg-0.6.tar.bz2 && mv ffmpeg-0.6 ffmpeg && cd ffmpeg
在ffmpeg下新建一個config.sh,內容如下,注意把PREBUILT和PLATFORM設置正確。另外裡面有些參數你也可以自行調整,我主要是為了配置一個播放器而這樣設置的。
#!/bin/bash
PREBUILT=/home/abitno/Android/android-ndk-r4/build/prebuilt/linux-x86/arm-eabi-4.4.0
PLATFORM=/home/abitno/Android/android-ndk-r4/build/platforms/android-8/arch-arm
./configure --target-os=linux \
--arch=arm \
--enable-version3 \
--enable-gpl \
--enable-nonfree \
--disable-stripping \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffserver \
--disable-ffprobe \
--disable-encoders \
--disable-muxers \
--disable-devices \
--disable-protocols \
--enable-protocol=file \
--enable-avfilter \
--disable-network \
--disable-mpegaudio-hp \
--disable-avdevice \
--enable-cross-compile \
--cc=$PREBUILT/bin/arm-eabi-gcc \
--cross-prefix=$PREBUILT/bin/arm-eabi- \
--nm=$PREBUILT/bin/arm-eabi-nm \
--extra-cflags="-fPIC -DANDROID" \
--disable-asm \
--enable-neon \
--enable-armv5te \
--extra-ldflags="-Wl,-T,$PREBUILT/arm-eabi/lib/ldscripts/armelf.x -Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtbegin.o $PREBUILT/lib/gcc/arm-eabi/4.4.0/crtend.o -lc -lm -ldl"
運行config.sh開始configure
chmod +x config.sh
./config.sh
configure完成後,編輯剛剛生成的config.h,找到這句
#define restrict restrict
Android的GCC不支持restrict關鍵字,於是修改成下面這樣
#define restrict
編輯libavutil/libm.h,把其中的static方法都刪除。
4. 在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的優化。
5. 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
6. ffmpeg里有x264么
FFMPEG是目前被應用最廣泛的編解碼軟體庫,支持多種流行的編解碼器,它是C語言實現的,不僅被集成到各種PC軟體,也經常被移植到多種嵌入式設備中。使用面向對象的辦法來設想這樣一個編解碼庫,首先讓人想到的是構造各種編解碼器的類,然後對於它們的抽象基類確定運行數據流的規則,根據演算法轉換輸入輸出對象。
在實際的代碼,將這些編解碼器分成encoder/decoder,muxer/demuxer和device三種對象,分別對應於編解碼,輸入輸出格式和設備。在main函數的開始,就是初始化這三類對象。在avcodec_register_all中,很多編解碼器被注冊,包括視頻的H.264解碼器和X264編碼器等,
REGISTER_DECODER (H264, h264);
REGISTER_ENCODER (LIBX264, libx264);
找到相關的宏代碼如下
#define REGISTER_ENCODER(X,x) { /
extern AVCodec x##_encoder; /
if(CONFIG_##X##_ENCODER) avcodec_register(&x##_encoder); }
#define REGISTER_DECODER(X,x) { /
extern AVCodec x##_decoder; /
if(CONFIG_##X##_DECODER) avcodec_register(&x##_decoder); }
這樣就實際在代碼中根據CONFIG_##X##_ENCODER這樣的編譯選項來注冊libx264_encoder和h264_decoder,注冊的過程發生在avcodec_register(AVCodec *codec)函數中,實際上就是向全局鏈表first_avcodec中加入libx264_encoder、h264_decoder特定的編解碼器,輸入參數AVCodec是一個結構體,可以理解為編解碼器的基類,其中不僅包含了名稱,id等屬性,而且包含了如下函數指針,讓每個具體的編解碼器擴展類實現。
int (*init)(AVCodecContext *);
int (*encode)(AVCodecContext *, uint8_t *buf, int buf_size, void *data);
int (*close)(AVCodecContext *);
int (*decode)(AVCodecContext *, void *outdata, int *outdata_size,
const uint8_t *buf, int buf_size);
void (*flush)(AVCodecContext *);
繼續追蹤libx264,也就是X264的靜態編碼庫,它在FFMPEG編譯的時候被引入作為H.264編碼器。在libx264.c中有如下代碼
AVCodec libx264_encoder = {
.name = "libx264",
.type = CODEC_TYPE_VIDEO,
.id = CODEC_ID_H264,
.priv_data_size = sizeof(X264Context),
.init = X264_init,
.encode = X264_frame,
.close = X264_close,
.capabilities = CODEC_CAP_DELAY,
.pix_fmts = (enum PixelFormat[]) { PIX_FMT_YUV420P, PIX_FMT_NONE },
.long_name = NULL_IF_CONFIG_SMALL("libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
};
這里具體對來自AVCodec得屬性和方法賦值。其中
.init = X264_init,
.encode = X264_frame,
.close = X264_close,
將函數指針指向了具體函數,這三個函數將使用libx264靜態庫中提供的API,也就是X264的主要介面函數進行具體實現。pix_fmts定義了所支持的輸入格式,這里4:2:0
PIX_FMT_YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
上面看到的X264Context封裝了X264所需要的上下文管理數據,
typedef struct X264Context {
x264_param_t params;
x264_t *enc;
x264_picture_t pic;
AVFrame out_pic;
} X264Context;
它屬於結構體AVCodecContext的void *priv_data變數,定義了每種編解碼器私有的上下文屬性,AVCodecContext也類似上下文基類一樣,還提供其他表示屏幕解析率、量化范圍等的上下文屬性和rtp_callback等函數指針供編解碼使用。
回到main函數,可以看到完成了各類編解碼器,輸入輸出格式和設備注冊以後,將進行上下文初始化和編解碼參數讀入,然後調用av_encode()函數進行具體的編解碼工作。根據該函數的注釋一路查看其過程:
1. 輸入輸出流初始化。
2. 根據輸入輸出流確定需要的編解碼器,並初始化。
3. 寫輸出文件的各部分
重點關注一下step2和3,看看怎麼利用前面分析的編解碼器基類來實現多態。大概查看一下這段代碼的關系,發現在FFMPEG里,可以用類圖來表示大概的編解碼器組合。
可以參考【3】來了解這些結構的含義(見附錄)。在這里會調用一系列來自utils.c的函數,這里的avcodec_open()函數,在打開編解碼器都會調用到,它將運行如下代碼:
avctx->codec = codec;
avctx->codec_id = codec->id;
avctx->frame_number = 0;
if(avctx->codec->init){
ret = avctx->codec->init(avctx);
進行具體適配的編解碼器初始化,而這里的avctx->codec->init(avctx)就是調用AVCodec中函數指針定義的具體初始化函數,例如X264_init。
在avcodec_encode_video()和avcodec_encode_audio()被output_packet()調用進行音視頻編碼,將同樣利用函數指針avctx->codec->encode()調用適配編碼器的編碼函數,如X264_frame進行具體工作。
從上面的分析,我們可以看到FFMPEG怎麼利用面向對象來抽象編解碼器行為,通過組合和繼承關系具體化每個編解碼器實體。設想要在FFMPEG中加入新的解碼器H265,要做的事情如下:
1. 在config編譯配置中加入CONFIG_H265_DECODER
2. 利用宏注冊H265解碼器
3. 定義AVCodec 265_decoder變數,初始化屬性和函數指針
4. 利用解碼器API具體化265_decoder的init等函數指針
完成以上步驟,就可以把新的解碼器放入FFMPEG,外部的匹配和運行規則由基類的多態實現了。
4. X264架構分析
X264是一款從2004年有法國大學生發起的開源H.264編碼器,對PC進行匯編級代碼優化,舍棄了片組和多參考幀等性能效率比不高的功能來提高編碼效率,它被FFMPEG作為引入的.264編碼庫,也被移植到很多DSP嵌入平台。前面第三節已經對FFMPEG中的X264進行舉例分析,這里將繼續結合X264框架加深相關內容的了解。
查看代碼前,還是思考一下對於一款具體的編碼器,怎麼面向對象分析呢?對熵編碼部分對不同演算法的抽象,還有幀內或幀間編碼各種估計演算法的抽象,都可以作為類來構建。
在X264中,我們看到的對外API和上下文變數都聲明在X264.h中,API函數中,關於輔助功能的函數在common.c中定義
void x264_picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_height );
void x264_picture_clean( x264_picture_t *pic );
int x264_nal_encode( void *, int *, int b_annexeb, x264_nal_t *nal );
而編碼功能函數定義在encoder.c
x264_t *x264_encoder_open ( x264_param_t * );
int x264_encoder_reconfig( x264_t *, x264_param_t * );
int x264_encoder_headers( x264_t *, x264_nal_t **, int * );
int x264_encoder_encode ( x264_t *, x264_nal_t **, int *, x264_picture_t *, x264_picture_t * );
void x264_encoder_close ( x264_t * );
在x264.c文件中,有程序的main函數,可以看作做API使用的例子,它也是通過調用X264.h中的API和上下文變數來實現實際功能。
X264最重要的記錄上下文數據的結構體x264_t定義在common.h中,它包含了從線程式控制制變數到具體的SPS、PPS、量化矩陣、cabac上下文等所有的H.264編碼相關變數。其中包含如下的結構體
x264_predict_t predict_16x16[4+3];
x264_predict_t predict_8x8c[4+3];
x264_predict8x8_t predict_8x8[9+3];
x264_predict_t predict_4x4[9+3];
x264_predict_8x8_filter_t predict_8x8_filter;
x264_pixel_function_t pixf;
x264_mc_functions_t mc;
x264_dct_function_t dctf;
x264_zigzag_function_t zigzagf;
x264_quant_function_t quantf;
x264_deblock_function_t loopf;
跟蹤查看可以看到它們或是一個函數指針,或是由函數指針組成的結構,這樣的用法很想面向對象中的interface介面聲明。這些函數指針將在x264_encoder_open()函數中被初始化,這里的初始化首先根據CPU的不同提供不同的函數實現代碼段,很多與可能是匯編實現,以提高代碼運行效率。其次把功能相似的函數集中管理,例如類似intra16的4種和intra4的九種預測函數都被用函數指針數組管理起來。
x264_encoder_encode()是負責編碼的主要函數,而其內包含的x264_slice_write()負責片層一下的具體編碼,包括了幀內和幀間宏塊編碼。在這里,cabac和cavlc的行為是根據h->param.b_cabac來區別的,分別運行x264_macroblock_write_cabac()和x264_macroblock_write_cavlc()來寫碼流,在這一部分,功能函數按文件定義歸類,基本按照編碼流程圖運行,看起來更像面向過程的寫法,在已經初始化了具體的函數指針,程序就一直按編碼過程的邏輯實現。如果從整體架構來看,x264利用這種類似介面的形式實現了弱耦合和可重用,利用x264_t這個貫穿始終的上下文,實現信息封裝和多態。
本文大概分析了FFMPEG/X264的代碼架構,重點探討用C語言來實現面向對象編碼,雖不至於強行向C++靠攏,但是也各有實現特色,保證實用性。值得規劃C語言軟體項目所借鑒。
【參考文獻】
1.「用例子說明面向對象和面向過程的區別」
2. liyuming1978,「liyuming1978的專欄」
3. 「FFMpeg框架代碼閱讀」
附錄:節選自【3】
3. 當前muxer/demuxer的匹配
在FFmpeg的文件轉換過程中,首先要做的就是根據傳入文件和傳出文件的後綴名[FIXME]匹配
合適的demuxer和muxer。匹配上的demuxer和muxer都保存在如下所示,定義在ffmpeg.c里的
全局變數file_iformat和file_oformat中:
static AVInputFormat *file_iformat;
static AVOutputFormat *file_oformat;
3.1 demuxer匹配
在libavformat/utils.c中的static AVInputFormat *av_probe_input_format2(
AVProbeData *pd, int is_opened, int *score_max)函數用途是根據傳入的probe data數據
,依次調用每個demuxer的read_probe介面,來進行該demuxer是否和傳入的文件內容匹配的
判斷。其調用順序如下:
void parse_options(int argc, char **argv, const OptionDef *options,
void (* parse_arg_function)(const char *));
static void opt_input_file(const char *filename)
int av_open_input_file(…… )
AVInputFormat *av_probe_input_format(AVProbeData *pd,
int is_opened)
static AVInputFormat *av_probe_input_format2(……)
opt_input_file函數是在保存在const OptionDef options[]數組中,用於
void parse_options(int argc, char **argv, const OptionDef *options)中解析argv里的
「-i」 參數,也就是輸入文件名時調用的。
3.2 muxer匹配
與demuxer的匹配不同,muxer的匹配是調用guess_format函數,根據main() 函數的argv里的
輸出文件後綴名來進行的。
void parse_options(int argc, char **argv, const OptionDef *options,
void (* parse_arg_function)(const char *));
void parse_arg_file(const char *filename)
static void opt_output_file(const char *filename)
AVOutputFormat *guess_format(const char *short_name,
const char *filename,
const char *mime_type)
3.3 當前encoder/decoder的匹配
在main()函數中除了解析傳入參數並初始化demuxer與muxer的parse_options( )函數以外,
其他的功能都是在av_encode( )函數里完成的。
在libavcodec/utils.c中有如下二個函數:
AVCodec *avcodec_find_encoder(enum CodecID id)
AVCodec *avcodec_find_decoder(enum CodecID id)
他們的功能就是根據傳入的CodecID,找到匹配的encoder和decoder。
在av_encode( )函數的開頭,首先初始化各個AVInputStream和AVOutputStream,然後分別調
用上述二個函數,並將匹配上的encoder與decoder分別保存在:
AVInputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec與
AVOutputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec變數。
4. 其他主要數據結構
4.1 AVFormatContext
AVFormatContext是FFMpeg格式轉換過程中實現輸入和輸出功能、保存相關數據的主要結構。
每一個輸入和輸出文件,都在如下定義的指針數組全局變數中有對應的實體。
static AVFormatContext *output_files[MAX_FILES];
static AVFormatContext *input_files[MAX_FILES];
對於輸入和輸出,因為共用的是同一個結構體,所以需要分別對該結構中如下定義的iformat
或oformat成員賦值。
struct AVInputFormat *iformat;
struct AVOutputFormat *oformat;
對一個AVFormatContext來說,這二個成員不能同時有值,即一個AVFormatContext不能同時
含有demuxer和muxer。在main( )函數開頭的parse_options( )函數中找到了匹配的muxer和
demuxer之後,根據傳入的argv參數,初始化每個輸入和輸出的AVFormatContext結構,並保
存在相應的output_files和input_files指針數組中。在av_encode( )函數中,output_files
和input_files是作為函數參數傳入後,在其他地方就沒有用到了。
4.2 AVCodecContext
保存AVCodec指針和與codec相關數據,如video的width、height,audio的sample rate等。
AVCodecContext中的codec_type,codec_id二個變數對於encoder/decoder的匹配來說,最為
重要。
enum CodecType codec_type; /* see CODEC_TYPE_xxx */
enum CodecID codec_id; /* see CODEC_ID_xxx */
如上所示,codec_type保存的是CODEC_TYPE_VIDEO,CODEC_TYPE_AUDIO等媒體類型,
codec_id保存的是CODEC_ID_FLV1,CODEC_ID_VP6F等編碼方式。
以支持flv格式為例,在前述的av_open_input_file(…… ) 函數中,匹配到正確的
AVInputFormat demuxer後,通過av_open_input_stream( )函數中調用AVInputFormat的
read_header介面來執行flvdec.c中的flv_read_header( )函數。在flv_read_header( )函數
內,根據文件頭中的數據,創建相應的視頻或音頻AVStream,並設置AVStream中
AVCodecContext的正確的codec_type值。codec_id值是在解碼過程中flv_read_packet( )函
數執行時根據每一個packet頭中的數據來設置的。
4.3 AVStream
AVStream結構保存與數據流相關的編解碼器,數據段等信息。比較重要的有如下二個成員:
AVCodecContext *codec; /**< codec context */
void *priv_data;
其中codec指針保存的就是上節所述的encoder或decoder結構。priv_data指針保存的是和具
體編解碼流相關的數據,如下代碼所示,在ASF的解碼過程中,priv_data保存的就是
ASFStream結構的數據。
AVStream *st;
ASFStream *asf_st;
… …
st->priv_data = asf_st;
4.4 AVInputStream/ AVOutputStream
根據輸入和輸出流的不同,前述的AVStream結構都是封裝在AVInputStream和AVOutputStream
結構中,在av_encode( )函數中使用。AVInputStream中還保存的有與時間有關的信息。
AVOutputStream中還保存有與音視頻同步等相關的信息。
4.5 AVPacket
AVPacket結構定義如下,其是用於保存讀取的packet數據。
typedef struct AVPacket {
int64_t pts; ///< presentation time stamp in time_base units
int64_t dts; ///< decompression time stamp in time_base units
uint8_t *data;
int size;
int stream_index;
int flags;
int ration; ///< presentation ration in time_base units (0 if not available)
void (*destruct)(struct AVPacket *);
void *priv;
int64_t pos; ///< byte position in stream, -1 if unknown
} AVPacket;
在av_encode()函數中,調用AVInputFormat的
(*read_packet)(struct AVFormatContext *, AVPacket *pkt)介面,讀取輸入文件的一幀數
據保存在當前輸入AVFormatContext的AVPacket成員中。
7. ffmpeg編譯視頻加上字幕
ffmpeg編譯及使用ffmpeg編譯及使用
1 ffmpeg介紹
ffmpeg是音視頻的分離,轉換,編碼解碼及流媒體的完全解決方案,其中最重要的就是libavcodec庫。它被mplayer或者xine使用作為解碼器。還有,國內比較流行的播放器影音風暴或MyMPC的後端ffdshow也是使用ffmpeg的解碼庫的。
ffmpeg軟體包經編譯過後將生成三個可執行文件,ffmpeg,ffserver,ffplay。其中ffmpeg用於對媒體文件進行處理,ffserver是一個http的流媒體伺服器,ffplay是一個基於SDL的簡單播放器。
ffmpeg中有五個庫文件,libavcodec,libavformat,libavutil,libswscale,libpostproc,其中庫libavcodec,libavformat用於對媒體文件進行處理,如格式的轉換;libavutil是一個通用的小型函數庫,該庫中實現了CRC校驗碼的產生,128位整數數學,最大公約數,整數開方,整數取對數,內存分配,大端小端格式的轉換等功能;libswscale,libpostproc暫時不知道何用。
2 ffmpeg下載
最新的ffmpeg可以通過svn下載,SVN輔助的軟體有:
SubVersion,從 http://subversion.tigris.org/ 下載,支持linux。
TortoiseSVN,從 http://tortoisesvn.tigris.org/ 下載,是很不錯的SVN客戶端程序,為windows外殼程序集成到windows資源管理器和文件管理系統的Subversion客戶端,用起來很方便。
subversion安裝,記住最好之前裝過apr和apr-util,在apache.org網站能下到
wget http://subversion.tigris.org/downloads/subversion-1.3.2.tar.gz
tar zvxf subversion-1.3.2.tar.gz
cd subversion-1.3.2
./configure --with-apr=/usr/local/apr-httpd --with-apr-util=/usr/local/apr-util-httpd/
make
make install
如果安裝了FC6,它已經帶了svn,不用裝了。
ffmpeg的下載:我們就可以通過svn命令獲取最新的ffmpeg,命令如下:
svn checkout svn://svn.mplayerhq.hu/ffmpeg/trunk ffmpeg
3 ffmpeg支持庫的安裝
* xvid
xvid的獲取地址如下:
http://www.xvid.org/
wget http://downloads.xvid.org/downloads/xvidcore-1.1.3.tar.gz
配置編譯
for x86
#./configure --prefix=/usr/local
#make
#make install
for arm
#CC=arm-linux-gcc ./configure --prefix=/usr/local/arm/arm-linux --build=i686-pc-linux --host=arm-linux --target=arm-linux
#make
#make install
* x264
x264的獲取地址如下:
svn co svn://svn.videolan.org/x264/trunk x264
配置編譯
for x86
#./configure --enable-shared --prefix=/usr/local
#make
#make install
for arm
#CC=arm-linux-gcc ./configure --enable-pthread --enable-shared --host=arm-linux
--prefix=/usr/local/arm/arm-linux
#make
#make install
* 支持mp3
lame的獲取地址如下: http://lame.sourceforge.net/index.php
配置編譯
for x86
./configure --enable-shared --prefix=/usr/local
* 支持Ogg Vorbis:
* AC3和dts編碼的支持
libdts編譯參數
./configure --prefix=/usr
make
make install
* mpg4 aac格式支持,如果ffserver伺服器還針對手機用戶服務,所以,類似aac,mpg4鈴聲格式的支持,我們也得做。這里我們安裝faad2和faac就行,下載請到http://www.audiocoding.com/moles/mydownloads/,http://prdownloads.sourceforge.net/faac
FAAD2的編譯
cd faad2
autoreconf -vif
./configure --prefix=/usr --with-mp4v2 --enable-shared
make
make install
faac的編譯
cd faac
chmod +x bootstrap
./bootstrap
./configure --prefix=/usr --with-mp4v2 --enable-shared
make
make install
在編譯ffmpeg,在configure時加上--enable-amr_nb --enable-faad --enable-faac參數。
* 支持3gp格式,這也是現在好多手機支持的格式,所以也得支持編譯
編譯的時候加上--enable-amr_nb --enable-amr_wb參數就行,根據編譯系統的提示,所以我們得下載一些編譯3gp所需得文件。
源碼網址:http://www.3gpp.org/ftp/Specs
wget http://www.3gpp.org/ftp/Specs/ar ... 6.204/26204-510.zip
解壓以後把裡面的文件都拷貝到libavcodec/amrwb_float
wget http://www.3gpp.org/ftp/Specs/ar ... 6.104/26104-510.zip
解壓以後把裡面的文件都拷貝到libavcodec/amr_float
* ffmpeg支持VC1格式
微軟ASF格式的三個版本,WMV1,WMV2,WMV3分別對應MediaPlayer的版本7,8和9,所以很多時候會稱VC1為WMV3或 WMV9,都是它了,有時候在代碼里,也能看到稱呼它為VC9的。因為微軟還沒有正式公開這種格式,所以當前對VC1的支持還很不完善。本文基本是根據Multimedia Mike的一篇博客翻譯和完善而來。
(1) 首先要下載 SMPTE VC-1 reference decoder,這個組織是要收費的,可以從這里下載免費的。
(2) 在ffmpeg目錄下的libavcodec目錄下面,建立目錄libvc1。
(3) 將VC1_reference_decoder_release6/decoder/目錄中的*.c和*.h文件全部到libvc1目錄下。
(4) 將VC1_reference_decoder_release6/shared/目錄中的*.c和*.h文件全部到libvc1目錄下。
(5) 將 libvc1-makefile.txt放到libvc1下的Makefile文件。
(6) 將smpte-vc1.c文件放到libavcodec目錄下。
(7) 修改libavcodec目錄下的vc9.c,將文件最後的wmv3_decoder這個AVCodec的structure,用#if 0和#endif包含起來,也就是使它失效了。
(8) 修改libavcodec目錄下的allcodecs.c