日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

FFmpeg学习教程

發(fā)布時間:2023/12/31 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 FFmpeg学习教程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、FFmpeg庫介紹

FFmpeg一共包含8個庫:

  • avcodec:編解碼(最重要的庫)。
  • avformat:封裝格式處理。
  • avfilter:濾鏡特效處理。
  • avdevice:各種設(shè)備的輸入輸出。
  • avutil:工具庫(大部分庫都需要這個庫的支持)。
  • postproc:后加工。
  • swresample:音頻采樣數(shù)據(jù)格式轉(zhuǎn)換。
  • swscale:視頻像素數(shù)據(jù)格式轉(zhuǎn)換
  • FFmpeg解碼函數(shù)簡介:

  • av_register_all();//注冊所有文件格式和編解碼庫
  • avformat_network_init();//打開網(wǎng)絡(luò)視頻流
  • av_open_input_file();//讀取文件頭部把信息保存到AVFormatContext結(jié)構(gòu)體
  • av_find_stream_info();//為pFormatCtx->streams填充上正確的信息
  • CODEC_TYPE_VIDEO;//通過判斷得到視頻流類型
  • avcodec_find_decoder();//查找解碼器
  • avcodec_open();//打開編解碼器
  • avcodec_alloc_frame();//分配空間保存幀數(shù)據(jù)
  • av_read_frame();//不斷從流中提取幀數(shù)據(jù)
  • avcodec_decode_video();//解碼視頻流
  • avcodec_close();//關(guān)閉解碼器
  • avformat_close_input_file();//關(guān)閉輸入文件
  • FFmpeg解碼的流程圖如下所示:

    SDL2.0顯示YUV的流程圖:

    ?SDL2.x和SDL1.x顯示流程的區(qū)別如下:

    ? ?參考博客地址:https://blog.csdn.net/leixiaohua1020/article/details/38868499

    SDL1.xSDL2.x
    SDL_SetVideoMode()?SDL_CreateWindow()
    SDL_SurfaceSDL_Window
    SDL_CreateYUVOverlay()SDL_CreateTexture()
    SDL_OverlaySDL_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中的枚舉:

    enum AVMediaType {AVMEDIA_TYPE_UNKNOWN = -1,AVMEDIA_TYPE_VIDEO,AVMEDIA_TYPE_AUDIO,AVMEDIA_TYPE_DATA,AVMEDIA_TYPE_SUBTITLE,AVMEDIA_TYPE_ATTACHMENT,AVMEDIA_TYPE_NB }; enum CodecID {CODEC_ID_NONE,CODEC_ID_MPEG1VIDEO,CODEC_ID_MPEG2VIDEO,CODEC_ID_MPEG2VIDEO_XVMC,CODEC_ID_H261,CODEC_ID_H263, CODEC_ID_H264,... };

    ? ? 若媒體格式的數(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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。