fmpeg(ignore)------主要函数=通用+解码+编码

FFmpeg中比较重要的函数以及数据结构
在这里插入图片描述
FFMpeg 中比较重要的函数以及数据结构如下:

1. 数据结构:

(1) AVFormatContext
(2-0) AVIOContext
(2) AVOutputFormat
(3) AVInputFormat
(4) AVCodecContext
(5) AVCodec

【查找编解码器】  //遍历AVCodec链表并且获得符合条件的元素 然后返回符合的编解码器
AVCodec *avcodec_find_encoder(enum AVCodecID id)	{return find_encdec(id, 1);}
AVCodec *avcodec_find_decoder(enum AVCodecID id)	{return find_encdec(id, 0);}
调用同一个查找函数find_encdec() 传参0/1表示找的是编码器or解码器
	//类似的:AVCodec *avcodec_find_decoder_by_name(const char *name);//根据解码器名称查找解码器并返回AVCodec
	//pCodec=avcodec_find_encoder(pCodecCtx->codec_id);【根据指定解码器ID查找相应的编码器并返回AVCodec】

static AVCodec *find_encdec(enum AVCodecID id, 【编or解】int encoder)
循环会遍历AVCodec结构的【全局变量】链表,逐一比较输入的ID和每一个编码器的ID,直到找到ID取值相等的编码器

int av_codec_is_encoder(const AVCodec *codec);
int av_codec_is_encoder(const AVCodec *codec)   
判定是否是编解码器 返回int

(6) AVFrame
(7) AVPicture
(8) AVPacket
(9) AVStream
(10)SwsContext视频分辩率、色彩空间变换时所需要的上下文句柄。
(11)AVCodecParser

通用

void av_register_all(void)		注册所有东西
void avcodec_register_all(void) 注册编解码器
int avformat_network_init(void); 加载socket库以及网络加密协议相关的库

int avio_open2(	创建的AVIOContext **s, 文件地址const char *url, 打开方式int flags,
				const AVIOInterruptCB *int_cb, AVDictionary **options);
//≈≈≈avio_open2

int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); 使用AVCodec初始化AVCodecContext  即打开解码器

解码

初始化:
	1.先进行avformat_alloc_context→→→得到一个AVFormatContext
	2.int avformat_open_input(得到的AVFormatContext **ps, 
							打开URL:const char *filename, 
							指定输入格式AVInputFormat *fmt, 
							额外参数AVDictionary **options);
//av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)函数读取文件的头部并且把信息保存到我们给的AVFormatContext结构体中。最后三个参数用来指定文件格式,缓冲大小和格式参数,但如果把它们设置为空NULL或者0,libavformat将自动检测这些参数。

//av_open_input_file 只是检测了文件的头部
//所以接着 av_find_stream_info(pFormatCtx) 函数负责检查在文件中的流的信息。 作用是为pFormatCtx->streams填充上正确的信息。
int avformat_find_stream_info(输入的AVFormatContext *ic, 额外选项AVDictionary **options); 
	检查并给每个媒体流(音频/视频)的AVStream结构体赋值
/*
		读取一部分视音频数据并且获得一些相关的信息
		【如各媒体流对应编解码器的类型AVMediaType和ID:CodecID】
		{
			1.查找解码器:find_decoder() //若有解码器or类型则直接返回 若没有则调用avcodec_find_decoder(codec_id)遍历找到解码器
			2.打开解码器:avcodec_open2()
			3.读取完整的一帧压缩编码的数据:read_frame_internal()	
			//av_read_frame()内部实际上就是调用的read_frame_internal()。
			4.解码一些压缩编码数据:try_decode_frame()
		}
*/


找到要处理的stream[videoindex]
	videoindex=-1;
	for(i=0; i<pFormatCtx->nb_streams; i++) 
		if(pFormatCtx->streams[i]->codec->codec_type==目标枚举类型AVMEDIA_TYPE_VIDEO){videoindex=i;break;}

pCodecCtx找到并打开编解码器
	pCodecCtx=pFormatCtx->streams[videoindex]->codec;
	pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
	avcodec_open2(pCodecCtx, pCodec,NULL) 打开解码器 即用pCodec去初始化pCodeCtx

pFrame=av_frame_alloc();   	解码得到
pFrameYUV=av_frame_alloc();	转换成YUV格式
packet=(AVPacket *)av_malloc(sizeof(AVPacket));

输出打印校验信息
	av_dump_format(pFormatCtx,0,filepath,0);	

设置并得到视频分辩率、色彩空间变换时所需要的上下文句柄
	img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); 
	
