FFmpeg 使用筆記

FFmpeg 是一個 open source 的影音媒體編碼/解碼軟體和程式庫,實作各式各樣格式的 codec,包括常的 AVI 、 WMV 、 WMA ...... 等等。

 

AVInputFormat/AVOutputFormat

multimedia 檔案的 Muxer/Demuxer ,將多個 stream;如 auodi 、video 等,以單個媒體承載。媒體可以是檔案、 network session 等等。每一個 AVFormat 代表一種格式,像是 .avi 、 .wmv 、 .dat 等等各種格式,使用不同的方式 mux/demux 多個 stream。每一 AVFormat 都實作特定的格式,使用時必需選擇和目的格式相符的 AVFormat 實作,才能正確的讀取 stream 的內容。

 

AVFormatContext

AVFormatContext 是 AVFormat 的一個 instance ,用以存放 AVFormat 的執行狀態。使用 FFmpeg 處理檔案時,必需為 AVInputFormat/AVOutputFormat 建立一個 AVFormatContext 。同一個 format 可以建立多個 AVFormatContext ,各立獨立,不相互干擾,以同時處理多個檔案。

 

AVStream

AVStream 用以對應到 AVFormatContext 裡的一個 stream 。因此同一個 AVFormatContext 管理了多個 AVStream ,以對應到存在檔案或透過 network session 傳送的數個 stream。

 

AVPacket

在 stream 裡的資料,是以 packet 為單位進行傳送/儲存。因此,一般多媒體檔,是將檔案當成 network session 一般處理。每一個 AVPacket 標注了所屬的 stream ,透過 AVPacket 的包裝,多個 stream 可以同時在一個媒介上傳送/儲存。因此,我們可以讀 stream 是由一系列的 packet 所組成。

 

AVCodec

一個影音媒體檔裡,可以承載多個 stream。而 stream 內有 packet。 packet 的 payload ,也就是資料,是透過 codec 編碼/壓縮的。因此,必需透過 codec 進行讀取/寫入時的 decode/encode。AVCodec 就是實作 codec 演算法。同樣的,codec 的演算法有各式各樣。使用時,必需選正確的 AVCodec ,才能正確的 decode/encode。

 

AVCodecContext

AVCodecContext 是 AVCodec 的一個 instance ,用以存放 AVCodec 的執行狀態。同樣可以建立多個 AVCodecContext ,同時處理多個相同格式、不同格式的 stream 。

 

AVFrame

AVFrame 的頭幾個欄位和 AVPicture 相同,也就是 AVPicture 為 AVFrame 的一小部分。

 

寫入多媒體檔

步驟

  • select an AVFormat
  • allocate a AVFormatContext
  • initialize AVFormatContext with selected AVFormat
  • create and initialize an AVStream with SVGFormatContext and codec_id specified by AVFormat
    • AVStream::codec is AVCodecContext
  • initialize AVCodecContext with the AVCodec specified by codec_id
    • avcodec_open()
  • initialize output stream of AVFormatContext
  • av_write_header()
  • write frames
    • create an AVFrame
    • initialize an AVPacket with AVFrame
    • av_write_frame()
  • close streams
  • av_write_trailer()
  • free streams
  • close output stream
  • free AVFromatContext

 

讀取多媒體檔

產生檔案時,目的格式是由我們所選擇,因此問題往往較單純一點。但讀取檔案時,我們不確定檔案的格式,必需進行格式判斷後,才確定使用哪一個 AVInputFormat 。

 

  • av_open_input_file()
    • Read probe buffer
      • get_buffer()
    • av_probe_input_format() or av_probe_input_format2()
      • return AVFormat
    • Allocate AVFormatContext
    • av_open_input_stream()
  • av_find_stream_info
  • setup codec for streams
    • avcodec_find_decoder()
    • avcodec_open()
  • 讀取 frames
    • av_read_frame()
    • prepare an AVFrame
      • avcodec_get_frame_defaults()
    • decode the frame in pkt to AVFrame
      • avcodec_decode_video()
      • got_picture is true if a picture being returned by the AVFrame.
  • close codec

 

影片讀取範例

 

