FFmpeg使用流程

来源:互联网 发布:mac结构图app免费 编辑:程序博客网 时间:2024/05/22 08:12

一、FFMPeg一般流程:

1、av_register_all();//注册所有文件格式和编解码库

2、avformat_network_init();//打开网络视频流

3、av_open_input_file();//读取文件头部把信息保存到AVFormatContext结构体

4、av_find_stream_info();//为pFormatCtx->streams填充上正确的信息

5、CODEC_TYPE_VIDEO;//通过判断得到视频流类型

6、avcodec_find_decoder();//查找解码器

7、avcodec_open();//打开编解码器

8、avcodec_alloc_frame();//分配空间保存帧数据

9、av_read_frame();//不断从流中提取帧数据

10、avcodec_decode_video();//解码视频流

11、avcodec_close();//关闭解码器

12、avformat_close_input_file();//关闭输入文件


二、详解:

1、AVCodecContext:描述编解码器上下文的数据结构,包含编解码器需要的参数信息(位于:avcodec.h

typedef struct AVCodecContext {

     .....
} AVCodecContext;


说明:

单纯使用libavcodec,参数信息需要调用者进行初始化;使用整个FFMPEG库,在调用avformat_open_input和avformat_find_stream_info时根据文件头信息及媒体流内头部信息初始化。


2、AVStream:描述一个媒体流存储视频/音频流信息的结构体,位于:avformat.h
typedef struct AVStream {
    ......
} AVStream;


主要变量:

int index:标识视频/音频流

AVCodecContext *codec:视频/音频流的AVCodecContext

AVRational time_base:时间基准,真正的时间 =PTS*time_base

int64_t duration:该视频/音频流时间长度

AVDictionary *metadata:元数据信息

AVRational avg_frame_rate:帧率

AVPacket attached_pic:附加图片


3、AVFormatContext:描述媒体文件或媒体流构成和基本信息包含码流参数较多,位于:avformat.h
typedef struct AVFormatContext {
......
} AVFormatContext;


AVFormatContext是一个很重要的数据结构,很多函数会使用它作为参数;是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体。

主要变量:

struct AVInputFormat *iformat:输入数据的封装格式

AVIOContext *pb:输入数据缓存

unsigned int nb_streams:音视频流个数

AVStream **streams:音视频流

char filename[1024]:文件名

int64_t duration:时长(单位:us)

int bit_rate:比特率

AVDictionary *metadata:元数据



4、AVPacket存储压缩编码数据相关信息的结构体(位于:avformat.h

typedef struct AVPacket {
   ......          
} AVPacket;


主要变量:

uint8_t *data:压缩编码数据,个AVPacket的data通常对应一个NAL。

int   size:data的大小

int64_t pts:显示时间戳

int64_t dts:解码时间戳

int   stream_index:标识该AVPacket所属的视频/音频流。


三、API
1、int avformat_open_input(AVFormatContext **ic_ptr,const char *filename,AVInputFormat *fmt,AVDictionary **options);
作用:打开文件或URL,并使基于字节流的底层输入模块得到初始化;解析多媒体文件或多媒体流的头信息,创建AVFormatContext结构并填充其中的关键字段,依次为各个原始流建立AVStream结构。

参数:
ic_ptr:用于返回avformat_open_input内部构造的一个AVFormatContext结构体。
filename:指定文件名。
fmt:用于显式指定输入文件的格式,如果设为空则自动判断其输入格式。
options:传入的附加参数。


说明:这个函数通过解析多媒体文件或流的头信息及其他辅助数据,能够获取足够多的关于文件、流和编解码器的信息,但任何一种多媒体格式提供的信息都是有限的,而且不同的多媒体软件制作对头信息的设置各有不同,另外这些软件在产生多媒体内容时难免引入错误,这种情况下并不保证获取到所有需要的信息,这是就要考虑另一个函数:avformat_find_stream_info。


2、int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);

作用:用于获取必要的编解码器参数。需要得到各媒体流对应编解码器的类型和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,
     ...

};


若媒体格式的数据流具有完整头信息,可以通过avformat_open_input得到编解码器的类型和id;否则,需要通过avformat_find_stream_info函数获取。此外,对于音频编解码器,时间基准、采样率、声道数、位宽、帧长度与视频编解码器图像大小、色彩空间等也需要从avformat_find_stream_info函数得到。

3、int av_read_frame(AVFormatContext *s, AVPacket *pkt);

作用:用于从多媒体文件或多媒体流中读取媒体数据,数据由AVPacket结构pkt来存放。对于音频数据,若是固定比特率,则pkt中装载一个或多个音频帧;若为可变比特率,则pkt中装载一个音频帧。对于视频数据,pkt中装载有一个视频帧。注:当再次调用本函数之前,需使用av_free_packet释放pkt所占用的资源。


4、int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags);

作用:通过改变媒体文件的读写指针来实现对媒体文件的随机访问,大多源于媒体播放器的快进、快退等功能。

参数:
s:AVFormatContext指针,avformat_open_input返回得到。
stream_index:指定媒体流。
timestamp:时间标签。。
flags:定位方式。


5、void av_close_input_file(AVFormatContext *s);
作用:关闭媒体文件,释放资源,关闭物理IO。


6、AVCodec *avcodec_find_decoder(enum CodecID id);
   AVCodec *avcodec_find_decoder_by_name(const char *name);
作用:根据指定解码器ID或者解码器名称查找相应的解码器并返回AVCodec *


