須要調用ffmpeg的API首先須要引入對應的頭文件:緩存
extern "C"{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
複製代碼
//初始化解封裝
av_register_all();
//初始化網絡,能夠直接從服務器拉流
avformat_network_init();
複製代碼
av_register_all()用於註冊全部複用器,編碼器和協議處理器。若是要指定註冊某種編碼器可使用:av_register_input_format() ,av_register_output_format(),ffurl_register_protocol()。av_register_all()調用了avcodec_register_all()。avcodec_register_all()註冊了和編解碼器有關的組件:硬件加速器,解碼器,編碼器,Parser,Bitstream Filter。av_register_all()除了調用avcodec_register_all()以外,還註冊了複用器,解複用器,協議處理器。 bash
//打開文件
AVFormatContext *ic = NULL;
char path[] = "/sdcard/1080.mp4";
int re = avformat_open_input(&ic,path,0,0);
if(re == 0)
{
LOGW("avformat_open_input %s success!",path);
}
else
{
LOGW("avformat_open_input failed!:%s",av_err2str(re));
}
複製代碼
ffmpeg打開媒體的的過程開始於avformat_open_input()。在該方法調用以前確保av_register_all(),avformat_network_init()已經被調用。該函數用於打開多媒體數據(輸入流)而且得到一些相關的信息(頭數據)。對應的關閉流的函數爲avformat_close_input()。服務器
該方法中主要完成了:網絡
輸入輸出結構體AVIOContext的初始化;socket
輸入數據的協議(例如RTMP,或者file)的識別(經過一套評分機制):1.判斷文件名的後綴 2.讀取文件頭的數據進行比對;ide
使用得到最高分的文件協議對應的URLProtocol,經過函數指針的方式,與FFMPEG鏈接(非專業用詞);函數
剩下的就是調用該URLProtocol的函數進行open,read等操做了。ui
URLProtocol結構以下,是一大堆函數指針的集合(avio.h文件)編碼
typedef struct URLProtocol {
const char *name;
int (*url_open)(URLContext *h, const char *url, int flags);
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);
struct URLProtocol *next;
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 priv_data_size;
const AVClass *priv_data_class;
int flags;
int (*url_check)(URLContext *h, int mask);
} URLProtocol;
複製代碼
URLProtocol功能就是完成各類輸入協議的讀寫等操做。加密
該方法的簽名爲:
int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options);
複製代碼
函數執行成功的話,其返回值大於等於0。
AVFormatContext:輸入數據的封裝格式
AVIOContext *pb:輸入數據的緩存
unsigned int nb_streams:視音頻流的個數
AVStream **streams:視音頻流
char filename[1024]:文件名
int64_t duration:時長(單位:微秒us,轉換爲秒須要除以1000000)
int bit_rate:比特率(單位bps,轉換爲kbps須要除以1000)
AVDictionary *metadata:元數據
char filename[1024]:輸入或輸出文件名
void avformat_close_input(AVFormatContext **s);:該函數用於關閉一個AVFormatContext,通常狀況下是和avformat_open_input()成對使用的。
avformat_find_stream_info()。該函數能夠讀取一部分視音頻數據而且得到一些相關的信息(適用於沒有頭部信息的文件):
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
複製代碼
函數正常執行後返回值大於等於0。
//獲取流信息
re = avformat_find_stream_info(ic,0);
if(re != 0)
{
LOGW("avformat_find_stream_info failed!");
}
LOGW("duration = %lld nb_streams = %d",ic->duration,ic->nb_streams);
複製代碼
獲取音視頻信息:
static double r2d(AVRational r)
{
return r.num==0||r.den == 0 ? 0 :(double)r.num/(double)r.den;
}
int fps = 0;
int videoStream = 0;
int audioStream = 1;
for(int i = 0; i < ic->nb_streams; i++)
{
AVStream *as = ic->streams[i];
if(as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
LOGW("視頻數據");
videoStream = i;
fps = r2d(as->avg_frame_rate);
LOGW("fps = %d,width=%d height=%d codeid=%d pixformat=%d",fps,
as->codecpar->width,
as->codecpar->height,
as->codecpar->codec_id,
as->codecpar->format
);
}
else if(as->codecpar->codec_type ==AVMEDIA_TYPE_AUDIO )
{
LOGW("音頻數據");
audioStream = i;
LOGW("sample_rate=%d channels=%d sample_format=%d",
as->codecpar->sample_rate,
as->codecpar->channels,
as->codecpar->format
);
}
}
複製代碼
AVStream:存儲每個視頻/音頻流信息的結構體。
int index:標識該視頻/音頻流
AVCodecContext *codec:指向該視頻/音頻流的AVCodecContext(它們是一一對應的關係)。codec參數在58版本及以後就不會支持了,須要由codecpar參數所替代。
AVRational time_base:時基。經過該值能夠把PTS,DTS轉化爲真正的時間。FFMPEG其餘結構體中也有這個字段,可是根據個人經驗,只有AVStream中的time_base是可用的。PTS*time_base=真正的時間。
int64_t duration:該視頻/音頻流長度。
AVDictionary *metadata:元數據信息。
AVRational avg_frame_rate:幀率(注:對視頻來講,這個挺重要的)。
AVPacket attached_pic:附帶的圖片。好比說一些MP3,AAC音頻文件附帶的專輯封面。
AVCodecParameters *codecpar:codec參數在58版本及以後就不會支持了,須要由codecpar參數所替代。
獲取音頻流索引:
//獲取音頻流信息
audioStream = av_find_best_stream(ic,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);
複製代碼
int av_find_best_stream (
AVFormatContext * ic,
enum AVMediaType type,
int wanted_stream_nb,
int related_stream,
AVCodec ** decoder_ret,
int flags
)
複製代碼
在文件中找到「最佳」流。
ic:媒體文件句柄。
type:流類型:視頻,音頻,字幕等。
wanted_stream_nb:用戶請求的流號碼,或-1用於自動選擇。
related_stream:嘗試查找與此相關的流(例如,在相同的程序中),若是沒有,則返回-1。
decoder_ret:若是非NULL,則返回所選流的解碼器。
flags:目前沒有定義。
//讀取幀數據
AVPacket *pkt = av_packet_alloc();
for(;;)
{
int re = av_read_frame(ic,pkt);
if(re != 0)
{
LOGW("讀取到結尾處!");
int pos = 20 * r2d(ic->streams[videoStream]->time_base);
av_seek_frame(ic,videoStream,pos,AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME );
continue;
}
LOGW("stream = %d size =%d pts=%lld flag=%d",
pkt->stream_index,pkt->size,pkt->pts,pkt->flags
);
av_packet_unref(pkt);
}
複製代碼
AVPacket *av_packet_alloc(void):分配一個AVPacket結構體大小的內存。
void av_packet_unref(AVPacket *pkt):釋放對應的AVPacket結構體。
AVPacket是存儲壓縮編碼數據相關信息的結構體。
uint8_t *data:壓縮編碼的數據。
例如對於H.264來講。1個AVPacket的data一般對應一個NAL。
注意:在這裏只是對應,而不是如出一轍。他們之間有微小的差異:使用FFMPEG類庫分離出多媒體文件中的H.264碼流
所以在使用FFMPEG進行視音頻處理的時候,經常能夠將獲得的AVPacket的data數據直接寫成文件,從而獲得視音頻的碼流文件。
int size:data的大小
int64_t pts:顯示時間戳(num/den)
int64_t dts:解碼時間戳
int stream_index:標識該AVPacket所屬的視頻/音頻流。
讀取幀數據:
int av_read_frame(AVFormatContext * s,AVPacket * pkt)
複製代碼
返回流的下一幀。
此函數返回存儲在文件中的內容,而且不驗證解碼器的有效幀是什麼。它會將存儲在文件中的內容拆分爲幀,併爲每一個調用返回一個。它不會忽略有效幀之間的無效數據,從而爲解碼器提供解碼所需的最大信息。
若是pkt-> buf爲NULL,那麼數據包在下一個av_read_frame()或avformat_close_input()以前是有效的。不然數據包無限期地有效。在這兩種狀況下,數據包必須在再也不須要時使用av_free_packet釋放。對於視頻,數據包剛好包含一幀。對於音頻,若是每一個幀具備已知的固定大小(例如PCM或ADPCM數據),則它包含整數個幀。若是音頻幀具備可變大小(例如MPEG音頻),則它包含一幀。
pkt-> pts,pkt-> dts和pkt->duration始終設置爲以AVStream.time_base單位的正確值。若是視頻格式具備B幀,則pkt-> pts能夠是AV_NOPTS_VALUE,因此若是不解壓縮有效載荷,則最好依賴pkt-> dts。
返回
若是成功返回爲0,錯誤或文件結束時爲 < 0。
設置ffmpeg將流偏移到正確的起始位置:
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags);
複製代碼
s:爲容器內容;
stream_index:流索引
timestamp:將要定位處的時間戳
flags:功能flag
#define AVSEEK_FLAG_BACKWARD 1 ///< seek backward
#define AVSEEK_FLAG_BYTE 2 ///< seeking based on position in bytes
#define AVSEEK_FLAG_ANY 4 ///< seek to any frame, even non-keyframes
#define AVSEEK_FLAG_FRAME 8 ///< seeking based on frame number
複製代碼