FFMPEG SDK开发手册

ffmpeg 编译安装 和SDL简单的视频播放器实现 一、源码获取 首先从下面地址先获取ffmpeg最新版本源码,如果没有git命令,则需要先安装git apt-get install git git clone https://git.ffmpeg.org/ffmpeg.git 源码下载完成后,进入ffmpeg目录,执行如下脚本 ./mk.sh #!/bin/bash ./configure \ --prefix=/usr/local/ffmpeg \ --enable-gpl \ --enable-nonfree \ --enable-.. 阅读详情

FFMpeg 中比较重要的函数以及数据结构如下:
1. 数据结构:
(1) AVFormatContext
(2) AVOutputFormat
(3) AVInputFormat
(4) AVCodecContext
(5) AVCodec
(6) AVFrame
(7) AVPacket
(8) AVPicture
(9) AVStream

2. 初始化函数:
(1) av_register_all()
(2) avcodec_open()
(3) avcodec_close()
(4) av_open_input_file()
(5) av_find_input_format()
(6) av_find_stream_info()
(7) av_close_input_file()

3. 音视频编解码函数:
(1) avcodec_find_decoder()
(2) avcodec_alloc_frame()
(3) avpicture_get_size()
(4) avpicture_fill()
(5) img_convert()
(6) avcodec_alloc_context()
(7) avcodec_decode_video()
(8) av_free_packet()
(9) av_free()

4. 文件操作:
(1) avnew_steam()
(2) av_read_frame()
(3) av_write_frame()
(4) dump_format()

5. 其他函数:
(1) avpicture_deinterlace()
(2) ImgReSampleContext()

以下就根据,以上数据结构及函数在ffmpeg测试代码output_example.c中出现的前后顺进行分析。在此之前还是先谈一下ffmpeg的编译问题。
在linux下的编译比较简单,这里不多说了。在windows下的编译可以参考以下网页:http://bbs.chinavideo.org/viewthread.php?tid=1897&extra=page%3D1。值得一提的是,在使用编译后的sdk进行测试时(用到ffmpeg目录下的output_example.c)编译过程中可能会有以下两个问题:
1. Output_example.c用到了snprintf.h这个头文件。然而这个头文件在win下和linux下有所不同。具体在win下可以用以下方法解决http://www.ijs.si/software/snprintf/
2. 如果使用vc6,或是vc6的命令行进行编译,inline可能不认。错误会出现在common.h文件中,可以在common.h中加入
#ifdef _MSC_VAR
#define inline __inline
#endif

交待完毕进入正题。
一.FFMpeg 中的数据结构:
I. AVFormatContext
    一般在使用ffmpeg sdk的代码中AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。FFmpeg代码中对这个数据结构的注释是:format I/O context此结构包含了一个视频流的格式内容。其中存有了AVInputFormat(or AVOutputFormat同一时间AVFormatContext内只能存在其中一个),和AVStream、AVPacket这几个重要的数据结构以及一些其他的相关信息,比如title,author,copyright等。还有一些可能在编解码中会用到的信息,诸如:duration, file_size, bit_rate等。参考avformat.h头文件。

Useage:声明:AVFormatContext *oc; (1)
初始化: 由于AVFormatConext结构包含许多信息因此初始化过程是分步完成,而且有些变量如果没有值可用,也可不初始化。但是由于一般声明都是用指针因此一个分配内存过程不可少:oc = av_alloc_format_context(); (2)

    结构中的AVInputFormat*(或AVOutputFormat*)是一定要初始化的,基本上这是编译码要使用什么codec的依据所在:oc->oformat = fmt; or oc->iformat = fmt; (3)
