广州手机网站制作咨询,微信怎样创建公众号,wordpress centos 7安装,招远网站建设联系电话在上一篇文章中#xff0c;对FFmpeg的视频解码过程做了一个总结。由于才接触FFmpeg#xff0c;还是挺陌生的#xff0c;这里就解码过程再做一个总结。本文的总结分为以下两个部分#xff1a; 数据读取#xff0c;主要关注在解码过程中所用到的FFmpeg中的结构体。解码过程中… 在上一篇文章中对FFmpeg的视频解码过程做了一个总结。由于才接触FFmpeg还是挺陌生的这里就解码过程再做一个总结。本文的总结分为以下两个部分 数据读取主要关注在解码过程中所用到的FFmpeg中的结构体。解码过程中所调用的函数在学习的过程主要参考的是dranger tutorial所以跟着教程在本文的最后使用SDL2.0将解码后的数据输出到屏幕上。 数据的读取 一个多媒体文件包含有多个流视频流 video stream音频流 audio stream字幕等流是一种抽象的概念表示一连串的数据元素流中的数据元素称为帧Frame。也就是说多媒体文件中主要有两种数据流Stream 及其数据元素 帧Frame在FFmpeg自然有与这两种数据相对应的抽象AVStream和AVPacket。 使用FFmpeg的解码数据的传递过程可归纳如下 调用avformat_open_input打开流将信息填充到AVFormatContext中调用av_read_frame从流中读取数据帧到 AVPacketAVPacket保存仍然是未解码的数据。调用avcodec_decode_video2将AVPacket的数据解码并将解码后的数据填充到AVFrame中AVFrame中保存的是解码后的原始数据。上述过程可以使用下图表示 结构体的存储空间的分配与释放 FFmpeg并没有垃圾回收机制所分配的空间都需要自己维护。而由于视频处理过程中数据量是非常大对于动态内存的使用更要谨慎。本小节主要介绍解码过程使用到的结构体存储空间的分配与释放。 AVFormatContext 在FFmpeg中有很重要的作用描述一个多媒体文件的构成及其基本信息存放了视频编解码过程中的大部分信息。通常该结构体由avformat_open_input分配存储空间在最后调用avformat_input_close关闭。 AVStream 描述一个媒体流在解码的过程中作为AVFormatContext的一个字段存在不需要单独的处理。AVpacket 用来存放解码之前的数据它只是一个容器其data成员指向实际的数据缓冲区在解码的过程中可有av_read_frame创建和填充AVPacket中的数据缓冲区当数据缓冲区不再使用的时候可以调用av_free_apcket释放这块缓冲区。AVFrame 存放从AVPacket中解码出来的原始数据其必须通过av_frame_alloc来创建通过av_frame_free来释放。和AVPacket类似AVFrame中也有一块数据缓存空间在调用av_frame_alloc的时候并不会为这块缓存区域分配空间需要使用其他的方法。在解码的过程使用了两个AVFrame这两个AVFrame分配缓存空间的方法也不相同 一个AVFrame用来存放从AVPacket中解码出来的原始数据这个AVFrame的数据缓存空间通过调avcodec_decode_video分配和填充。另一个AVFrame用来存放将解码出来的原始数据变换为需要的数据格式例如RGBRGBA的数据这个AVFrame需要手动的分配数据缓存空间。代码如下AVFrame* pFrameYUV;
pFrameYUV av_frame_alloc();
// 手动为 pFrameYUV分配数据缓存空间
int numBytes avpicture_get_size(AV_PIX_FMT_YUV420P,pCodecCtx-widht,pCodecCtx-width);
uint8_t* buffer (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));
// 将分配的数据缓存空间和AVFrame关联起来
avpicture_fill((AVPicture *)pFrameYUV, buffer, AV_PIX_FMT_YUV420P,pCodecCtx-width, pCodecCtx-height)首先计算需要缓存空间大小调用av_malloc分配缓存空间最后调用avpicture_fill将分配的缓存空间和AVFrame关联起来。调用av_frame_free来释放AVFrame该函数不止释放AVFrame本身的空间还会释放掉包含在其内的其他对象动态申请的空间例如上面的缓存空间。 av_malloc和av_freeFFmpeg并没有提供垃圾回收机制所有的内存管理都要手动进行。av_malloc只是在申请内存空间的时候会考虑到内存对齐2字节4字节对齐其申请的空间要调用av_free释放。调用的函数 av_register_all 这个函数不用多说了注册库所支持的容器格式及其对应的CODEC。avformat_open_input 打开多媒体文件流并读取文件的头将读取到的信息填充到AVFormatContext结构体中。在使用结束后要调用avformat_close_input关闭打开的流avformat_find_stream_info 上面提到avformat_open_input只是读取文件的头来得到多媒体文件的信息但是有些文件没有文件头或者文件头的格式不正确这就造成只调用avformat_open_input可能得不到解码所需要的必要信息需要调用avformat_find_stream_info进一步得到流的信息。通过上面的三个函数已经获取了对多媒体文件进行解码的所需要信息下面要做的就是根据这些信息得到相应的解码器。结构体AVCodecContext描述了编解码器的上下文信息包含了流中所使用的关于编解码器的所有信息可以通过 AVFormatContext-AVStream-AVCodecContext来得到在有了AVCodecContext后可以通过codec_id来找到相应的解码器具体代码如下 AVCodec* pCodec nullptr;
pCodecCtxOrg pFormatCtx-streams[videoStream]-codec; // codec context
// 找到video stream的 decoder
pCodec avcodec_find_decoder(pCodecCtxOrg-codec_id); avcodec_find_decoder 可以通过codec_id或者名称来找到相应的解码器返回值是一个AVCodec的指针。avcodec_open2 打开相应的编解码器av_read_frame 从流中读取数据帧暂存到AVPacket中avcodec_decode_video2 从AVPacket中解码数据到AVFrame中经过以上的过程AVFrame中的数据缓存中存放的就是解码后的原始数据了。整个流程梳理如下 使用SDL2.0显示视频 使用SDL2.0dranger tutorial中的显示视频部分的代码就不是很适用了需要做一些修改。不过SDL2.0显示图像还是挺简单的。 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);SDL_Window* window SDL_CreateWindow(FFmpeg Decode, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,pCodecCtx-width, pCodecCtx-height, SDL_WINDOW_OPENGL | SDL_WINDOW_MAXIMIZED);SDL_Renderer* renderer SDL_CreateRenderer(window, -1, 0);SDL_Texture* bmp SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,pCodecCtx-width, pCodecCtx-height);SDL_Rect rect;rect.x 0;rect.y 0;rect.w pCodecCtx-width;rect.h pCodecCtx-height;SDL_Event event; 上述代码为初始化后SDL显示图像所需要的环境在使用FFmpeg解码数据后 int frameFinished 0;avcodec_decode_video2(pCodecCtx, pFrame, frameFinished, packet);if (frameFinished){sws_scale(sws_ctx, (uint8_t const * const *)pFrame-data, pFrame-linesize, 0,pCodecCtx-height, pFrameRGB-data, pFrameRGB-linesize);SDL_UpdateTexture(bmp, rect, pFrameRGB-data[0], pFrameRGB-linesize[0]);SDL_RenderClear(renderer);SDL_RenderCopy(renderer, bmp, rect, rect);SDL_RenderPresent(renderer);} 上面代码就将解码得到的图像帧使用SDL显示了出来。不过这里真的只是显示而已以能够解码速度快速的将整个视频的图像帧显示一遍。 本节示例代码 FFmpeg1.cpp 转载于:https://www.cnblogs.com/laughingQing/p/5901675.html