作者:樱花落下的那天 | 来源:互联网 | 2023-07-10 13:58
ffmpeg学习日记5-使用ffmpeg进行h264解码文章目录首先解惑几个专业术语:解码步骤函数解释项目环境介绍编码中的报错释义报错1ubuntu安装libx264ubuntu下
ffmpeg学习日记5-使用ffmpeg进行h264解码
文章目录 首先解惑几个专业术语: 解码步骤 函数解释 项目环境介绍 编码中的报错释义 报错1 ubuntu安装libx264 ubuntu下将libx264编译进ffmpeg 报错2 安装新版本后的编译环境 项目完整代码如下 参考 后记 当我们确定一个视频的编码格式是使用h264进行编码的,那么就可以进行对应的解码,将视频解码之后,解码数据才可以进行相应的渲染,加特效的操作,下面学习如何对视频进行解码。
首先解惑几个专业术语:
YUV数据 YUV:视频像素格式 YUV数据:视频像素格式数据 H264 H264:视频压缩数据格式 解码步骤
注册组件
初始化封装格式上下文
获取源文件属性参数值
查找编码器
打开视频解码器
进行解码
数据存储
释放解码器
函数解释
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec); 功能:分配一个AVCodecContext并将其字段设置为默认值,结果结构应该释放使用avcodec_free_context() 参数:编解码器如果非null,分配私有数据并初始化给定编解码器的默认值。然后使用不同的编解码器调用avcodec_open2()是非法的。如果为NULL,那么特定于编解码器的默认值将不会被初始化,这可能会导致次优的默认设置(这主要对编码器来说很重要,例如libx264)。 返回值:返回一个经过填充默认值的AVCodecContext机构,失败时为NULL AVFrame *av_frame_alloc(void); 功能:分配一个AVFrame并将其字段设置为默认值。结果结构必须使用av_frame_free()释放。 返回值:一个AVFrame填充默认值或失败时为NULL。 note:这只分配了AVFrame本身,而不是数据缓冲区。这些必须通过其他方式分配,例如通过av_frame_get_buffer()或手动分配。 int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr, const AVPacket *avpkt); 功能:解码一帧视频数据。输入一个压缩编码的结构体AVPacket,输出一个解码后的结构体AVFrame。 项目环境介绍
ffmpeg版本:
>ffmpeg -version ffmpeg version n4.4-78-g031c0cb0b4-20210628 Copyright (c) 2000-2021 the FFmpeg developers built with gcc 10-win32 (GCC) 20210408 configuration: --prefix=/ffbuild/prefix --pkg-config-flags=--static --pkg-cOnfig=pkg-config --cross-prefix=x86_64-w64-mingw32- --arch=x86_64 --target-os=mingw32 --enable-gpl --enable-version3 --disable-debug --enable-shared --disable-static --disable-w32threads --enable-pthreads --enable-iconv --enable-libxml2 --enable-zlib --enable-libfreetype --enable-libfribidi --enable-gmp --enable-lzma --enable-fontconfig --enable-libvorbis --enable-opencl --enable-libvmaf --enable-vulkan --enable-amf --enable-libaom --enable-avisynth --enable-libdav1d --enable-libdavs2 --disable-libfdk-aac --enable-ffnvcodec --enable-cuda-llvm --enable-libglslang --enable-libgme --enable-libass --enable-libbluray --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvpx --enable-libwebp --enable-lv2 --enable-libmfx --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librav1e --enable-librubberband --enable-schannel --enable-sdl2 --enable-libsoxr --enable-libsrt --enable-libsvtav1 --enable-libtwolame --enable-libuavs3d --enable-libvidstab --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libzimg --extra-cflags=-DLIBTWOLAME_STATIC --extra-cxxflags= --extra-ldflags=-pthread --extra-ldexeflags= --extra-libs=-lgomp --extra-version=20210628 libavutil 56. 70.100 / 56. 70.100 libavcodec 58.134.100 / 58.134.100 libavformat 58. 76.100 / 58. 76.100 libavdevice 58. 13.100 / 58. 13.100 libavfilter 7.110.100 / 7.110.100 libswscale 5. 9.100 / 5. 9.100 libswresample 3. 9.100 / 3. 9.100 libpostproc 55. 9.100 / 55. 9.100
qt版本:5.12.0
编码中的报错释义
报错1 avcodec_encode_video2 -22 原因:没有编码器,安装libx264 ,这个是编码时的报错,解码报错应该也是一样的, 参考: avcodec_encode_video2 -22 这个解决办法是安装libx264,要进行编译安装,在win10下我不熟悉,所以,将重新搭建一个编译环境,环境为: $ cat /etc/issue ubuntu:Ubuntu 20.04.2 LTS \n \l qt:5.12.3
ffmpeg安装参考ffmpeg学习日记1-ffmpeg的基本介绍(相关概念理解,资料收集)中的安装ffmpeg
段落。
ubuntu安装libx264 $ wget https://johnvansickle.com/ffmpeg/release-source/libx264-git.tar.xz $ tar -xvf libx264-git.tar.xz $ cd libx264-git/ $ sudo ./configure --enable-shared $ sudo make -j4 $ sudo make install
参考:
ubuntu下将libx264编译进ffmpeg $ sudo ./configure --enable-shared --prefix=/usr/local/ffmpeg --enable-libx264 --extra-cflags=-I/usr/local/include --extra-ldflags=-L/usr/local/lib --enable-gpl $ sudo make -j4 $ sudo make install
参考:
linux 编译安装ffmpeg 与libx264的方法 编译完成后,运行项目,ret = avcodec_decode_video2(pCodecCtx,pFrame,&ret,&packet);
语句未报错。
报错2 安装新版本后的编译环境
ffmpeg版本:
$ ffmpeg -version ffmpeg version 4.1 Copyright (c) 2000-2018 the FFmpeg developers built with gcc 9 (Ubuntu 9.3.0-17ubuntu1~20.04) configuration: --enable-shared --prefix=/usr/local/ffmpeg --enable-libx264 --extra-cflags=-I/usr/local/include --extra-ldflags=-L/usr/local/lib --enable-gpl libavutil 56. 22.100 / 56. 22.100 libavcodec 58. 35.100 / 58. 35.100 libavformat 58. 20.100 / 58. 20.100 libavdevice 58. 5.100 / 58. 5.100 libavfilter 7. 40.101 / 7. 40.101 libswscale 5. 3.100 / 5. 3.100 libswresample 3. 3.100 / 3. 3.100 libpostproc 55. 3.100 / 55. 3.100
qt版本:5.12.2
Note: 在ffmpeg4.1版本中,去除了libavcodec/packet.h这个头文件。
项目完整代码如下
pro文件内容:
TEMPLATE = app CONFIG += console c++11 CONFIG -= app_bundle CONFIG -= qt SOURCES += \ main.cpp INCLUDEPATH += /usr/local/ffmpeg/include LIBS += /usr/local/ffmpeg/lib/libavcodec.so \ /usr/local/ffmpeg/lib/libavdevice.so \ /usr/local/ffmpeg/lib/libavfilter.so \ /usr/local/ffmpeg/lib/libavformat.so \ /usr/local/ffmpeg/lib/libavutil.so \ /usr/local/ffmpeg/lib/libpostproc.so \ /usr/local/ffmpeg/lib/libswresample.so \ /usr/local/ffmpeg/lib/libswscale.so
main.cpp内容如下:
#include #include #include using namespace std; extern "C"{ #include #include #include "libavutil/avutil.h" #include "libavformat/avformat.h" //#include "libavcodec/packet.h" #include "libavcodec/avcodec.h" } int main(int argc,char **argv) { cout <<"Hello World!" < //注册组件 avcodec_register_all(); AVCodecID codec_id = AV_CODEC_ID_H264; AVCodec *pcodec = NULL; AVCodecContext *pCodecCtx = NULL; //查找编码器 pcodec = avcodec_find_decoder(codec_id); if(!pcodec){ av_log(NULL,AV_LOG_ERROR,"no found decoder\n"); return 0; } //初始化封装格式上下文 pCodecCtx = avcodec_alloc_context3(pcodec); if(!pCodecCtx){ av_log(NULL,AV_LOG_ERROR,"avcodec_alloc_context3 is failed\n"); return 0; } //初始化参数,下面的参数应该由具体的业务决定 pCodecCtx->time_base.num = 1; pCodecCtx->frame_number = 1; pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO; pCodecCtx->bit_rate = 0; pCodecCtx->time_base.den = 29; pCodecCtx->width = 544; pCodecCtx->height = 960; //打开视频解码器 if (avcodec_open2(pCodecCtx,pcodec,NULL) <0){ av_log(NULL,AV_LOG_ERROR,"avcodec_open2 is failed\n"); return 0; } //读取文件数据 char *buff = new char[1024*1024*4]; fstream fileio; fileio.open("../jk.mp4",ios::binary | ios::in); fileio.read(buff,1024*1024*4); cout <<"read size:" < fileio.close(); //进行解码 int ret = 0; AVFrame *pFrame = NULL; pFrame = av_frame_alloc(); AVPacket packet; av_init_packet(&packet); ret = avcodec_decode_video2(pCodecCtx,pFrame,&ret,&packet); cout <<"ret:" < if ((ret <0) && (ret != AVERROR(EAGAIN)) && (ret != AVERROR_EOF) ){ av_log(NULL,AV_LOG_ERROR,"avcodec_send_packet error\n"); return 0; } else { cout <<"start" < int picSize = pCodecCtx->height * pCodecCtx->width; int newSize = int(picSize * 1.5); //申请内存 unsigned char *buf = new unsigned char[newSize]; //数据写入 int a = 0,i = 0; for(i = 0;i height;i++){ memcpy(buf+a,pFrame->data[0] + i*pFrame->linesize[0],pCodecCtx->width ); a += pCodecCtx->width; } for (i = 0;i height/2;i++){ memcpy(buf+a,pFrame->data[1] + i*pFrame->linesize[1],pCodecCtx->width/2 ); a += pCodecCtx->width/2; } for (i = 0;i height/2;i++){ memcpy(buf+a,pFrame->data[2] + i*pFrame->linesize[2],pCodecCtx->width/2 ); a += pCodecCtx->width/2; } cout <<"data:" < } return 0; }
参考
ffmpeg视频的编码Encode—YUV编码为h264
FFmpeg实时解码H264
ffmpeg 源代码简单分析 : avcodec_decode_video2()
H264 编解码协议详解
ffmpeg实战教程(一)Mp4,mkv等格式解码为h264和yuv数据
后记