其中AVOutputFormat* fmt或AVInputFormat* fmt。(AVInputFormat and AVOutputFormat的初始化在后面介绍。

     随后在参考代码output_example.c中有一行:snprintf(oc-filename, sizeof(oc->filename), “%s”, filename); (4)
还不是十分清楚有什么作用,估计是先要在输出文件中写一些头信息。

    在完成以上步骤後,(初始化完毕AVInputFormat*(或AVOutputFormat*)以及AVFormatContext)接下来就是要利用oc初始化本节开始讲到的AVFormatContext中的第二个重要结构。AVStream(假设已经有了声明AVStream *video_st。参考代码中用了一个函数来完成初始化,当然也可以在主函数中做,传递进函数的参数是oc 和fmt->video_codec(这个在下一节介绍(29)):vdeo_st = add_video_stream(oc, fmt->video_codec); (5)

此函数会在后面讲到AVStream结构时分析。AVFormatContext最后的一个设置工作是:if( av_set_paramters(oc,NULL) b_streams; i++){

av_freep(&oc->streams->codec); (13)

av_freep(&oc->streams); (14)

}
//close the ouput file

if(!(fmt->flags & AVFMT_NOFILE)){

url_fclose(&oc->pb); (15)

}
av_free(oc); (16)

通过以上的一串代码,就可以清晰地看出AVFormatContex* oc和AVStream* video_st是在使用ffmpeg SDK开发时贯穿始终的两个数据结构。以下,简要介绍一下三个标为红色的函数,他们是参考代码output_example.c开发者自行定义的函数。这样可以使整个代码结构清晰,当然你在使用ffmpeg SDK时也可以在主函数中完成对应的功能。在后面我们会专门针对这三个函数做分析。

1. open_video(oc, video_st);
此函数主要是对视频编码器(或解码器)的初始化过程。初始化的数据结构为AVCodec* codec和AVCodecContext* c包括用到了的SDK函数有:
c = st->codec;
codec = avcodec_find_encoder(c->codec_id); //编码时,找编码器 (17)
codec = avcodec_find_decoder(c->codec_id); //解码时,找解码器 (18)
AVCodecContex是结构AVStream中的一个数据结构,因此在AVStream初始化後(5)直接复值给c。

// internal open video codec

avcodec_open(c,codec); (19)

// allocate video stream buffer


// AVFrame *picture

// uint8_t *video_outbuf

video_outbuf_size=200000;
video_outbuf = av_maloc(video_outbuf_size); (20)

// allocate video frame buffer

picture = alloc_picture(c->pix_fmt, c->width, c->height); (21)

上述三步比较容易理解,打开视频编解码codec、分配输出流缓存大小、分配每一帧图像缓存大小。其中AVFrame也是ffmpeg中主要数据结构之一。这一步(8)是对编解码器的初始化过程。

2. write_video_frame(AVFormatContext *oc, AVStream *st)
这个函数中做了真正的编解码工作,其中的函数比较复杂先列出来慢慢分析。
用到的数据结构有AVCodecContext *c, SwsContext *img_convert_ctx。其中SwsContext是用来变换图像格式的。比如yuv422变到yuv420等,当然也用到函数,见下面列表。

fill_yuv_image(tmp_picture, frame_count, c->width, c->height); (22)

sws_scale(img_convert_ctx, tmp_picture->, tmp_picture->linesize,

0, c->height, picture->data, picture->linesize); (23)

img_convert_ctx = sws_getContxt(c->width, c->height, PIX_FMT_YUV420P, (24)

c->width, c->heigth, c->pix_fmt, sws_flags, NULL, NULL, NULL);

由于参考代码中做的是一个编码。因此,它总是要求编码器输入的是yuv文件,而且是yuv420格式的。就会有了以上一些处理过程。接下来调用编码器编码,数据规则化(打包)用到AVPacket,这也是ffmpeg中一个比较不好理解的地方。

out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture); (25)
AVPacket pkt;

av_init_packet(&pkt); (26)

//……handle pkt process, we will analyze later

ret = av_write_frame(oc, &pkt); (27)

有encode就一定会有decode。而且ffmpeg专为解码而生,但是为什么在参考代码中只用了encoder呢?个人猜想是因为encode只是用yuv420来编码,这样的yuv420生成比较容易,要是用到解码的化,还要在代码中附带一个其他格式的音视频文件。在源代码libavcodec文件夹中有一个apiexample.c的参考代码,其中就做了编解码。有空的化我会分析一下。

3. close_video(AVFormatContext *oc, AVStream *st)
avcodec_close(st->codec);
av_free(picture->data[0]);
av_free(picture);
av_free(video_outbuf);
比较容易理解,不多说了。

以上一大段虽然名为介绍AVFormatContext。但基本上把ouput_example.c的视频编码部分的框架走了一遍,其一是想说明结构AVFormatContext的重要性,另一方面也是希望对使用FFMpeg SDK开发者有一个大致的框架。

其实,真正的一些编码函数,内存分配函数在SDK中都已经封装好了,只要搞清楚结构就能用了。而开发者要做的就是一些初始化的过程,基本上就是针对数据结构1的初始化。

II. AVOutputFormat

虽然简单(初始化)但是十分重要,他是编解码器将要使用哪个codec的“指示”。在其成员数据中最重要的就是关于视频codec的了:enum CodecID video_codec;

AVOutputFormat *fmt;

fmt = guess_format(NULL, filename, NULL); (28)

根据filename来判断文件格式,同时也初始化了用什么编码器。当然,如果是用AVInputFormat *fmt的化,就是fix用什么解码器。(指定输出序列->fix编码器,指定输入序列->fix解码器?)

III. AVStream

AVStream作为继AVFormatContext後第二个贯穿始终的结构是有其理由的。他的成员数据中有AVCodecContext这基本的上是对所使用的Video Codec的参数进行设定的(包括bit rate、分辨率等重要信息)。同时作为“Stream”,它包含了“流”这个概念中的一些数据,比如:帧率(r_frame_rate)、基本时间计量单位(time_base)、(需要编解码的)首帧位置(start_time)、持续时间(duration)、帧数(nb_frames)以及一些ip信息。当然后面的这些信息中有些不是必须要初始化的,但是AVCodecContex是一定要初始化的,而且就是作为初始化AVStream最重要的一个部分。我们在前面就谈到了AVStream的初始化函数(5),现在来看看他是怎么做的:

// declaration

AVStream *video_st;

video_st = add_video_stream(oc, fmt->video_codec);

static AVStream *add_video_stream(AVFormatContex *oc, int codec_id){ (29)

AVCodecContext *c; // member of AVStream, which will be initialized here

AVStream *st; // temporary data, will be returned

st = av_new_stream(oc, 0); (30)

c = st->codec;

// 以下基本是针对c的初始化过程。包括比特率、分辨率、GOP大小等。

……
// 以下的两行需要注意一下,特别是使用MP4的

if(!strcmp(oc->oformat->name, “mp4”) || !strcmp(oc->oformat->name, “mov”) || !strcmp(oc->oformat->name, “3gp”))

c->flags |= CODEC_FLAG_GLOBAL_HEADER;

// 将st传给video_st;

return st;

}
以上代码中,有几点需要注意的。一个是(30)和c = st->codec是一定要做的,当然这是编程中最基本的问题,(30)是将st这个AVSteam绑定到AVFormatContext* oc上。后面的c = st->codec是将c绑定到st的AVCodecContext上。其二是对c的初始化过程中,ouput_example.c里做的是一些基本的配置,当然作为使用者的你还希望对codec加入其他的一些编解码的条件。可以参考avcodec.h里关于AVCodecContext结构的介绍,注释比较详细的。

关于AVStream的使用在前面介绍AVFormatContext时已有所涉及,在主函数中三个编解码函数中(8)、(10)和(11)中。观察相关的代码,可以发现主要还是将AVStream中的AVCodecContext提取出来,再从中提取出AVCodec结构如在(8)中:

// open_video(oc, video_st);

// AVFormatContext *oc, AVStream *st

AVCodec *codec;

AVCodecContext *c;

c = st->codec;

codec = avcodec_find_encoder(c->codec_id); (31)

// open the codec

avcodec_open(c, codec); (32)

同样,我们可以看到在(10)(write_video_frame())中AVFrame也是做为传递
AVCodecContext结构的载体而存在。(11)(close_video())比较简单,不熬述。

IV. AVCodecContext

此结构在Ffmpeg SDK中的注释是:main external api structure其重要性可见一斑。而且在avcodec它的定义处,对其每个成员变量,都给出了十分详细的介绍。应该说AVCodecContext的初始化是Codec使用中最重要的一环。虽然在前面的AVStream中已经有所提及,但是这里还是要在说一遍。AVCodecContext作为Avstream的一个成员结构,必须要在Avstream初始化後(30)再对其初始化(AVStream的初始化用到AVFormatContex)。虽然成员变量比较多,但是这里只说一下在output_example.c中用到了,其他的请查阅avcodec.h文件中介绍。

// static AVStream *add_video_stream(AVFormatContext *oc, int codec_id)

AVCodecContext *c;

st = av_new_stream(oc, 0);

c = st->codec;

c->codec_id = codec_id;

c->codec_type = CODEC_TYPE_VIDEO;

c->bit_rate = 400000; // 400 kbits/s

c->width = 352;

c->height = 288; // CIF

// 帧率做分母,秒做分子,那么time_base也就是一帧所用时间。(时间基!)

c->time_base.den = STREAM_FRAME_RATE;

c->time_base.num = 1;

c->gop_size =12;

// here define:

// #define STREAM_PIX_FMT PIX_FMT_YUV420P

// pixel format, see PIX_FMT_xxx

// -encoding: set by user.

// -decoding: set by lavc
.
c->pix_fmt = STREAM_PIX_FMT;

除了以上列出了的。还有诸如指定运动估计算法的: me_method。量化参数、最大b帧数:max_b_frames。码率控制的参数、差错掩盖error_concealment、模式判断模式:mb_decision (这个参数蛮有意思的,可以看看avcodec.h 1566行)、Lagrange multipler参数:lmin & lmax 和 宏块级Lagrange multipler参数:mb_lmin & mb_lmax、constant quantization parameter rate control method: cqp等。

值得一提的是在AVCodecContext中有两个成员数据结构:AVCodec、AVFrame。AVCodec记录了所要使用的Codec信息并且含有5个函数:init、encoder、close、decode、flush来完成编解码工作(参见avcode.h 2072行)。AVFrame中主要是包含了编码後的帧信息,包括本帧是否是key frame、*data[4]定义的Y、Cb和Cr信息等,随后详细介绍。

初始化後,可以说AVCodecContext在(8)&(10)中大显身手。先在(8)open_video()中初始化AVCodec *codec以及AVFrame* picture:

// AVCodecContext *c;

codec = avcodec_find_encoder(c->codec_id);

……
picture = alloc_picture(PIX_FMT_YUV420P, c->width, c->height);

後在writer_video_frame(AVFormatContext *oc, AVStream *st)中作为一个编解码器的主要参数被利用:

AVCodecContext *c;

c = st->codec;

……
out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture);