001	#include <stdio.h>;
002	#include <string.h>
003	#include <libavformat/avformat.h>
004	#include <libavcodec/avcodec.h>
005	    
006	int
007	main(int argc, const char *argv[]) {
008	    const char *fname;
009	    AVFormatContext *ic;
010	    AVStream *is, *tis;
011	    AVCodec *codec;
012	    AVFormatParameters params, *ap = &params;
013	    AVPacket pkt;
014	    AVFrame frame;
015	    AVPicture *pic;
016	    int got_picture;
017	    int i, r;
018	    
019	    av_register_all();
020	
021	    if(argc != 2) {
022	        fprintf(stderr, "Usage: %s <media file>\n", argv[0]);
023	        return 1;
024	    }
025	    
026	    fname = argv[1];
027	         
028	    memset(ap, 0, sizeof(AVFormatParameters));
029	    ap->video_codec_id = CODEC_ID_NONE;
030	    printf("codec id %X\n", ap->video_codec_id);
031	    
032	    r = av_open_input_file(&ic, fname, NULL, 0, ap);
033	    if(r != 0 || ic == NULL) {
034	        fprintf(stderr, "can not open input file %s\n", fname);
035	        return 1;
036	    }
037	    
038	    av_find_stream_info(ic);
039	    
040	    is = NULL;
041	    for(i = 0; i < ic->nb_streams; i++) {
042	        tis = ic->streams[i];
043	        if(tis->codec->codec_type == CODEC_TYPE_VIDEO) {
044	            printf("channel %d of %d\n", i, ic->nb_streams);
045	            is = tis;
046	        }
047	    }
048	
049	    codec = avcodec_find_decoder(is->codec->codec_id);
050	    if(codec == NULL) {
051	        fprintf(stderr, "can not find codec %s\n", is->codec->codec_name);
052	        return 1;
053	    }
054	
055	    r = avcodec_open(is->codec, codec);
056	    if(r != 0) {
057	        fprintf(stderr, "can not initialize a AVCodecContext for codec %s\n",
058	                codec->name);
059	        return 1;
060	    }
061	    printf("Codec %s (%d x %d)\n", codec->name, is->codec->width,
062	           is->codec->height);
063	
064	    for(i = 0; i < 10;) {
065	        r = av_read_frame(ic, &pkt);
066	        if(r != 0) {
067	            fprintf(stderr, "no more frame\n");
068	            return 1;
069	        }
070	
071	        if(pkt.stream_index != is->index)
072	            continue;
073	
074	        if(pkt.pts != AV_NOPTS_VALUE)
075	            printf("Frame %d@%d: pts=%lld, dts=%lld, size=%d, data=%x\n",
076	                   i, pkt.stream_index, pkt.pts, pkt.dts, pkt.size, pkt.data);
077	        else
078	            printf("Frame %d@%d: pts=N/A, dts=%lld, size=%d, data=%x\n",
079	                   i, pkt.stream_index, pkt.dts, pkt.size, pkt.data);
080	        av_pkt_dump(stdout, &pkt, 0);
081	
082	        avcodec_get_frame_defaults(&frame);
083	        r = avcodec_decode_video(is->codec, &frame, &got_picture,
084	                             pkt.data, pkt.size);
085	        if(r < 0) {
086	            printf("decoding error\n");
087	            return 1;
088	        }
089	        if(got_picture) {
090	            printf("\tlinesize[4]={%d %d %d %d}, data[4]={%x %x %x %x)}\n",
091	                   frame.linesize[0], frame.linesize[1],
092	                   frame.linesize[2], frame.linesize[3],
093	                   frame.data[0], frame.data[1],
094	                   frame.data[2], frame.data[3]);
095	        }
096	
097	        av_free_packet(&pkt);
098	        i++;
099	    }
100	
101	    avcodec_close(is->codec);
102	
103	    return 0;
104	}

compile it

gcc -o mminfo mminfo.c -I/usr/local/include -lavformat -lavcodec -L/usr/local/lib

FFMPEG中结构体很多。最关键的结构体可以分成以下几类:

 

a)        解协议(http,rtsp,rtmp,mms)

AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”)

b)        解封装(flv,avi,rmvb,mp4)

AVFormatContext主要存储视音频封装格式中包含的信息;AVInputFormat存储输入视音频使用的封装格式。每种视音频封装格式都对应一个AVInputFormat 结构。

c)        解码(h264,mpeg2,aac,mp3)

每个AVStream存储一个视频/音频流的相关数据;每个AVStream对应一个AVCodecContext,存储该视频/音频流使用解码方式的相关数据;每个AVCodecContext中对应一个AVCodec,包含该视频/音频对应的解码器。每种解码器都对应一个AVCodec结构。

d) 存数据

视频的话,每个结构一般是存一帧;音频可能有好几帧

解码前数据:AVPacket

解码后数据:AVFrame

 

他们之间的对应关系如下所示:

 

http://blog.csdn.net/leixiaohua1020/article/details/11693997

posted @ 2017-08-09 19:26  爱吃土豆的男孩  阅读(331)  评论(0编辑  收藏  举报