本文共 6617 字,大约阅读时间需要 22 分钟。
FFMpeg 中比较重要的函数以及数据结构如下:(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)AVCodecParservoid 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_open2int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); 使用AVCodec初始化AVCodecContext 即打开解码器
avformat_open_input为AVFormatContext对象分配内存,打开指定的文件(自动检测格式)并读取文件头,将存储在其中的信息导出到AVFormatContext *s。有些格式没有文件头,或者没有在其中存储足够的信息,因此建议您调用avformat_find_stream_info()函数,该函数尝试读取和解码几个帧以查找丢失的信息。
初始化: 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; inb_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去初始化pCodeCtxpFrame=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);
初始化: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_getContext():初始化SwsContextsws_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/2YUYV 1 w, w/2, w/2 2s, 0, 0 h, h, hNV12 2 w, w/2, w/2 s, s, 0 h, h/2RGB24 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_scalevoid sws_freeContext(struct SwsContext *swsContext);
转载地址:http://ryhrn.baihongyu.com/