循环提取出一帧数据并转换:
	while(av_read_frame(pFormatCtx, packet)>=0)		从AVFormatContext中读取一帧数据AVPacket
	{
		if(packet->stream_index==videoindex)	必须属于指定的stream
		{
			ret = avcodec_decode_video2(pCodecCtx, 输出pFrame, &got_picture, 输入packet); 解码一帧视频数据:AVPacket→AVFrame
			if(ret < 0){printf("Decode Error.\n");return -1;}
			if(got_picture){
				SDL_LockYUVOverlay(bmp);		锁定
				pFrameYUV->data[0]=bmp->pixels[0];
				pFrameYUV->data[1]=bmp->pixels[2];
				pFrameYUV->data[2]=bmp->pixels[1];     
				pFrameYUV->linesize[0]=bmp->pitches[0];
				pFrameYUV->linesize[1]=bmp->pitches[2];   
				pFrameYUV->linesize[2]=bmp->pitches[1];
				sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, 
					pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
				SDL_UnlockYUVOverlay(bmp); 

				SDL_DisplayYUVOverlay(bmp, &rect); 
				//Delay 40ms
				SDL_Delay(40);
			}
		}
		av_free_packet(packet);	
	}
	
	FIX: Flush Frames remained in Codec 类似上面的循环 输出余下的数据

清理
	SDL_Quit();
	av_free(pFrameYUV);
	avcodec_close(pCodecCtx);
	avformat_close_input(&pFormatCtx);

编码

FFmpeg源代码简单分析:avformat_write_header + av_write_frame + av_write_trailer

初始化:
int avformat_alloc_output_context2(	初始化并得到的AVFormatContext **avctx, 
									指定输出格式AVOutputFormat *oformat,
                               	  	输出格式const char *format, 
                                	输出文件名const char *filename) 
{
	1)avformat_alloc_context初始化默认AVFormatContext。
	2)若输入AVOutputFormat→→给予AVOutputFormat的oformat。
	反之(无参数2),调用av_guess_format(使用参数34)推测输出的AVOutputFormat。然后给予AVOutputFormat的oformat。
}


编码:
int avcodec_encode_video2(	AVCodecContext *avctx, 		输出AVPacket *avpkt,
                          	输入const AVFrame *frame, 	编码成功标志位int *got_packet_ptr);		

输出:
写视频文件头		int avformat_write_header(用于输出的AVFormatContext *s,额外选项 AVDictionary **options);
写视频数据		int av_write_frame(用于输出的AAVFormatContext *s, 等待输出的AVPacket *pkt);		
写视频文件尾		int av_write_trailer(用于输出的AVFormatContext *s);		

sws 1.图像色彩空间转换;2.分辨率缩放;3.前后图像滤波处理。

FFmpeg 的sws_getContext函数 、sws_scale函数 滤波器?

sws_getContext():初始化SwsContext
sws_scale():处理图像数据 转换图像格式
sws_freeContext():释放

1.初始化
struct SwsContext *sws_getContext(&目标的宽高、像素格式+flag+输入/输出图像滤波器信息+param)
	sws_getContext(w, h, YV12, w, h, NV12, 0, NULL, NULL, NULL);      // YV12->NV12 色彩空间转换
	sws_getContext(w, h, YV12, w/2, h/2, YV12, 0, NULL, NULL, NULL);  // YV12图像缩小到原图1/4
	sws_getContext(w, h, YV12, 2w, 2h, YN12, 0, NULL, NULL, NULL);    // YV12图像放大到原图4倍,并转换为NV12结构
函数返回SwsContext结构体,定义了基本变换信息。
2.转换
int sws_scale(	struct SwsContext *c,
				源数据的通道指针const uint8_t *const srcSlice[],   //Y U V分别对应一个通道指针 [R+G+B]+[R+G+B]... 故只占用一个通道 维数=1
              	源数据的每一行起始位置const int srcStride[], 
              	起始位置int srcSliceY, 
              	处理行数int srcSliceH,
              	输出数据的通道指针uint8_t *const dst[], 
              	输出数据通道行字节数const int dstStride[]);


stride定义下一行的起始位置。stride和width不一定相同,这是因为:
1.由于数据帧存储的对齐,有可能会向每行后面增加一些填充字节这样 stride = width + N;
2.packet色彩空间下,每个像素几个通道数据混合在一起,例如RGB24,每个像素3字节连续存放,因此下一行的位置需要跳过3*width字节。
srcSlice和srcStride的维数相同,由srcFormat值来。
csp       维数        宽width      跨度stride      高
YUV420     3        w, w/2, w/2    s, s/2, s/2   h, h/2, h/2
YUYV       1        w, w/2, w/2   2s, 0, 0       h, h, h
NV12       2        w, w/2, w/2    s, s, 0       h, h/2
RGB24      1        w, w,   w     3s, 0, 0       h, 0, 0           
参数int srcSliceY, int srcSliceH,定义在输入图像上处理区域,srcSliceY是起始位置,srcSliceH是处理多少行。如果srcSliceY=0,srcSliceH=height,表示一次性处理完整个图像。
这种设置是为了多线程并行,例如可以创建两个线程,第一个线程处理 [0, h/2-1]行,第二个线程处理 [h/2, h-1]行。并行处理加快速度。
参数uint8_t *const dst[], const int dstStride[]定义输出图像信息(输出的每个通道数据指针,每个通道行字节数)
3.释放sws_scale
void sws_freeContext(struct SwsContext *swsContext);
发布了81 篇原创文章 · 获赞 1 · 访问量 2901

猜你喜欢

转载自blog.csdn.net/qq_42024067/article/details/103066148