FFmpeg学习教程
一、FFmpeg庫介紹
FFmpeg一共包含8個庫:
FFmpeg解碼函數(shù)簡介:
FFmpeg解碼的流程圖如下所示:
SDL2.0顯示YUV的流程圖:
?SDL2.x和SDL1.x顯示流程的區(qū)別如下:
? ?參考博客地址:https://blog.csdn.net/leixiaohua1020/article/details/38868499
| SDL1.x | SDL2.x |
| SDL_SetVideoMode()? | SDL_CreateWindow() |
| SDL_Surface | SDL_Window |
| SDL_CreateYUVOverlay() | SDL_CreateTexture() |
| SDL_Overlay | SDL_Texture |
| ? | ? |
資料參考:
【1】《最簡單的基于FFMPEG+SDL的視頻播放器 ver2 (采用SDL2.0)》https://blog.csdn.net/leixiaohua1020/article/details/38868499
二、FFmpeg數(shù)據(jù)結(jié)構(gòu)詳解:
1、AVFormatContext
? ? ?描述媒體文件或媒體流構(gòu)成和基本信息(包含碼流參數(shù)較多,位于:avformat.h),封裝格式上下文結(jié)構(gòu)體,也是統(tǒng)領(lǐng)全局的結(jié)構(gòu)體,保存了視頻文件封裝格式相關(guān)信息
主要變量:
struct AVInputFormat *iformat:輸入數(shù)據(jù)的封裝格式
AVIOContext *pb:輸入數(shù)據(jù)緩存
unsigned int nb_streams:音視頻流個數(shù)(輸入視頻的AVStream 個數(shù))
AVStream **streams:音視頻流(輸入視頻的AVStream []數(shù)組)
char filename[1024]:文件名
int64_t duration:時長(單位:us)(輸入視頻的時長(以微秒為單位))
int bit_rate:比特率(輸入視頻的碼率)
AVDictionary *metadata:元數(shù)據(jù)
2、AVInputFormat
? ?每種封裝格式(例如FLV, MKV, MP4, AVI)對應(yīng)一個該結(jié)構(gòu)體。
long_name:封裝格式的長名稱
extensions:封裝格式的擴展名
id:封裝格式ID
一些封裝格式處理的接口函數(shù)
3、AVCodecContext:
? ?描述編解碼器上下文的數(shù)據(jù)結(jié)構(gòu),包含編解碼器需要的參數(shù)信息(位于:avcodec.h),編碼器上下文結(jié)構(gòu)體,保存了視頻(音頻)編解碼相關(guān)信息
說明:
codec:編解碼器的AVCodec
width, height:圖像的寬高(只針對視頻)
pix_fmt:像素格式(只針對視頻)
sample_rate:采樣率(只針對音頻)
channels:聲道數(shù)(只針對音頻)
sample_fmt:采樣格式(只針對音頻)
4、AVStream:
? ?描述一個媒體流(存儲視頻/音頻流信息的結(jié)構(gòu)體,位于:avformat.h),視頻文件中每個視頻(音頻)流對應(yīng)一個該結(jié)構(gòu)體
主要變量:
AVCodecContext *codec:視頻/音頻流的AVCodecContext
AVRational time_base:時間基準(zhǔn),真正的時間 =PTS*time_base
int64_t duration:該視頻/音頻流時間長度
AVDictionary *metadata:元數(shù)據(jù)信息
AVRational avg_frame_rate:幀率
AVPacket attached_pic:附加圖片
5、AVCodec
? ?每種視頻(音頻)編解碼器(例如H.264解碼器)對應(yīng)一個該結(jié)構(gòu)體。
name:編解碼器名稱
long_name:編解碼器長名稱
type:編解碼器類型
id:編解碼器ID
一些編解碼的接口函數(shù)
6、AVPacket:
? 存儲一幀壓縮編碼數(shù)據(jù)。
uint8_t *data:壓縮編碼數(shù)據(jù),一個AVPacket的data通常對應(yīng)一個NAL。
int ? size:data的大小
int64_t pts:顯示時間戳
int64_t dts:解碼時間戳
int ? stream_index:標(biāo)識該AVPacket所屬的視頻/音頻流。
7、AVFrame
? ?存儲一幀解碼后像素(采樣)數(shù)據(jù)。
data:解碼后的圖像像素數(shù)據(jù)(音頻采樣數(shù)據(jù))。
linesize:對視頻來說是圖像中一行像素的大小;對音頻來說是整個音頻幀的大小。
width, height:圖像的寬高(只針對視頻)。
key_frame:是否為關(guān)鍵幀(只針對視頻) 。
pict_type:幀類型(只針對視頻) 。例如I,P,B。
三、常用的API接口
1、avformat_open_input
int avformat_open_input(AVFormatContext **ic_ptr,const char *filename,AVInputFormat *fmt,AVDictionary **options);
作用:打開文件或URL,并使基于字節(jié)流的底層輸入模塊得到初始化;解析多媒體文件或多媒體流的頭信息,創(chuàng)建AVFormatContext結(jié)構(gòu)并填充其中的關(guān)鍵字段,依次為各個原始流建立AVStream結(jié)構(gòu)。
參數(shù):
ic_ptr:用于返回avformat_open_input內(nèi)部構(gòu)造的一個AVFormatContext結(jié)構(gòu)體。
filename:指定文件名。
fmt:用于顯式指定輸入文件的格式,如果設(shè)為空則自動判斷其輸入格式。
options:傳入的附加參數(shù)。
? ? ?說明:這個函數(shù)通過解析多媒體文件或流的頭信息及其他輔助數(shù)據(jù),能夠獲取足夠多的關(guān)于文件、流和編解碼器的信息,但任何一種多媒體格式提供的信息都是有限的,而且不同的多媒體軟件制作對頭信息的設(shè)置各有不同,另外這些軟件在產(chǎn)生多媒體內(nèi)容時難免引入錯誤,這種情況下并不能保證獲取到所有需要的信息,這是就要考慮另一個函數(shù):avformat_find_stream_info。
2、avformat_find_stream_info
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
作用:用于獲取必要的編解碼器參數(shù)。需要得到各媒體流對應(yīng)編解碼器的類型和id,這是兩個定義在avutils.h和avcodec.h中的枚舉:
? ? 若媒體格式的數(shù)據(jù)流具有完整頭信息,可以通過avformat_open_input得到編解碼器的類型和id;否則,需要通過avformat_find_stream_info函數(shù)獲取。此外,對于音頻編解碼器,時間基準(zhǔn)、采樣率、聲道數(shù)、位寬、幀長度與視頻編解碼器圖像大小、色彩空間等也需要從avformat_find_stream_info函數(shù)得到。
3、av_read_frame
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
作用:用于從多媒體文件或多媒體流中讀取媒體數(shù)據(jù),數(shù)據(jù)由AVPacket結(jié)構(gòu)pkt來存放。對于音頻數(shù)據(jù),若是固定比特率,則pkt中裝載一個或多個音頻幀;若為可變比特率,則pkt中裝載一個音頻幀。對于視頻數(shù)據(jù),pkt中裝載有一個視頻幀。注:當(dāng)再次調(diào)用本函數(shù)之前,需使用av_free_packet釋放pkt所占用的資源。
4、av_seek_frame
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags);
作用:通過改變媒體文件的讀寫指針來實現(xiàn)對媒體文件的隨機訪問,大多源于媒體播放器的快進(jìn)、快退等功能。
參數(shù):
s:AVFormatContext指針;
avformat_open_input返回得到。
stream_index:指定媒體流。
timestamp:時間標(biāo)簽。
flags:定位方式。
5、av_close_input_file
void av_close_input_file(AVFormatContext *s);
作用:關(guān)閉媒體文件,釋放資源,關(guān)閉物理IO。
6、avcodec_find_decoder
AVCodec *avcodec_find_decoder(enum CodecID id);
AVCodec *avcodec_find_decoder_by_name(const char *name);
作用:根據(jù)指定解碼器ID或者解碼器名稱查找相應(yīng)的解碼器并返回AVCodec 。
7、avcodec_open
int avcodec_open(AVCodecContext *avctx, AVCodec *codec);
作用:根據(jù)輸入的AVCodec指針具體化AVCodecContext結(jié)構(gòu)。在調(diào)用該函數(shù)之前,首先調(diào)用avcodec_alloc_context分配一個AVCodecContext結(jié)構(gòu),或調(diào)用avformat_open_input獲取媒體文件中對應(yīng)媒體流的AVCodecContext結(jié)構(gòu);
此外,通過avcodec_find_decoder獲取AVCodec結(jié)構(gòu)。
8、avcodec_decode_video2
int avcodec_decode_video2(AVCodecContext *avctx,AVFrame *picture,int *got_picture_ptr,AVPacket *avpkt);
作用:解碼視頻幀。
參數(shù):
avctx:解碼器上下文。
picture:輸出數(shù)據(jù)。
got_picture_ptr:指示是否有解碼數(shù)據(jù)輸出。
avpkt:輸入數(shù)據(jù)。
9、avcodec_decode_audio4
int avcodec_decode_audio4(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, AVPacket *avpkt);
作用:解碼音頻幀。輸入數(shù)據(jù)在AVPacket結(jié)構(gòu)中,輸出數(shù)據(jù)在frame中,got_frame_ptr表示是否有數(shù)據(jù)輸出。
參數(shù):
avctx:解碼器上下文。
frame:輸出數(shù)據(jù)。
got_frame_ptr:指示是否有解碼數(shù)據(jù)輸出。
avpkt:輸入數(shù)據(jù)。
10、avcodec_close
int avcodec_close(AVCodecContext *avctx);作用:關(guān)閉解碼器,釋放avcodec_open中分配的資源。
四、代碼流程
? ? 這是來自雷霄驊大神的教程代碼《最簡單的基于FFmpeg的解碼器》
#include <stdio.h> #define __STDC_CONSTANT_MACROS extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" }; int main(int argc, char* argv[]) { AVFormatContext *pFormatCtx; int i, videoindex; AVCodecContext *pCodecCtx; AVCodec *pCodec; AVFrame *pFrame,*pFrameYUV; uint8_t *out_buffer; AVPacket *packet; int y_size; int ret, got_picture; struct SwsContext *img_convert_ctx; //輸入文件路徑 char filepath[]="../video/Titanic.ts"; int frame_cnt; av_register_all();//注冊所有組件 avformat_network_init(); pFormatCtx = avformat_alloc_context(); //打開輸入視頻文件 if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){ printf("Couldn't open input stream.\n"); return -1; } //獲取視頻文件信息 if(avformat_find_stream_info(pFormatCtx,NULL)<0){ printf("Couldn't find stream information.\n"); return -1; } videoindex=-1; for(i=0; i<pFormatCtx->nb_streams; i++) if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){ videoindex=i;//找到視頻的數(shù)組位置 break; } if(videoindex==-1){ printf("Didn't find a video stream.\n"); return -1; } pCodecCtx=pFormatCtx->streams[videoindex]->codec; pCodec=avcodec_find_decoder(pCodecCtx->codec_id);//查找解碼器 if(pCodec==NULL){ printf("Codec not found.\n"); return -1; } if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){//打開解碼器 printf("Could not open codec.\n"); return -1; } /* * 在此處添加輸出視頻信息的代碼 * 取自于pFormatCtx,使用fprintf() */ printf("視頻的時長:%dμs\n", pFormatCtx->duration);//輸入視頻的時長(以微秒為單位) pFrame=av_frame_alloc(); pFrameYUV=av_frame_alloc(); out_buffer=(uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height)); avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); packet=(AVPacket *)av_malloc(sizeof(AVPacket)); //Output Info----------------------------- printf("--------------- File Information ----------------\n"); av_dump_format(pFormatCtx,0,filepath,0); printf("-------------------------------------------------\n"); img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); frame_cnt=0; //從輸入文件讀取一幀壓縮數(shù)據(jù) while(av_read_frame(pFormatCtx, packet)>=0){ if(packet->stream_index==videoindex){ /* * 在此處添加輸出H264碼流的代碼 * 取自于packet,使用fwrite() */ ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);//解碼一幀壓縮數(shù)據(jù) if(ret < 0){ printf("Decode Error.\n"); return -1; } if(got_picture){ sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); printf("Decoded frame index: %d\n",frame_cnt); /* * 在此處添加輸出YUV的代碼 * 取自于pFrameYUV,使用fwrite() */ frame_cnt++; } } av_free_packet(packet); } sws_freeContext(img_convert_ctx); av_frame_free(&pFrameYUV); av_frame_free(&pFrame); avcodec_close(pCodecCtx);//關(guān)閉解碼器 avformat_close_input(&pFormatCtx);//關(guān)閉輸入視頻文件。 return 0; }?
?
總結(jié)
以上是生活随笔為你收集整理的FFmpeg学习教程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: win7美化_Windows 美化资源大
- 下一篇: mike21换成计算机名称,[转载]mi