V.AVCodec

结构AVCodec中成员变量和成员函数比较少,但是很重要。他包含了CodecID,也就是用哪个Codec、

像素格式信息。还有前面提到过的5个函数(init、encode、close、decoder、flush)。顺便提一下,虽然在参考代码output_example.c中的编码函数用的是avcodec_encode_video(),我怀疑在其中就是调用了AVCodec的encode函数,他们传递的参数和返回值都是一致的,当然还没有得到确认,有兴趣可以看看ffmpeg源代码。在参考代码中,AVCodec的初始化後的使用都是依附于AVCodecContex,前者是后者的成员。在AVCodecContext初始化後(add_video_stream()),AVCodec也就能很好的初始化了:

//初始化

codec = avcodec_find_encoder(c->codec_id); (33)

//打开Codec

avcodec_open(c, codec) (34)

VI. AVFrame
AVFrame是个很有意思的结构,它本身是这样定义的:
typedef struct AVFrame {
FF_COMMON_FRAME
}AVFrame;
其中,FF_COMMON_FRAME是以一个宏出现的。由于在编解码过程中AVFrame中的数据是要经常存取的。为了加速,要采取这样的代码手段。
AVFrame是作为一个描述“原始图像”(也就是YUV或是RGB…还有其他的吗?)的结构,他的头两个成员数据,uint8_t *data[4],int linesize[4],第一个存放的是Y、Cb、Cr(yuv格式),linesize是啥?由这两个数据还能提取处另外一个数据结构:

