ffmpeg的基本用法
v 容器(Container)
v 容器就是一種檔案(封裝)格式,比如flv、mkv、ts、mp4、rmvb、avi等。包含下面5種流以及檔案頭資訊。
v 流(Stream)
v 是一種視訊資料資訊的傳輸方式,5種流:音訊,視訊,字幕,附件,資料。
v 幀(Frame) 代表一幅靜止的影象,分為I幀,P幀,B幀。
v 編解碼器(Codec)
v 是對視訊進行壓縮或者解壓縮,CODEC =CODE(編碼) +DECODE(解碼)
v 複用/解複用(mux/demux)
v 把不同的流按照某種容器的規則放入容器,這種行為叫做複用(mux)
把不同的流從某種容器中解析出來,這種行為叫做解複用(demux)
v 位元速率和幀率是視訊檔案的最重要的基本特徵,對於他們的特有設定會決定視訊質量。如果我們知道位元速率和時長那麼可以很容易計算出輸出檔案的大小。
v 幀率:幀率也叫幀頻率,幀率是視訊檔案中每一秒的幀數,肉眼想看到連續移動影象至少需要15幀。
v 位元速率:位元率(也叫位元速率,資料率)是一個確定整體視訊/音訊質量的引數,秒為單位處理的位元組數,位元速率和視訊質量成正比,在視訊檔案中中位元率用bps來表達。
轉碼流程
v FFmpeg的名稱來自MPEG視訊編碼標準,前面的“FF”代表“Fast Forward”,FFmpeg是一套可以用來音視訊採集、音視訊格式轉換,編碼解碼,視訊截圖,加水印等的開源計算機程式。可以輕易地實現多種視訊格式之間的相互轉換。
v FFmpeg的使用者有Google,Facebook,Youtube,VLC,優酷,愛奇藝,土豆,Mplayer,射手播放器,暴風影音,KMPlayer,QQ影音,格式工廠,狸窩視訊轉換器,暴風轉碼等。
v FFmpeg的開發是基於Linux作業系統,但是可以在大多數作業系統中編譯和使用。(在vs2010中編譯不了,因為vs2010支援的是C89(不支援C99) ,ffmpeg使用的是C99,vs2013/2015可以編譯)
v FFmpeg一共包含8個庫:
v 1、avcodec:編解碼(最重要的庫)。
v 2、avformat:封裝格式處理。
v 3、avfilter:濾鏡特效處理。
v 4、avdevice:各種裝置的輸入輸出。
v 5、avutil:工具庫(大部分庫都需要這個庫的支援)。
v 6、postproc:後加工。
v 7、swresample:音訊取樣資料格式轉換。
v 8、swscale:視訊畫素資料格式轉換
v FFmpeg一共包含四個主要程式:
v 1、ffmpeg:是一個命令列工具,用來對視音訊檔案轉換格式,也支援對電視卡實時編碼;
ffmpeg -i input.flv -c:v libx264 -c:a libfaac -b:v 800k -b:a 100k -r 25 -ar 48000 -s 1280x720 -f flv out.flv
v 2、ffsever:是一個HTTP多媒體實時廣播流伺服器,支援時光平移
v 3、ffplay:是一個簡單的播放器,使用ffmpeg 庫解析和解碼,通過SDL顯示;
ffplay input.avi
該命令將播放當前資料夾下
的input.avi檔案
v 4、ffprobe:探測分析視音訊檔案
ffprobe -i input -print_format json -show_format -show_streams -show_frames
v 官網:
v http://ffmpeg.org/download.html
v https://ffmpeg.zeranoe.com/builds/
v Linux下編譯很簡單 ./configure && make && make install
v Windows下使用MinGW的gcc toolchain進行編譯,沒有pdb,無法進行除錯
v Windows下可以使用vs2013/2015版本編譯
v configure的時候可以指定配置,開啟或關閉一些選項,啟用外部的編解碼庫等
./configure --enable-shared --enable-version3 --enable-gpl --enable-nonfree --enable-libfdk_aac --enable-libmp3lame --enable-zlib --enable-libspeex --enable-libx264 --prefix=/usr/lib/buildwin64ffmpeg
學習研究ffmpeg推薦在linux下,編譯除錯都方便(試了vs2015也不錯,有個老外有個開源工程都整合好了)。
v 可用的bit流 :ffmpeg –bsfs
v 可用的編解碼器:ffmpeg –codecs
v 可用的解碼器:ffmpeg –decoders
v 可用的編碼器:ffmpeg –encoders
v 可用的過濾器:ffmpeg –filters
v 可用的視訊格式:ffmpeg –formats
v 可用的聲道佈局:ffmpeg –layouts
v 可用的license:ffmpeg –L
v 可用的畫素格式:ffmpeg –pix_fmts
v 可用的協議:ffmpeg -protocals
v 改變視訊的解析度 -s 1280x720
v 改變視訊的幀率 -r 15
v 改變音訊的取樣率 -ar 44100
v 改變視音訊的位元速率 -b:v 1000k -b:a 80k
v 設定輸出格式 -f flv(mpegts/hls/mp4)
v 設定視音訊編碼格式 -c:v libx264 –c:a libfaac
v 指定視音訊不轉碼 -c copy -c:a copy –c:v copy
v 設定處理開始時間 -ss HH:MM:SS
v 設定處理結束時間 -to HH:MM:SS
v 不要視訊或者音訊 –vn –an
v 設定關鍵幀間隔
-force_key_frames "expr:gte(t,n_forced*1)"
ffmpeg擷取視訊
v ffmpeg -i %s -ss %02d:%02d:%02d -to %02d:%02d:%02d -c copy -f %s %s -y
v 示例:
ffmpeg –i input.flv –ss 00:10:00 –to 00:20:00 –c copy –f flv jiequ.flv –y
v 說明:
v -i 指定輸入
v -ss 指定擷取的開始時間
v -to指定擷取的結束時間
v 可以用兩種方式指定時間,一種是用時分秒指定、中間用冒號分離HH:MM:SS,另一種是直接用秒數指定
v –c copy即音視訊編碼採用直接複製的方式,不進行轉碼
v -f flv 指定輸出格式為flv,這裡的flv可以替換為mp4,ts,hls等,後面接著輸出檔名
v -y即如果檔案存在,直接覆蓋寫
ffmpeg合併視訊
v 把檔名都輸出到一個檔案,如下放到join.txt,格式是file+空格+檔名+換行+……
v ffmpeg -f concat -i %s -c copy -f %s %s –y
v 示例:
v ffmpeg -f concat -i join.txt -c copy -f flv join.flv –y
v 注:只適用於相同編解碼引數音視訊的合併(不同的得轉碼可以用filter合併)
ffmpeg轉封裝
v ffmpeg -i %s -c copy -f %s %s -y
v 示例:ffmpeg -i input.flv -c copy -f mp4 output.mp4 -y
這個比較簡單,直接指定不轉碼,輸出格式和檔名就好了。
v 其中轉hls有一些額外的引數可供選擇
v -hls_time 指定切片時長,預設值為2
v -hls_list_size 指定m3u8中ts切片數量的最大值,點播可以配置一個很大的數(最大可配值INT_MAX),即都儲存,直播可以酌情配置
v -hls_segment_filename 指定ts切片的名字
v 示例:
v ffmpeg -i input.flv -c copy -hls_time 10 -hls_list_size 10000000 -hls_segment_filename hongyun_%d.ts -f hls hongyun.m3u8
ffmpeg轉碼
v ffmpeg -i %s -c:v %s -c:a %s -ar %s -b:v %s -b:a %s -f %s %s -y
v 示例:
v ffmpeg -i input.flv -c:v h264 -c:a libmp3lame -ar 44100 -b:v 1000k -b:a 80k -f flv output.flv –y
v 說明:
v -c:v指定視訊編碼格式
v -c:a指定音訊編碼格式
v -ar 指定音訊取樣率
v -b:v 指定視訊位元速率
v -b:a 指定音訊位元速率
v 注:轉換過程中可能會遇到一些問題,ffmpeg會給出提示資訊,我們可以根據提示資訊進行進一步轉碼
v
v 這種情況我們加上 -bsf:v h264_mp4toannexb 應該就可以了
ffmpeg 混屏(畫中畫)(多屏播放)
v ffmpeg混屏主要用到的filter中視訊相關的有scale、pad、overlay;音訊相關的是amix
v ffmpeg -i rtmp://10.111.22.210/live/livestream6 -i xx.flv -i rtmp://10.111.22.210/live/livestream5 -i rtmp://10.121.22.210/live/livestream7 -filter_complex "[0:v]scale=300:200,pad=600:400:0:0[left];[1:v]scale=300:200[right];[left][right]overlay=300:0[up];[2:v]scale=300:200[down]; [up][down]overlay=0:200[downleft];[3:v]scale=300:200[downright];[downleft][downright]overlay=300:200;amix=inputs=4" -c:v h264 -c:a libfaac -f flv rtmp://192.168.11.168/live/livestream
v 命令中首先指定了4路輸入;然後按一下步驟進行操作:
v 1.指定第一路流的視訊([0:v])作為輸入,進行比例變換(scale=300:200)、並填充視訊(pad=600:400:0:0),輸出為[left];
v 2.指定第二路流的視訊([1:v])為輸入進行比例變換(scale=300:200),輸出為[right];
v 3.把[right]疊加到[left]上([left][right]overlay),並指定位置,座標為(300:0),輸出為[up];
v 4.指定第三路流的視訊([2:v]) 為輸入進行比例變換(scale=300:200),輸出為[down];
v 5. 把[down] 疊加到[up] 上([up][down]overlay)並指定位置,座標為(0:200),輸出為 [downleft];
v 6.指定第四路流的視訊([3:v])為輸入進行比例變換(scale=300:200),輸出為[downright];
v 7. 把[downright]疊加到[downleft]上([downleft][downright]overlay) 並指定位置,座標為(300:200),這裡我們不指定輸出了(當然也可以指定),因為濾鏡鏈如果沒有指定輸入或輸出,則預設使用前面的濾鏡鏈的輸出為輸入,並輸出給後面的濾鏡鏈做輸入。
v 8. amix=inputs=4,對音訊進行混流,這裡我們指定混4路音訊
v 兩路視訊分屏的效果
ffmpeg中很多框架都是基於函式指標,有點類似於C++的基類,有幾個重要的結構體,下面介紹下
幾個重要的結構體
v AVFormatContext 封裝格式上下文結構體,也是統領全域性的結構體,儲存了視訊檔案封裝格式相關資訊。
v AVInputFormat 每種作為輸入的封裝格式(例如FLV, MKV, MP4, AVI)對應一個該結構體。
v AVOutputFormat 每種作為輸出的封裝格式(例如FLV, MKV, MP4, AVI)對應一個該結構體。
v AVStream 視訊檔案中每個視訊(音訊)流對應一個該結構體。
v AVCodecContext 編碼器上下文結構體,儲存了視訊(音訊)編解碼相關資訊。
v AVCodec 每種視訊(音訊)編解碼器(例如H.264解碼器)對應一個該結構體。
v AVPacket 儲存一幀壓縮編碼資料。
v AVFrame 儲存一幀解碼後像素(取樣)資料。
v AVIOContext 管理輸入輸出資料的結構體
AVFormatContext
v AVFormatContext中幾個重要的成員
v struct AVInputFormat *iformat; 輸入的封裝格式(Demuxing only)
v struct AVOutputFormat *oformat;輸入的封裝格式(Muxing only)
v unsigned int nb_streams; 輸入/出視訊的AVStream 個數
v AVStream **streams; 輸入/出視訊的AVStream []陣列
v duration :輸入視訊的時長(以微秒為單位)( Demuxing only )
v bit_rate :輸入視訊的位元速率。
AVInputFormat
v AVInputFormat中幾個重要的成員
v const char *name; 封裝格式名稱
v const char *long_name ;封裝格式的長名稱
v const char *extensions; 封裝格式的副檔名
v 一些封裝格式處理的介面函式。
v int (*read_probe)(AVProbeData *);
v int (*read_header)(struct AVFormatContext *);
v int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);
v int (*read_close)(struct AVFormatContext *);
v int (*read_seek)(struct AVFormatContext *, int stream_index, int64_t timestamp, int flags);
AVInputFormat例子
v AVInputFormat ff_flv_demuxer = {
.name = "flv",
.long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
.priv_data_size = sizeof(FLVContext),
.read_probe = flv_probe,
.read_header = flv_read_header,
.read_packet = flv_read_packet,
.read_seek = flv_read_seek,
.read_close = flv_read_close,
.extensions = "flv",
.priv_class = &flv_class,
};
AVOutputFormat
v AVOutputFormat中幾個重要的成員
v const char *name; 封裝格式名稱
v const char *long_name ;封裝格式的長名稱
v const char *extensions; 封裝格式的副檔名
v 一些封裝格式處理的介面函式。
v int (*write_header)(struct AVFormatContext *);
v int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);
v int (*write_trailer)(struct AVFormatContext *);
AVOutputFormat例子
v AVOutputFormat ff_flv_muxer = {
.name = "flv",
.long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
.mime_type = "video/x-flv",
.extensions = "flv",
.priv_data_size = sizeof(FLVContext),
.audio_codec = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF,
.video_codec = AV_CODEC_ID_FLV1,
.write_header = flv_write_header,
.write_packet = flv_write_packet,
.write_trailer = flv_write_trailer,
.codec_tag = (const AVCodecTag* const []) { flv_video_codec_ids, flv_audio_codec_ids, 0},
.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |
AVFMT_TS_NONSTRICT,
.priv_class = &flv_muxer_class,
};
AVStream
v AVStream中幾個重要的成員
v int index; /**< stream index in AVFormatContext */
v int id;
v AVCodecContext *codec; 該流對應的AVCodecContext
v AVRational time_base; 該流的時基
v AVRational r_frame_rate; 該流的幀率
AVCodecContext
v AVCodecContext中幾個重要的成員
v const struct AVCodec *codec; 編解碼器的AVCodec
v int width, height; 影象的寬高(只針對視訊)
v enum AVPixelFormat pix_fmt; 畫素格式(只針對視訊)
v int sample_rate; 取樣率(只針對音訊)
v int channels; 聲道數(只針對音訊)
v enum AVSampleFormat sample_fmt; 取樣格式(只針對音訊)
AVCodec
v AVCodec中幾個重要的成員
v const char *name; 編解碼器名稱
v const char *long_name; 編解碼器長名稱
v enum AVMediaType type; 編解碼器型別
v enum AVCodecID id; 編解碼器ID
v 一些編解碼的介面函式
v int (*init)(AVCodecContext *);
v int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr);
v int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt);
v int (*close)(AVCodecContext *);
AVCodec例子
v AVCodec ff_h264_decoder = {
.name = "h264",
.long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.priv_data_size = sizeof(H264Context),
.init = ff_h264_decode_init,
.close = h264_decode_end,
.decode = h264_decode_frame,
.profiles = NULL_IF_CONFIG_SMALL(ff_h264_profiles),
.priv_class = &h264_class,
};
v AVCodec ff_libx264_encoder = {
.name = "libx264",
.long_name = NULL_IF_CONFIG_SMALL("libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.priv_data_size = sizeof(X264Context),
.init = X264_init,
.encode2 = X264_frame,
.close = X264_close,
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS,
.priv_class = &x264_class,
.defaults = x264_defaults,
.init_static_data = X264_init_static,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE |
FF_CODEC_CAP_INIT_CLEANUP,
};
AVPacket
v AVPacket中幾個重要的成員
v int64_t pts; 顯示時間戳
v int64_t dts; 解碼時間戳
v uint8_t *data; 壓縮編碼資料
v int size; 壓縮編碼資料大小
v int stream_index; 所屬的AVStream
v int flags; 1關鍵幀
AVFrame
v AVFrame中幾個重要的成員
v uint8_t *data[AV_NUM_DATA_POINTERS]; 解碼後的影象畫素資料(音訊取樣資料)。
v int linesize[AV_NUM_DATA_POINTERS]; 對視訊來說是影象中一行畫素的大小;對音訊來說是整個音
v 頻幀的大小。
v int width, height; 影象的寬高(只針對視訊)。
v int key_frame; 是否為關鍵幀(只針對視訊) 。
v enum AVPictureType pict_type; 幀型別(只針對視訊) 。例如I,P,B。
AVIOContext
v AVIOContext中幾個重要的成員
v unsigned char *buffer; /**< Start of the buffer. */
v int buffer_size; /**< Maximum buffer size */
v unsigned char *buf_ptr; /**< Current position in the buffer */
v unsigned char *buf_end; /**< End of the data, may be less than buffer+buffer_size if the read function returned less data than requested, e.g. for streams where no more data has been received yet. */
v void *opaque; /**< A private pointer, passed to the read/write/seek/... functions. */
v int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
v int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);
v int64_t (*seek)(void *opaque, int64_t offset, int whence);
v int64_t pos; /**< position in the file of the current buffer */
v AVIOContext中幾個重要的成員
v 在解碼的情況下,buffer用於儲存ffmpeg讀入的資料。例如開啟一個視訊檔案的時候,先把資料從硬碟讀入buffer,然後在送給解碼器用於解碼。
v 其中opaque指向了URLContext。
typedef struct URLContext {
const AVClass *av_class; /**< information for av_log(). Set by url_open(). */
const struct URLProtocol *prot;
void *priv_data;
char *filename; /**< specified URL */
int flags;
……
} URLContext;
URLProtocol
typedef struct URLProtocol {
const char *name;
int (*url_open)( URLContext *h, const char *url, int flags);
int (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
int (*url_accept)(URLContext *s, URLContext **c);
int (*url_handshake)(URLContext *c);
int (*url_read)( URLContext *h, unsigned char *buf, int size);
int (*url_write)(URLContext *h, const unsigned char *buf, int size);
int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);
int (*url_close)(URLContext *h);
int (*url_read_pause)(URLContext *h, int pause);
int64_t (*url_read_seek)(URLContext *h, int stream_index,
int64_t timestamp, int flags);
int (*url_get_file_handle)(URLContext *h);
int (*url_get_multi_file_handle)(URLContext *h, int **handles,
int *numhandles);
int (*url_shutdown)(URLContext *h, int flags);
…..
} URLProtocol;
URLProtocol例子
const URLProtocol ff_file_protocol = {
.name = "file",
.url_open = file_open,
.url_read = file_read,
.url_write = file_write,
.url_seek = file_seek,
.url_close = file_close,
.url_get_file_handle = file_get_handle,
.url_check = file_check,
.url_delete = file_delete,
.url_move = file_move,
.priv_data_size = sizeof(FileContext),
.priv_data_class = &file_class,
.url_open_dir = file_open_dir,
.url_read_dir = file_read_dir,
.url_close_dir = file_close_dir,
.default_whitelist = "file,crypto"
};
const URLProtocol ff_tcp_protocol = {
.name = "tcp",
.url_open = tcp_open,
.url_accept = tcp_accept,
.url_read = tcp_read,
.url_write = tcp_write,
.url_close = tcp_close,
.url_get_file_handle = tcp_get_file_handle,
.url_shutdown = tcp_shutdown,
.priv_data_size = sizeof(TCPContext),
.flags = URL_PROTOCOL_FLAG_NETWORK,
.priv_data_class = &tcp_class,
};
轉載自:https://blog.csdn.net/tuleiying/article/details/60874276