FFMPEG SDK 開發介紹
FFMPEG SDK 開發介紹
1.簡介:
ffmpeg是一套能夠用來記錄、轉換數字音頻、視頻,並能將其轉化爲流的開源計算機程序。
使用ffmpeg可以完成以下功能:parse,demux,decode,filter(preprocessing),encode,mux,stream和player等.
2.下載和編譯:
下載地址: http://ffmpeg.org/download.html
編譯:
1)windows平臺static library/shared library, 編譯工具:mingw-gcc或者在linux平臺下交叉編譯(推薦)
2)linux平臺static library/shared library, 編譯工具:gcc
模塊:
libavcodec - 編碼解碼器
libavdevice - 輸入輸出設備的支持
libavfilter - 視音頻濾鏡支持
libavformat - 視音頻等格式的解析
libavutil - 工具庫
libpostproc - 後期效果處理
libswscale - 圖像顏色、尺寸轉換
3.SDK介紹和開發(基於ffmpeg 0.8 sdk)
ffmpeg每部分功能都採用plugin的方式,使用統一的接口調用,這樣就可以很是方便的使用和擴展。
plugin分爲幾種:muxer,demuxer,protocol,hwaccel,encoder,decoder,parser,bitstream,filter,...
所以在使用SDK的時候第一步就是註冊plugin
avcodec_register_all() : 註冊 hwaccel,encoder,decoder,parser,bitstream
av_register_all() : 註冊 muxer,demuxer,protocol
avfilter_register_all() : 註冊 濾鏡filter
下面根據不一樣的應用場景,給出主要的代碼示例(僅是代碼片段,不必定能編譯經過):
1)如何獲取媒體文件的信息(Parser):
{
av_register_all();
AVFormatContext * pFormatCtx = NULL;
int err = 0;
const char *fileName = "c:\\test.mp4";
err = av_open_input_file(&pFormatCtx, fileName, NULL, 0, NULL);
if(err != 0)
{
// break ;
}
err = av_find_stream_info(pFormatCtx);
if(err < 0)
{
// break ;
}
for(uint32_t i = 0; i < pFormatCtx->nb_streams; i ++)
{
// stream 結構數據
AVStream *pStream = pFormatCtx->streams[i];
// 幀率信息
AVRational frameRate = pStream->r_frame_rate;
// 時間單位比率
AVRational timeBase = pStream->time_base;
// stream duration
int64_t duration = pStream->duration;
// 獲取Codec數據結構
AVCodecContext *pCodecCtx = pStream->codec;
AVMediaType codecType = pCodecCtx->codec_type;
/*
判斷codec類型:Video/Audio/Subtitle...
AVMEDIA_TYPE_VIDEO,
AVMEDIA_TYPE_AUDIO,
AVMEDIA_TYPE_DATA,
AVMEDIA_TYPE_SUBTITLE,
AVMEDIA_TYPE_ATTACHMENT,
*/
CodecID codecId = pCodecCtx->codec_id;
/*
// video codec
CODEC_ID_H261,
CODEC_ID_H263,
CODEC_ID_RV10,
CODEC_ID_RV20,
// ...
// audio codec
CODEC_ID_MP2= 0x15000,
CODEC_ID_MP3,
CODEC_ID_AAC,
CODEC_ID_AC3,
CODEC_ID_DTS,
// ...
// subtitle codec
CODEC_ID_DVD_SUBTITLE= 0x17000,
CODEC_ID_DVB_SUBTITLE,
CODEC_ID_TEXT, ///< raw UTF-8 text
CODEC_ID_XSUB,
CODEC_ID_SSA,
CODEC_ID_MOV_TEXT,
// ...
*/
if(codecType == AVMEDIA_TYPE_VIDEO)
{
// 獲取Video基本信息
int width = pCodecCtx->width;
int height = pCodecCtx->height;
PixelFormat pixelFormat = pCodecCtx->pix_fmt;
}
else if(codecType == AVMEDIA_TYPE_AUDIO)
{
// 獲取Audio基本信息
int channels = pCodecCtx->channels;
int sample_rate = pCodecCtx->sample_rate;
AVSampleFormat sampleFmt = pCodecCtx->sample_fmt;
}
}
// 釋放
if(pFormatCtx != NULL)
{
av_close_input_file(pFormatCtx);
pFormatCtx = NULL;
}
}
2)讀取sample數據(Read raw sample不解碼)
{
// 參考Parser代碼
// av_register_all();
// AVFormatContext * pFormatCtx = NULL;
// err = av_open_input_file(&pFormatCtx, fileName, NULL, 0, NULL);
AVPacket packet;
av_init_packet(&packet);
int ret = av_read_frame(pFormatCtx, &packet);
if(ret >= 0)
{
int streamIndex = packet.stream_index;
AVStream *pStream = pFormatCtx->streams[streamIndex];
AVCodecContext *pCodecCtx = pStream->codec;
// 計算timestamp
// 轉換時間到1/1000000秒
AVRational time_base;
time_base.num = 1;
time_base.den = 1000000;
// 25.0 1/25, 29.97 1001/30000
// 獲取 dts/pts
const int64_t dts = av_rescale_q(packet.dts, pStream->time_base, time_base);
const int64_t pts = av_rescale_q(packet.pts, pStream->time_base, time_base);
uint8_t *data = packet.data;
int size = packet.size;
bool isKey = ((packet.flags & AV_PKT_FLAG_KEY) == AV_PKT_FLAG_KEY);
}
av_free_packet(&packet);
}
3)解碼sample(Video ES=>YUV/RGB, Audio ES=>PCM)
{
// 參考Parser,Read raw sample代碼
// AVMediaType codecType = pCodecCtx->codec_type;
AVMediaType codecType = AVMEDIA_TYPE_VIDEO;
// CodecId codecId = pCodecCtx->codec_id;
CodecId codecId = CODEC_ID_H264;
// 經過Codec ID查找解碼器
AVCodec *pCodec = avcodec_find_decoder(codecId);
// 分配codec關聯結構
AVCodecContext *pCodecCtx = avcodec_alloc_context();
// 設置一些必要的信息
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO / AVMEDIA_TYPE_AUDIO;
pCodecCtx->codec_id = codecId;
if(pCodec->capabilities & CODEC_CAP_TRUNCATED)
pCodecCtx->flags |= CODEC_FLAG_TRUNCATED;
// 在open codec時要加鎖,不然多個codec同時打開時時會出現錯誤
gMutexFFmpeg.lock();
// 打開Codec
avcodec_open(pCodecCtx, pCodec);
gMutexFFmpeg.unlock();
if(codecType == AVMEDIA_TYPE_VIDEO)
{
AVFrame *pSrcFrame = avcodec_alloc_frame();
AVFrame *pDstFrame = avcodec_alloc_frame();
// 由於內存的緣由,因此須要多分配一些數據, FF_INPUT_BUFFER_PADDING_SIZE
uint8_t *data = ...;
int size = ...;
while(size > 0))
{
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = data;
pkt.size = size;
int frameFinished = 0;
int bytesDecoded = avcodec_decode_video2(pCodecCtx, pSrcFrame, &frameFinished, &pkt);
if(bytesDecoded > 0)
{
data += bytesDecoded;
size -= bytesDecoded;
}
if(frameFinished)
{
int numBytes = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
uint8_t *pBuffer = new uint8_t[numBytes];
avpicture_fill((AVPicture *)pDstFrame, pBuffer, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
av_picture_copy((AVPicture *)pDstFrame, (AVPicture *)pSrcFrame, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
// pBuffer/numBytes/pCodecCtx->pix_fmt : YUV/RGB數據
delete []pBuffer;
}
if(bytesDecoded < 0)
break ;
}
av_free(pSrcFrame);
av_free(pDstFrame);
}
else if(codecType == AVMEDIA_TYPE_AUDIO)
{
// 分配解碼內存空間
uint8_t *pBuffer = new uint8_t[AVCODEC_MAX_AUDIO_FRAME_SIZE];
// 由於內存的緣由,因此須要多分配一些數據, FF_INPUT_BUFFER_PADDING_SIZE
uint8_t *data = ...;
int size = ...;
while(size > 0)
{
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = data;
pkt.size = size;
int outSize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
int bytesDecoded = avcodec_decode_audio3(pCodecCtx, (int16_t *)pBuffer, &outSize, &pkt);
if(bytesDecoded > 0)
{
data += bytesDecoded;
size -= bytesDecoded;
}
if((bytesDecoded >= 0) && (outSize > 0))
{
// pBuffer/outSize : PCM數據
// 格式
// pCodecCtx->channels;
// pCodecCtx->sample_fmt;
// pCodecCtx->sample_rate;
}
}
}
gMutexFFmpeg.lock();
// 關閉和釋放
avcodec_close(pCodecCtx);
gMutexFFmpeg.unlock();
av_free(pCodecCtx);
}
4)視音頻編碼(YUV/RGB=>Video ES, PCM=>Audio ES)
{
// video encode
avcodec_register_all();
// 查找編碼器
AVCodec *avCodec = avcodec_find_encoder((CodecID)mConfig.codec);
AVCodecContext *codecCtx = avcodec_alloc_context();
codecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
codecCtx->codec_id = (CodecID)mConfig.codec;
codecCtx->width = mOutFormat.width;
codecCtx->height = mOutFormat.height;
codecCtx->pix_fmt = (PixelFormat)mOutFormat.pixelFormat;
uint32 num = 0;
uint32 den = 0;
SampleUtil::FPS2Timescale(mOutFormat.frameRate, num, den);
codecCtx->time_base.num = num;
codecCtx->time_base.den = den;
codecCtx->bit_rate = mConfig.bitRate*1000;
codecCtx->max_b_frames = 0;
codecCtx->gop_size = 100;
if(codecCtx->codec_id == CODEC_ID_MPEG1VIDEO)
{
codecCtx->mb_decision = FF_MB_DECISION_RD;
}
else
{
codecCtx->mb_decision = FF_MB_DECISION_RD;
}
avcodec_open(codecCtx, avCodec);
// 分配編碼後的內存,分配爲1MB
mOutputBuffer.resize(1*1024*1024);
AVFrame *pSrcFrame = avcodec_alloc_frame();
avcodec_get_frame_defaults(pSrcFrame);
int ret = avpicture_fill((AVPicture *)pSrcFrame, (uint8_t *)inData.data, (PixelFormat)mOutFormat.pixelFormat, mOutFormat.width, mOutFormat.height);
AVRational time_base;
time_base.num = 1;
time_base.den = 1000000;
pSrcFrame->pts = av_rescale_q(inData.dts, time_base, codecCtx->time_base);
int bytesWritten = avcodec_encode_video(codecCtx, (uint8 *)mOutputBuffer.data(), mOutputBuffer.size(),
isEmpty ? NULL : pSrcFrame);
outData.data = (char *)mOutputBuffer.data();
outData.size = bytesWritten;
outData.isKey = (mCodecCtx->coded_frame->key_frame != 0);
av_free(pSrcFrame);
avcodec_close(codecCtx);
av_free(codecCtx);
// audio encode請看audioencoder.cpp 文件
}
5)圖像格式轉換(YUV/RGB <=> YUV/RGB & Resize)
{
SwsContext *pSwsCtx = NULL;
/*
srcWidth - 源圖像 width
srcHeight - 源圖像 height
srcFmt - 源圖像像素格式 enum PixelFormat
dstWidth - 目標圖像 width
dstHeight - 目標圖像 height
dstFmt - 目標圖像像素格式 enum PixelFormat
*/
// resize 算法
int swsFlags = SWS_LANCZOS; // SWS_FAST_BILINEAR;
// 初始化
pSwsCtx = sws_getCachedContext(NULL, srcWidth, srcHeight, srcFmt,
dstWidth, dstHeight, dstFmt, swsFlags, NULL, NULL, NULL);
// 設置數據到結構 AVPicture
AVPicture avSrcPic;
AVPicture avDstPic;
memset(&avSrcPic, 0, sizeof(avSrcPic));
memset(&avDstPic, 0, sizeof(avDstPic));
int dstRet = avpicture_fill(&avDstPic, (uint8_t *)pDstBuffer, dstFmt, dstWidth, dstHeight);
{
// pSrcBuffer - 源數據
// pDstBuffer - 目標數據
int srcRet = avpicture_fill(&avSrcPic, (uint8_t *)pSrcBuffer, srcFmt, srcWidth, srcHeight);
// 執行轉換
sws_scale(pSwsCtx, avSrcPic.data, avSrcPic.linesize, 0, abs(srcHeight), avDstPic.data, avDstPic.linesize);
}
// 釋放
sws_freeContext(pSwsCtx);
}
6)封裝格式(Muxer, .mp4/.avi/.mkv...)
{
av_register_all();
AVFormatContext * pFormatCtx;
avformat_alloc_output_context2(&pFormatCtx, NULL, "mp4", "c:\\out.mp4");
{
// new video stream
AVStream * avStream = av_new_stream(pFormatCtx, pFormatCtx->nb_streams;
avcodec_get_context_defaults3(avStream->codec, NULL);
AVCodecContext *codecCtx = avStream->codec;
codecCtx->codec_id = (CodecID)format->codecId;
codecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
codecCtx->width = format->width;
codecCtx->height = format->height;
codecCtx->bit_rate = 800000;
uint32 num = 0;
uint32 den = 0;
SampleUtil::FPS2Timescale(format->frameRate, num, den);
codecCtx->time_base.num = num;
codecCtx->time_base.den = den;
av_set_pts_info(streamInfo->avStream, 64, num, den);
if(pFormatCtx->oformat->flags & AVFMT_GLOBALHEADER)
{
codecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
switch(codecCtx->codec_id)
{
case CODEC_ID_H264:
{
AVBitStreamFilterContext * avFilter = av_bitstream_filter_init("h264_mp4toannexb");
}
break ;
case CODEC_ID_AAC:
{
codecCtx->frame_size = 1024;
AVBitStreamFilterContext * avFilter = av_bitstream_filter_init("aac_adtstoasc");
}
break ;
}
// 設置解碼相關數據, 好比H264要設置:SPS & PPS
codecCtx->extradata_size = ;// size;
codecCtx->extradata = ;// (uint8_t *)av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
}
{
// new stream
AVStream * avStream = av_new_stream(pFormatCtx, pFormatCtx->nb_streams;
avcodec_get_context_defaults3(avStream->codec, NULL);
}
err = av_set_parameters(pFormatCtx, NULL);
// 以寫的方式打開文件
err = avio_open(&pFormatCtx->pb, "c:\\out.mp4", AVIO_FLAG_WRITE);
// 寫文件頭信息
err = av_write_header(pFormatCtx);
{
const AVRational in_time_base = { 1, 1000000 };
AVRational out_time_base = avStream->time_base;
AVPacket pkt = { 0 };
av_init_packet(&pkt);
pkt.stream_index = streamId; // 流的id
pkt.data = ;//(uint8_t *)mediaSample->data();
pkt.size = ;//mediaSample->size();
// 轉換dts/pts時間單位 1/1000000=>avStream->time_base
pkt.dts = av_rescale_q(mediaSample->dts(), in_time_base, out_time_base);
pkt.pts = av_rescale_q(mediaSample->pts(), in_time_base, out_time_base);
pkt.flags = mediaSample->isKey() ? AV_PKT_FLAG_KEY : 0;
// 寫入一幀數據
int err = av_interleaved_write_frame(pFormatCtx, &pkt);
av_free_packet(&pkt);
}
// 寫文件尾信息
av_write_trailer(pFormatCtx);
// 釋放
// av_bitstream_filter_close(avFilter);
avio_close(pFormatCtx->pb);
avformat_free_context(pFormatCtx);
}
7)濾鏡filter的使用(crop, resize, deinterlace, drawtext, overlay, vflip, ...)
經過搭建若干個filter能夠對視音頻進行一系列的處理.
a).Simple filtergraphs:
reencode filter graph:
filter graph:
int ret = av_vsink_buffer_get_video_buffer_ref(mBufferDstCtx, &picRef, 0);
request_frame
start_frame
draw_slice
end_frame
b).Complex filtergraphs:
咱們搭建的filter graph:
{
avcodec_register_all();
avfilter_register_all();
AVFilterGraph * pFilterGraph = NULL;
AVFilterContext * pBufferSrcCtx = NULL;
AVFilterContext * pBufferDstCtx = NULL;
AVFrame * pSrcFrame = avcodec_alloc_frame();
AVFrame * pSinkFrame = avcodec_alloc_frame();
AVFrame * pDstFrame = avcodec_alloc_frame();
// 設定輸出格式列表,咱們僅支持PIX_FMT_YUV420P
PixelFormat pix_fmts[] = { PIX_FMT_YUV420P, PIX_FMT_NONE };
char args[512];
AVFilterContext *lastFilterCtx = NULL;
// 咱們使用到的filter,其中"nl_"開頭的是咱們本身寫的filter
// 輸入buffer filter
AVFilter *bufferFilter = avfilter_get_by_name("buffer");
// deinterlace filter, 目前使用yadif filter
AVFilter *yadifFilter = avfilter_get_by_name("yadif");
// 咱們本身實現的fps轉換filter
AVFilter *fpsFilter = avfilter_get_by_name("nl_fps");
// 咱們本身實現的遮logo的filter,支持多個,動態設置,可以設定區間範圍
AVFilter *delogosFilter = avfilter_get_by_name("nl_delogos");
// 咱們本身實現的調節對比度和亮度的filter
AVFilter *colorFilter = avfilter_get_by_name("nl_color");
// 咱們本身實現的疊加圖片的filter,支持多個,動態設置,可以設定區間範圍
AVFilter *overlaysFilter = avfilter_get_by_name("nl_overlays");
// crop filter
AVFilter *cropFilter = avfilter_get_by_name("crop");
// resize filter
AVFilter *resizeFilter = avfilter_get_by_name("scale");
// 圖像擴展filter,能夠在圖像邊界填充特定的顏色
AVFilter *padFilter = avfilter_get_by_name("pad");
// 輸出buffer filter
AVFilter *buffersinkFilter = avfilter_get_by_name("buffersink");
// 建立graph
pFilterGraph = avfilter_graph_alloc();
// 開始建立filter
AVRational tb = { 1, 1000000 };
AVRational sar = { 0, 1 };
// 計算圖像寬度比
av_reduce(&sar.num, &sar.den, mConfig.width, mConfig.height, 1000*1000);
// 設定 buffer filter的參數
// w:h:pixfmt:time_base.num:time_base.den:sample_aspect_ratio.num:sample_aspect_ratio.den:sws_param
sprintf(args, "%d:%d:%d:%d:%d:%d:%d",
mConfig.width, mConfig.height, mConfig.pixelFormat, tb.num, tb.den, sar.num, sar.den);
// input filter
err = avfilter_graph_create_filter(&pBufferSrcCtx, bufferFilter, "in", args, NULL, pFilterGraph);
// 記錄前一個filter context
lastFilterCtx = pBufferSrcCtx;
// 若是須要 deinterlace,則建立 yadif filter,同時和前一個filter進行鏈接
// deinterlace : yadif
if(mConfig.deinterlace > 0)
{
if(yadifFilter == NULL)
break ;
// yadif filter的參數
// mode:parity
sprintf(args, "%d:%d", 0, -1);
// 建立filter,同時加入到graph
AVFilterContext *deinterlaceCtx = NULL;
err = avfilter_graph_create_filter(&deinterlaceCtx, yadifFilter, "yadif", args, NULL, pFilterGraph);
if(err < 0)
break ;
// 和前一個filter進行鏈接
err = avfilter_link(lastFilterCtx, 0, deinterlaceCtx, 0);
if(err < 0)
break ;
lastFilterCtx = deinterlaceCtx;
}
// ... 中間略過
// 建立output filter
err = avfilter_graph_create_filter(&pBufferDstCtx, buffersinkFilter, "out", NULL, pix_fmts, pFilterGraph);
if(err < 0)
break ;
// 和前一個filter進行鏈接
err = avfilter_link(lastFilterCtx, 0, pBufferDstCtx, 0);
if(err < 0)
break ;
// 配置 graph
err = avfilter_graph_config(pFilterGraph, NULL);
// 把輸入frame填充到結構AVFrame
avpicture_fill((AVPicture *)pSrcFrame, (uint8_t *)inMediaSample->data(),
(PixelFormat)mConfig.pixelFormat, mConfig.width, mConfig.height);
pSrcFrame->width = mConfig.width;
pSrcFrame->height = mConfig.height;
pSrcFrame->format = mConfig.pixelFormat;
pSrcFrame->pts = inMediaSample->dts();
// 開始寫input寫入frame
ret = av_vsrc_buffer_add_frame(pBufferSrcCtx, pSrcFrame, AV_VSRC_BUF_FLAG_OVERWRITE);
// 從輸出filter查看輸入是否能夠獲取數據,返回可獲取的數目
int count = avfilter_poll_frame(pBufferDstCtx->inputs[0]);
if(count > 0)
{
AVFilterBufferRef *picRef = NULL;
// 從輸出filter中獲取結果
int ret = av_vsink_buffer_get_video_buffer_ref(pBufferDstCtx, &picRef, 0);
if(picRef != NULL)
{
// 轉換AVFilterBufferRef到AVFrame
avfilter_fill_frame_from_video_buffer_ref(pSinkFrame, picRef);
pSinkFrame->format = picRef->format;
pSinkFrame->width = picRef->video->w;
pSinkFrame->height = picRef->video->h;
const int numBytes = avpicture_get_size((PixelFormat)pSinkFrame->format, pSinkFrame->width, pSinkFrame->height);
// 轉換時間單位
AVRational tb = { 1, 1000000 };
const int64 dts = av_rescale_q(picRef->pts, mBufferDstCtx->inputs[0]->time_base, tb);
// 獲取圖像數據
avpicture_fill((AVPicture *)pDstFrame, (uint8_t *)mediaSample->data(),
(PixelFormat)pSinkFrame->format, pSinkFrame->width, pSinkFrame->height);
av_picture_copy((AVPicture *)pDstFrame, (AVPicture *)pSinkFrame,
(PixelFormat)pSinkFrame->format, pSinkFrame->width, pSinkFrame->height);
// 釋放buffer計數器
avfilter_unref_buffer(picRef);
}
}
}
歡迎關注本站公眾號,獲取更多信息