typedef struct AVPicture {

uint8_t *data[4];

int linesize[4]; // number of bytes per line

}AVPicture ;

此外,AVFrame还含有其他一些成员数据,比如。是否key_frame、已编码图像书coded_picture_number、是否作为参考帧reference、宏块类型 *mb_type等等(avcodec.h 446行)。

AVFrame的初始化并没有他结构上看上去的那么简单。由于AVFrame还有一个承载图像数据的任务(data[4])因此,对他分配内存应该要小心完成。output_example.c中提供了alloc_picute()来完成这项工作。参考代码中定义了两个全局变量:AVFrame *picture,*tmp_picture。(如果使用yuv420格式的那么只用到前一个数据picture就行了,将图像信息放入picture中。如果是其他格式,那么先要将yuv420格式初始化后放到tmp_picture中在转到需求格式放入picture中。)在open_video()打开编解码器后初始化AVFrame:

picture = alloc_picture(c->pix_fmt, c->width, c->height);

tmp_picture = alloc_picture(PIX_FMT_YUV420P, c->width, c->height);

static AVFrame *alloc_picture(int pix_fmt, int width, int height){

AVFrame *picture;
uint8_t *picture_buf; // think about why use uint8_t? a byte!

picture = avcodec_alloc_frame(); (35)

if(!picture)
return NULL;

size = avpicture_get_size(pix_fmt, width, height); (36)

picture_buf = av_malloc(size); (37)

if(!picture_buf){

av_free(picture); (38)

return NULL;

}
avpicture_fill ( (AVPicture *)picture, picture_buf, pix_fmt, width, height); (39)
return picture;

}
从以上代码可以看出,完成对一个AVFrame的初始化(其实也就是内存分配),基本上是有这样一个固定模式的。至于(35)(39)分别完成了那些工作,以及为什么有这样两步,还没有搞清楚,需要看原代码。我的猜测是(35)对AVFrame做了基本的内存分配,保留了对可以提取出AVPicture的前两个数据的内存分配到(39)来完成。

说到这里,我们观察到在(39)中有一个(AVPicture *)picture,AVPicture这个结构也很有用。基本上他的大小也就是要在网络上传输的包大小,我们在后面可以看到AVPacket跟AVPicture有密切的关系。

VII.AVPicture

AVPicture在参考代码中没有自己本身的申明和初始化过程。出现了的两次都是作为强制类型转换由AVFrame中提取出来的:

// open_video() 中

avpicture_fill((AVPicture *)picture, picture_buf, pix_fmt, width, height); (40)
//write_video_frame 中

// AVPacket pkt;

