檔案參考
http://www.diybl.com/course/6_system/linux/Linuxjs/2008924/145267.html


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


影片讀取範例
--------------------
#!cpp
#include <stdio.h>;
#include <string.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>

int main(int argc, const char *argv[])
{
    const char *fname;
    AVFormatContext *ic;
    AVStream *is, *tis;
    AVCodec *codec;
    AVFormatParameters params, *ap = &params;
    AVPacket pkt;
    AVFrame frame;
    AVPicture *pic;
    int got_picture;
    int i, r;
    av_register_all();
    if( argc != 2 ) {
        fprintf(stderr, "Usage: %s <media file>\n", argv[0]);
        return 1;
    }

    fname = argv[1];
    memset(ap, 0, sizeof(AVFormatParameters));
    ap->video_codec_id = CODEC_ID_NONE;
    printf("codec id %X\n", ap->video_codec_id);
    r = av_open_input_file(&ic, fname, NULL, 0, ap);
    if( r != 0  || ic == NULL) {
        fprintf(stderr, "can not open input file %s\n", fname);
        return 1;
    }

    av_find_stream_info(ic);
    is = NULL;

    for(i = 0; i < ic->nb_streams; i++) {
        tis = ic->streams[i];
        if( tis->codec->codec_type == CODEC_TYPE_VIDEO ) {
            printf("channel %d of %d\n", i, ic->nb_streams);
            is = tis;
        }
    }

    codec = avcodec_find_decoder(is->codec->codec_id);

    if( codec == NULL ) {
        fprintf(stderr, "can not find codec %s\n", is->codec->codec_name);
        return 1;
    }

    r = avcodec_open(is->codec, codec);

    if( r != 0 ) {
        fprintf(stderr, "can not initialize a AVCodecContext for codec %s\n", codec->name);
        return 1;
    }

    printf("Codec %s (%d x %d)\n", codec->name, is->codec->width, is->codec->height);

    for(i = 0; i < 10;) {
        r = av_read_frame(ic, &pkt);
        if( r != 0 ) {
            fprintf(stderr, "no more frame\n");
            return 1;
        }

        if( pkt.stream_index != is->index ) {
            continue;
        }
        if( pkt.pts != AV_NOPTS_VALUE )
            printf("Frame %d@%d: pts=%lld, dts=%lld, size=%d, data=%x\n", i, pkt.stream_index, pkt.pts, pkt.dts, pkt.size, pkt.data);
        } else {
            printf("Frame %d@%d: pts=N/A, dts=%lld, size=%d, data=%x\n", i, pkt.stream_index, pkt.dts, pkt.size, pkt.data);
        }
        av_pkt_dump(stdout, &pkt, 0);
        avcodec_get_frame_defaults(&frame);
        r = avcodec_decode_video(is->codec, &frame, &got_picture, pkt.data, pkt.size);

        if( r < 0 ) {
            printf("decoding error\n");
            return 1;
        }

        if( got_picture ) {
            printf("\tlinesize[4]={%d %d %d %d}, data[4]={%x %x %x %x)}\n",
            frame.linesize[0], frame.linesize[1],
            frame.linesize[2], frame.linesize[3],
            frame.data[0], frame.data[1],
            frame.data[2], frame.data[3]);
        }

        av_free_packet(&pkt);
        i++;
    }

    avcodec_close(is->codec);
    return 0;
}

compile it

#!raw

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

arrow
arrow
    全站熱搜

    BB 發表在 痞客邦 留言(0) 人氣()