7、int avcodec_open(AVCodecContext *avctx, AVCodec *codec);
作用:根据输入的AVCodec指针具体化AVCodecContext结构。在调用该函数之前,首先调用avcodec_alloc_context分配一个AVCodecContext结构,或调用avformat_open_input获取媒体文件中对应媒体流的AVCodecContext结构;此外,通过avcodec_find_decoder获取AVCodec结构。


8、int avcodec_decode_video2(AVCodecContext *avctx,AVFrame *picture,int *got_picture_ptr,AVPacket *avpkt);

作用:解码视频帧。

参数:

avctx:解码器上下文。

picture:输出数据。

got_picture_ptr:指示是否有解码数据输出。

avpkt:输入数据。


9、int avcodec_decode_audio4(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, AVPacket *avpkt);

作用:解码音频帧。输入数据在AVPacket结构中,输出数据在frame中,got_frame_ptr表示是否有数据输出。

参数:

avctx:解码器上下文。

frame:输出数据。

got_frame_ptr:指示是否有解码数据输出。

avpkt:输入数据。


10、int avcodec_close(AVCodecContext *avctx);
作用:关闭解码器,释放avcodec_open中分配的资源。
        

三、大体代码流程

int main(int argc, char **argv){    AVFormatContext* pCtx = NULL;    AVCodecContext *pCodecCtx = NULL;    AVCodec *pCodec = NULL;    AVPacket packet;    AVFrame *pFrame = NULL;    FILE *fpo1 = NULL;    FILE *fpo2 = NULL;    int nframe;    int err;    int got_picture;    int picwidth, picheight, linesize;    unsigned char *pBuf;    int i;    int64_t timestamp;    struct options opt;    int usefo = 0;    struct audio_dsp dsp;    int dusecs;    float usecs1 = 0;    float usecs2 = 0;    struct timeval elapsed1, elapsed2;    int decoded = 0;    av_register_all();    av_log_set_callback(log_callback);    av_log_set_level(50);    err = avformat_open_input(&pCtx, opt.finput, 0, 0);    if (err < 0){        printf("\n->(avformat_open_input)\tERROR:\t%d\n", err);        goto fail;    }    err = avformat_find_stream_info(pCtx, 0);    if (err < 0){        printf("\n->(avformat_find_stream_info)\tERROR:\t%d\n", err);        goto fail;    }    if (opt.streamId < 0){        av_dump_format(pCtx, 0, pCtx->filename, 0);        goto fail;    }else{        for (i = 0; i < pCtx->streams[opt.streamId]->codec->extradata_size; i++){            if (i%16 == 0) printf("\n");            printf("%2x ", pCtx->streams[opt.streamId]->codec->extradata[i]);        }    }    / *Try to open output file*/    if (strlen(opt.foutput1) && strlen(opt.foutput2))    {        fpo1 = fopen(opt.foutput1, "wb");        fpo2 = fopen(opt.foutput2, "wb");        if (!fpo1 || !fpo2){            printf("\n->error Open output file.\n");            goto fail;        }        usefo = 1;    }else{        usefo = 0;    }    if (opt.streamId >= pCtx->nb_streams){        printf("\n->StreamId\tERROR\n");        goto fail;    }    if (opt.lstart > 0){        err = av_seek_frame(pCtx, opt.streamId, opt.lstart, AVSEEK_FLAG_ANY);        if (err < 0)        {            printf("\n->(av_seek_frame)\tERROR:\t%d\n", err);            goto fail;        }    }   / *Config decoder*/    if (!opt.nodec){        /* prepare codec */        pCodecCtx = pCtx->streams[opt.streamId]->codec;         if (opt.thread_count <= 16 && opt.thread_count > 0 ){            pCodecCtx->thread_count = opt.thread_count;            pCodecCtx->thread_type = FF_THREAD_FRAME;        }        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);        if (!pCodec){            printf("\n->Can't find decoder!\n");            goto fail;        }        err = avcodec_open2(pCodecCtx, pCodec, 0);        if (err < 0){            printf("\n->(avcodec_open)\tERROR:\t%d\n", err);            goto fail;        }        pFrame = avcodec_alloc_frame();         / *Ready audio device* /        if (opt.bplay){           / *Audio device* /            dsp.audio_fd = open(OSS_DEVICE, O_WRONLY);            if (dsp.audio_fd == -1){                printf("\n-> Can't find audio device\n");                goto fail;            }            dsp.channels = pCodecCtx->channels;            dsp.speed = pCodecCtx->sample_rate;            dsp.format = map_formats(pCodecCtx->sample_fmt);            if (set_audio(&dsp) < 0){                printf("\n-> Can't set audio device\n");                goto fail;            }        }    }    nframe = 0;    while(nframe < opt.frames || opt.frames == -1)    {        gettimeofday(&elapsed1, NULL);        err = av_read_frame(pCtx, &packet);        if (err < 0){            printf("\n->(av_read_frame)\tERROR:\t%d\n", err);            break;        }    }    if (!opt.nodec && pCodecCtx)    {        avcodec_close(pCodecCtx);    } fail:    if (pCtx){        avformat_close_input(&pCtx);    }    if (fpo1){        fclose(fpo1);    }    if (fpo2){        fclose(fpo2);    }    if (!pFrame){        av_free(pFrame);    }    if (!usefo && (dsp.audio_fd != -1)){        close(dsp.audio_fd);    }    return 0;}


0 0