if(oc->oformat->flags & AVFMT_RAWPICTURE){

……
pkt.size = sizeof(AVPicture); (41)}
在(40)中,实际上是对AVFrame的data[4]、linesize[4]分配内存。由于这两个数据大小如何分配确实需要有pix_fmt、width、height来确定。如果输出文件格式就是RAW 图片(如YUV和RGB),AVPacket作为将编码后数据写入文件的基本数据单元,他的单元大小、数据都是由AVPacket来的。总结起来就是,AVPicture的存在有以下原因,AVPicture将Picture的概念从Frame中提取出来,就只由Picture(图片)本身的信息,亮度、色度和行大小。而Frame还有如是否是key frame之类的信息。这样的类似“分级”是整个概念更加清晰。VIII.AVPacketAVPacket的存在是作为写入文件的基本单元而存在的。我们可能会认为直接把编码后的比特流写入文件不就可以了,为什么还要麻烦设置一个AVPacket结构。在我看来这样的编码设置是十分有必要的,特别是在做视频实时传输,同步、边界问题可以通过AVPacket来解决。AVPacket的成员数据有两个时间戳、数据data(通常是编码后数据)、大小size等等(参见avformat.h 48行)。讲AVPacket的用法就不得不提到编解码函数,因为AVPacket的好些信息只有在编解码后才能的知。在参考代码中(ouput_example.c 从362到394行),做的一个判断分支。如果输出文件格式是RAW图像(即YUV或RGB)那么就没有编码函数,直接写入文件(因为程序本身生成一个YUV文件),这里的代码虽然在此看来没什么价值,但是如果是解码函数解出yuv文件(或rgb)那么基本的写文件操作就是这样的:if(oc->oformat->flags & AVFMT_RAWPICTURE) {AVPacket pkt; // 这里没有用指针!av_init_packet(&pkt);pkt.flags |= PKT_FLAG_KEY // raw picture 中,每帧都是key frame?pkt.stream_index = st->index;pkt.data = (uint8_t *)picture;pkt.size = sizeof(AVPicture);ret = av_write_frame(oc, &pkt);}输出非raw picture,编码后:else{
// video_outbuf & video_outbuf_size在open_video() 中初始化out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture); (42)if(out_size > 0){AVPacket pkt;av_init_packet(&pkt); (43)pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base); (44)if(c->coded_frame->key_frame)
pkt.flags |= PKT_FLAG_KEY;pkt.stream_index= st->index;pkt.data= video_outbuf;pkt.size= out_size;/* write the compressed frame in the media file */
ret = av_write_frame(oc, &pkt); (45)} else {ret = 0;}if (ret != 0) {fprintf(stderr, "Error while writing video frame/n");exit(1);}其中video_outbuf和video_outbuf_size在open_video()里的初始化是这样的:video_outbuf = NULL;// 输出不是raw picture,而确实用到编码codecif( !(oc->oformat->flags & AVFMT_RAWPICTURE)){video_outbuf_size = 200000;video_outbuf = av_malloc(video_outbuf_size);}
(43)是AVPacket结构的初始化函数。(44)比较难理解,而且为什么会有这样的一些时间戳我也没有搞明白。其他的AVPacket成员数据的赋值比较容易理解,要注意的是video_outbuf和video_outbuf_size的初始化问题,由于在参考代码中初始化和使用不在同一函数中,所以比较容易忽视。(45)是写文件函数,AVFormatContext* oc中含有文件名等信息,返回值ret因该是一共写了多少数据信息,如果返回0则说明写失败。(42)和(45)作为比较重要的SDK函数,后面还会介绍的。.IX. Conclusion以上分析了FFMpeg中比较重要的数据结构。下面的这个生成关系理一下思路:(->表示 派生出)AVFormatContext->AVStream->AVCodecContext->AVCodec
|
AVOutputFormat or AVInputFormatAVFrame->AVPicture….>AVPacket二.FFMpeg 中的函数:在前一部分的分析中我们已经看到FFMpeg SDK提供了许多初始化函数和编码函数。我们要做的就是对主要数据结构正确的初始化,以及正确使用相应的编解码函数以及读写(I/O)操作函数。作为一个整体化的代码SDK,FFMpeg有一些他自己的标准化使用过程。比如函数av_register_all(); 就是一个最开始就该调用的“注册函数”,他初始化了libavcodec,“注册”了所有的的codec和视频文件格式(format)。下面,我沿着参考代码(ouput_example.c)的脉络,介绍一下相关函数。/******************************************************************
main()
******************************************************************/
1. av_register_all ();usage: initialize ibavcoded, and register all codecs and formats
每个使用FFMpeg SDK的工程都必须调用的函数。进行codec和format的注册,然后才能使用。声明在allformats.c中,都是宏有兴趣看看。2. AVOutputFormat guess_format(const char *short_name, const char *filename, const char *mime_type)usage: 通过文件后缀名,猜测文件格式,其实也就是要判断使用什么编码器(or解码器)。AVOutputFormat *fmt;fmt = guess_format(NULL, filename, NULL);3. AVFormatContext *av_alloc_format_context(void)usage: allocate the output media context.实际是初始化AVFormatContext的成员数据AVClass:AVFormatContext *ic;ic->av_class = &av_format_context_class;//where// format_to_name, options are pointer to functionstatic const AVClass av_format_context_class = {“AVFormatContext”, format_to_name, options};4. static AVStream *add_video_stream(AVFormatContext *ox, int codec_id);AVStream *video_st;video_st = add_video_stream(oc, fmt->video_codec);5. int av_set_parameters(AVFormatContext *s, AVFormatParameters *ap)usage: set the output parameters (must be done even if no parameters).AVFormatContext *oc;// if failed, return integer smaller than zeroav_set_parameters(oc, NULL);6. void dump_format(AVFormatContext *ic, int index, const char *url, int is_output);usage: 这一步会用有效的信息把 AVFormatContext 的流域(streams field)填满。作为一个可调试的诊断,我们会将这些信息全盘输出到标准错误输出中,不过你在一个应用程序的产品中并不用这么做:dump_format(oc, 0, filename, 1); // 也就是指明AVFormatContext中的事AVOutputFormat,还是 // AVInputFormat7. static void open_video(AVFormatContext *oc, AVStream *st)open_video(oc, video_st);8. int av_write_header(AVFormatContext *s)usage: allocate the stream private data and writer the stream header to an output media file. param s media filehandle, return 0 if OK, AVERROR_xxx if error.write the stream header, if anyav_write_header(oc);
9. static void write_video_frame(AVFormatContext *oc, AVStream *st)write_video_frame(oc, video_st);10. static void close_video(AVFormatContext *oc, AVStream *st)// close each codecclose_video(oc, video_st);11. int av_write_trailer(AVFormatContext *s)usage: write the trailer, if any. Write the stream trailer to an output media file and free the file private data.av_write_trailer(oc);12. void av_freep(void *arg)usage: free the streams. Frees memory and sets the pointer to NULL. arg pointer to the pointer which should be freed .av_freep(&oc->streams->codec);av_freeep(&oc->streams[s]);13. int url_fclose(ByteIOContext *s);usage: close the output fileurl_fclose(&oc->pb);
14. void av_free(void *ptr)usage: free the stream. Free memory which has been allocated with av_malloc(z)() or av_realloc().av_free(oc);/******************************************************************
******************************************************************
add_video_stream()
AVCodecContext *cAVStream *st******************************************************************/
******************************************************************
1.AVStream *av_new_stream(AVFormatContext *s, int id)

usage: add a new stream to a media file. s: media file handle, id: file format dependent stream id

st = av_new_stream(oc, 0);

/******************************************************************
******************************************************************
open_video()
AVCodecContext *c

AVCodec *codec

AVFrame *picture, *tmp_picture

uint8_t *video_output

int frame_count, video_outbuf_size;

******************************************************************
******************************************************************/
1 AVCodec *avcodec_find_encoder(enum CodecID id
)
usage: find the codec of encoder by CodecID. 在前面main中的guess_format()就已经开始为此准备了。

codec = avcodec_find_encoder(c->codec_id);

2 int avcodec_open(AVCodecContext *avctx, AVCodec *codec);

usage: opens / inits the AVCodecContext. 打开失败的话,返回值小于零。

avcodec_open(c, codec);

3 void *av_malloc(unsigned in size);

usage: you can redefine av_malloc and av_free in your project to use your memory allocator. You do not need to suppress this file because the linker will do it automaticallyMemory allocation of size byte with alignment suitable for all memory accesses (including vectors if available on the CPU). av_malloc(0) must return a non NULL pointer.

video_outbuf_size = 200000;

video_outbuf = avmalloc(video_outbuf_size);

4 static AVFrame *alloc_picture(int pix_fmt, int width, int height)

picture = alloc_picture(c->pix_fmt, c->width, c->height);

/******************************************************************
******************************************************************
******************************************************************
alloc_picture()
AVFrame *pictureuint8_t *picture_buf

int size************************************************************************************************************************************/
******************************************************************
1. avcodec_alloc_frame()usage: initialize AVFrame* picturepicture = avcodec_alloc_frame()2. int avpicture_get_size(int pix_fmt, int width, int height)usage: 根据像素格式和视频分辨率获得picture存储大小。size = avpicture_get_size(pix_fmt, width, height);picture_buf = av_malloc(size)3. int avpicture_fill(AVPicture *picture, uint8_t *ptr, int pix_fmt, int width, int height)usage: Picture field are filled with ‘ptr’ addresses, also return size。用ptr中的内容根据文件格式(YUV…) 和分辨率填充picture。这里由于是在初始化阶段,所以填充的可能全是零。avpicture_fill((AVPicture*)picture, picture_buf, pix_fmt, width, height);/******************************************************************
******************************************************************
write_video_frame()
int out_size, ret;AVCodecContext *c;static struct SwsContext *img_convert_ctx******************************************************************
******************************************************************/
1 struct SwsContext *sws_getContext(int srcW, ……)usage: 转变raw picture格式的获取context函数,比如下面的代码就是将其他格式的(如yuv422)转为yuv420,就要将context 保存在img_convert_ctx中,然后再利用后面的介绍函数做转化。img_convert_ctx = sws_getContext(c->width, c->height,PIX_FMT_YUV420P,c->width, c->height,c->pix_fmt,sws_flags, NULL, NULL, NULL);2 int sws_scale(struct SwsContext *ctx, uing8_t* src[], int srcStride[], int srcSliceY, int srcSliceH, uint8_t* dst[], int dstStride[]);usage: 根据SwsContext保存的目标文件context将src(source)转为dst(destination)。sws_scale(img_convert_ctx, tmp_picture->data, tmp_picture->linesize, 0, c->height, picture->data, picture->linesize);4. int avcodec_encode_video(AVCodecContext *avctx, uint8_t *buf, int buf_size, const AVFrame *pict);usage: 根据AVCodecContext将pict编码到buf中,buf_size是buf的大小。out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture);5 static inline void av_init_packet(AVPacket *pkt)usage: initialize optional fields of a packet.初始化AVPacket。AVPacket pkt;av_init_packet(&pkt)6 int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)usage: 校准时间基(maybe)pkt.pts = av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base);
7 int av_write_frame(AVFormatContext *s, AVPacket *pkt)usage: write a packet to an output media file . pkt: the packet, which contains the stream_index, buf/buf_size, dts/pts, …if error returncodec);av_free(picture->data[0]);av_free(picture);
if (tmp_picture) {av_free(tmp_picture->data[0]);av_free(tmp_picture);}av_free(video_outbuf);
}
/******************************************************************
******************************************************************

【zz】FFMpeg SDK 开发手册 阅读详情

相关推荐

ffmpeg.autogen的例子.rar

如果没有积分,可以从这里下载http://zxy15914507674.gitee.io/shared_resource_name/ffmpeg.autogen的例子.rar,C#对ffmpeg的封装,该Demo已经删到最精简了,C#如何操作可以参考:https://blog.csdn.net/vanjoge/article/details/79657874和https://www.cnblogs.com/leoxjy/p/9357810.html

C#使用FFMpeg.Autogen进行rtsp视频倍速播放

1.在你的C#项目中,使用NuGet包管理器安装FFMpeg.Autogen。

刺眼的博客 3913

c#-FFMpeg-AutoGen.zip

C#对ffmpeg的封装,该Demo已经删到最精简了,可直接运行。。。。。。。。

【亲测免费】 FFmpeg.AutoGen 安装与配置完全指南

**FFmpeg.AutoGen** 是一个基于 FFmpeg 的跨平台音频/视频处理库,特别设计用于简化 C# 和 .NET 开发者对 FFmpeg 强大功能的访问。这个开源项目通过自动生成不安全的绑定,让 C# 项目能够更直接地调用 FFmpeg 的底层原生库,支持 Linux, macOS, 和 Mono 环境。主要编程语言是 C#,同时也依赖于 C++ 生成绑定逻辑。 ## 项目使用的关...

gitblog_07158的博客 1890

FFmpeg.AutoGen:FFmpeg自动为C#.NET和Mono生成不安全的绑定

重要 FFmpeg API庞大而复杂,并且该项目以最小的修改公开了它-支持非常有限。 请考虑请问如何对问题或者在特殊仓库 。 社区也许可以提供一些帮助,但是您将很大程度上依靠自己。 作为另一种选择,您可以搜索C(lang)中的解决方案,您可以将其转换为C#。 FFmpeg.AutoGen FFmpeg自动为C#/。NET和Mono生成不安全的绑定。 用法 库用法的基本示例: FFmpeg.AutoGen.Example项目中包括视频解码,转换和将帧提取为jpeg。 有关更复杂的操作,请参阅官方“ API”部分。 Nuget软件包的版本使用并与FFmpeg的MAJOR和MINOR版本同步,因为

FFmpeg.AutoGen代码例子.rar

C#学习资料源码工具

FFMpeg SDK 开发手册(中文)下载仓库介绍

FFMpeg SDK 开发手册(中文)下载仓库介绍 去发现同类优质开源项目:https://gitcode.com/ 欢迎来到FFMpeg SDK 开发手册(中文)下载仓库!此仓库提供了一份详尽的FFMpeg SDK中文开发手册,旨在帮助开发者更好地理解和应用FFMpeg技术。 资源简介 本仓库所提供的《FFMpeg SDK 开发手册(中文)》是一份非常适合中文开发者的学习资料。由于国内网络环境的...

gitblog_06796的博客 755

FFmpeg.AutoGen-master_c#ffmpeg_FFmpeg.AutoGen_ffmpeg_ladysef_pay

C# ffmpeg.autogen 拉流

FFMpeg SDK 开发手册

FFMpeg 中比较重要的函数以及数据结构如下: 数据结构: (1) AVFormatContext (2) AVOutputFormat (3) AVInputFormat (4) AVCodecContext (5) AVCodec (6) AVFrame (7) AVPacket (8) AVPicture (9) AVStream 2. 初始化函数: (1) av_register_all() (2) avcodec_open() (3) avcodec_close() (4) av_open_i

qq_42894864的博客 1620

EfficientNet中的MBConv模块详解:为什么它能如此高效?

本文深度解析了EfficientNet中的MBConv模块,详细介绍了其核心设计理念、效率优化技术和复合缩放理论。MBConv通过深度可分离卷积、倒残差结构和注意力机制三大关键技术,实现了计算效率与模型性能的平衡,成为高效网络结构设计的典范。

tcp8optimizer的博客 349

KNX智能家居工程设计应用

KNX,是一个起源于欧洲的总线标准,主要应用于家居和楼宇中,对末端电气设备进行总线式的控制,比如照明、空调、地暖、电动窗帘和遮阳设备等等。根据2017年英国著名咨询公司BSRIA的数据报告显示,在中国的智能家居和灯光控制市场,KNX产品的市场份额为各类产品中排名第一,占据了42%的市场份额,远远领先于其他的标准协议。设备中一个继电器表示一路输出,某些功能可能需要用到多个输出,比如1个窗帘AC输出需要占用两路继电器输出,一个继电器用于控制正转,一个用于控制反转,而普通开关输出则需要占用一路继电器输出即可。

hhhh5555666的博客 934

ffmpeg SDK开发手册

FFMpeg SDK 开发手册FFMpeg 中比较重要的函数以及数据结构如下:1. 数据结构:(1) AVFormatContext(2) AVOutputFormat(3) AVInputFormat(4) AVCodecContext(5) AVCodec(6) AVFrame(7) AV...

chuanzou4767的博客 213

微信小程序(云开发) - 实现微信小程序拼团功能+前后台源码/数据库设计,开团参团、自动成团、微信分享链接URL参与拼团、组团人数/倒计时/实时数据、设置拼团人数限制等(拼团退款处理逻辑、订单同步等)

实现微信小程序云开发拼团功能,云开发小程序--微信拼团设计实现,微信小程序实现秒杀、拼团等效果,微信小程序云开发拼团源码完整版 包含前后台和数据库,拼团商城小程序开发,分销,拼团,社群团购,微信小程序—判断参团人数(设置拼团剩余人数数组)拼多多拼团小程序开发,微信云开发教程怎么实现微信小程序云开发拼团功能?云开发如何实现拼团,拼团小程序怎么弄,微信小程序案例:拼团模块,需要朋友助力,详细源码demo示例,如何做拼团功能,云开发小程序如何做拼团功能,外卖电商拼团,仿拼多多拼团,拼团后退款怎么办,拼团后订单管理

🏆 前端领域优质创作者 4628

FFMpegSDK开发手册中文下载仓库介绍:FFMpeg SDK中文开发手册,助力开发者掌握音视频处理技术

FFMpegSDK开发手册中文下载仓库介绍:FFMpeg SDK中文开发手册,助力开发者掌握音视频处理技术 去发现同类优质开源项目:https://gitcode.com/ 项目介绍 在现代软件开发领域,音视频处理技术已成为一项关键能力。FFMpeg作为一款功能强大的开源音视频处理工具,受到了全球开发者的广泛欢迎。然而,由于官方文档通常为英文编写,对于中文开发者来说,理解和应用FFMpeg技术可能...

gitblog_06788的博客 372

Ardupilot无人船(车)mavros自主控制

rover 4.4。

超维空间科技的博客 3229

【二 Deepseek+Coze案例】【 2.2 Coze + Deepseek 实现小红书爆款笔记:打造专属Agent智能体】

摘要 本文介绍了如何利用Coze平台和Deepseek模型构建自动化Agent智能体,用于生成和优化小红书爆款笔记。文章首先分析了爆款笔记的核心特征,包括精准定位、吸引标题、实用内容、精美排版和热门标签。随后提出Agent智能体的功能需求,涵盖热点追踪、内容生成与优化、数据分析及自动发布等模块。在技术实现上,详细阐述了从环境配置、数据采集到内容生成与优化的全流程,并提供了Python代码示例,展示如何调用Coze函数获取热点数据及竞品笔记。该方案通过智能化的内容创作与运营策略,帮助用户高效打造高曝光的小红书

商务合作|问题讨论|交流学习 请联系作者微信,加微信请务必注明来意,博客主页有联系方式 2281

FFMpeg SDK 开发手册 [转]

FFMpeg SDK 开发手册(1)- [3DTV] 版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明 http://leezen.blogbus.com/logs/16084170.html FFMpeg SDK开发手册 FFMpeg中比较重要的函数以及数据结构如下: 1. 数据结构: (1) AVFormatContext (2) AVOutputFo

kaisuihu的专栏 1219

电商数据采集实战——多平台价格监控、比价分析与智能告警系统

在电商竞争日益激烈的今天,价格监控与比价分析已成为品牌商、渠道商和消费者的核心需求。对于品牌商而言,需要监控各渠道售价是否低于指导价(破价监控);对于消费者而言,希望在最佳时机以最低价购入心仪商品;对于数据分析师而言,价格波动数据是洞察市场趋势的金矿。然而,电商数据采集面临三大核心挑战:本文将基于 Python + Scrapy + Playwright + Redis + ClickHouse 技术栈,构建一套完整的电商价格监控与比价系统。架构分层说明:电商平台的反爬手段层出不穷,采集系统必须具备多层次的

从事移动开发八年,csdn博客专家,阿里云特邀专家,华为云云享专家,高级网络信息安全工程师,大数据工程师(高级),python技术开发(高级),HarmonyOS 应用开发者高级认证,现在某学校任教。 136
上一篇: RM/RMVB文件格式总结
下一篇: AVI文件在opencore框架下的解析
uBar
博客等级 码龄19年 74粉丝 21原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值