当前位置: 首页 > news >正文

在 Jetson Orin 开发套件上使用 Hardware Encoder / Decoder 构建 FFmpeg

 

目录

1、构建 Jetson-FFmpeg

2、构建 FFmpeg

3、通过代码实现硬解 H264 文件为 YUV420P

3.1 运行可能遇到的问题

3.2 补丁应用失败的解决方法


1、构建 Jetson-FFmpeg

通过 Git 下载 Jetson-ffmpeg:

git clone https://github.com/Keylost/jetson-ffmpeg.git
cd jetson-ffmpeg

用文本编辑器打开 CMakeLists.txt ,注释掉以下行:

# find_library(LIB_NVBUF nvbuf_utils PATHS /usr/lib/aarch64-linux-gnu/tegra)

创建 build 文件夹,构建并安装 Jetson-ffmpeg:

mkdir build
cd build
cmake ..
make
sudo make install
sudo ldconfig

此时已经准备好将 NVMPI 与 FFmpeg 一起使用。

 

2、构建 FFmpeg

通过 Git 下载特定版本的 ffmpeg,并下载 ffmpeg_nvmpi 的补丁:

git clone git://source.ffmpeg.org/ffmpeg.git -b release/6.0 --depth=1
cd ffmpeg
wget -O ffmpeg_nvmpi.patch https://github.com/Keylost/jetson-ffmpeg/raw/master/ffmpeg_patches/ffmpeg6.0_nvmpi.patch

安装依赖,设置 ffmpeg 的安装路径以及依赖,编译并安装:

git apply ffmpeg_nvmpi.patch  # 根据需要上传到自己的分支
sudo apt install -y libpulse-dev
sudo apt-get install -y build-essential yasm nasm libx265-dev libx264-dev libnuma-de
./configure --prefix=/home/user/work/ffmpeg/install --enable-nvmpi --enable-libpulse --enable-shared --enable-libx264 --enable-gpl --enable-libx265 --enable-nonfree --enable-swresample --enable-swscale
make
sudo make install

 尝试用 ffmpeg 解码 h264 文件:

ffmpeg -re -i ../data/video.h264 -c:v h264_nvmpi -r 25 -f null -

 

3、通过代码实现硬解 H264 文件为 YUV420P

 以 Ubuntu 20.04 版本为例,通过 C 实现硬解 H264 文件为 YUV420P:

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <stdio.h>typedef struct StreamContext {AVCodecContext *dec_ctx;FILE *yuv_file;         // YUV输出文件
} StreamContext;static int open_input_file(const char *filename, AVFormatContext **input_ctx) {int ret;if ((ret = avformat_open_input(input_ctx, filename, NULL, NULL)) < 0) {av_log(NULL, AV_LOG_ERROR, "无法打开输入文件 '%s'\n", filename);return ret;}if ((ret = avformat_find_stream_info(*input_ctx, NULL)) < 0) {av_log(NULL, AV_LOG_ERROR, "无法获取流信息\n");return ret;}return 0;
}static int init_decoder(AVCodecContext **dec_ctx, AVStream *stream) {// 尝试使用 NVMPI 硬件解码器const AVCodec *dec = avcodec_find_decoder_by_name("h264_nvmpi");int ret;if (!dec) {av_log(NULL, AV_LOG_WARNING, "无法找到 NVMPI 硬件解码器,尝试使用软解码器\n");// 如果找不到硬件解码器,回退到软解码器dec = avcodec_find_decoder(stream->codecpar->codec_id);if (!dec) {av_log(NULL, AV_LOG_ERROR, "无法找到解码器\n");return AVERROR_DECODER_NOT_FOUND;}} else {av_log(NULL, AV_LOG_INFO, "使用 NVMPI 硬件解码器\n");}*dec_ctx = avcodec_alloc_context3(dec);if (!*dec_ctx) {av_log(NULL, AV_LOG_ERROR, "无法分配解码器上下文\n");return AVERROR(ENOMEM);}if ((ret = avcodec_parameters_to_context(*dec_ctx, stream->codecpar)) < 0) {av_log(NULL, AV_LOG_ERROR, "无法复制解码器参数\n");return ret;}// 设置线程数为1,避免多线程导致的问题(*dec_ctx)->thread_count = 1;// 确保输出格式为 YUV420P(*dec_ctx)->pix_fmt = AV_PIX_FMT_YUV420P;// 设置一些 NVMPI 特定的参数(如果需要的话)if (dec->id == AV_CODEC_ID_H264) {av_opt_set_int(*dec_ctx, "refcounted_frames", 1, 0);}if ((ret = avcodec_open2(*dec_ctx, dec, NULL)) < 0) {av_log(NULL, AV_LOG_ERROR, "无法打开解码器\n");return ret;}return 0;
}static int write_yuv_frame(FILE *f, AVFrame *frame) {// 检查帧数据是否有效if (!frame || !frame->data[0] || !frame->data[1] || !frame->data[2]) {av_log(NULL, AV_LOG_ERROR, "无效的帧数据\n");return AVERROR(EINVAL);}// 检查帧格式if (frame->format != AV_PIX_FMT_YUV420P) {const char *fmt_name = av_get_pix_fmt_name(frame->format);av_log(NULL, AV_LOG_ERROR, "不支持的像素格式: %s (需要 YUV420P)\n",fmt_name ? fmt_name : "unknown");return AVERROR(EINVAL);}// 检查帧尺寸if (frame->width <= 0 || frame->height <= 0) {av_log(NULL, AV_LOG_ERROR, "无效的帧尺寸: %dx%d\n", frame->width, frame->height);return AVERROR(EINVAL);}// 写入Y平面for (int i = 0; i < frame->height; i++) {size_t bytes_written = fwrite(frame->data[0] + i * frame->linesize[0], 1, frame->width, f);if (bytes_written != frame->width) {av_log(NULL, AV_LOG_ERROR, "写入Y平面失败: 写入了 %zu 字节,应该写入 %d 字节\n",bytes_written, frame->width);return AVERROR(EIO);}}// 写入U平面for (int i = 0; i < frame->height/2; i++) {size_t bytes_written = fwrite(frame->data[1] + i * frame->linesize[1], 1, frame->width/2, f);if (bytes_written != frame->width/2) {av_log(NULL, AV_LOG_ERROR, "写入U平面失败: 写入了 %zu 字节,应该写入 %d 字节\n",bytes_written, frame->width/2);return AVERROR(EIO);}}// 写入V平面for (int i = 0; i < frame->height/2; i++) {size_t bytes_written = fwrite(frame->data[2] + i * frame->linesize[2], 1, frame->width/2, f);if (bytes_written != frame->width/2) {av_log(NULL, AV_LOG_ERROR, "写入V平面失败: 写入了 %zu 字节,应该写入 %d 字节\n",bytes_written, frame->width/2);return AVERROR(EIO);}}return 0;
}int main(int argc, char **argv) {if (argc != 3) {av_log(NULL, AV_LOG_ERROR, "用法: %s <input.h264> <output.yuv>\n", argv[0]);return 1;}AVFormatContext *input_ctx = NULL;StreamContext stream_ctx = {0};AVPacket *packet = NULL;AVFrame *frame = NULL;int ret = 0;int video_stream_idx = -1;int frame_count = 0;// 打开输入文件if ((ret = open_input_file(argv[1], &input_ctx)) < 0)goto end;// 打开输出YUV文件stream_ctx.yuv_file = fopen(argv[2], "wb");if (!stream_ctx.yuv_file) {av_log(NULL, AV_LOG_ERROR, "无法打开输出文件 '%s'\n", argv[2]);ret = AVERROR(EIO);goto end;}// 查找视频流for (int i = 0; i < input_ctx->nb_streams; i++) {if (input_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {video_stream_idx = i;break;}}if (video_stream_idx < 0) {av_log(NULL, AV_LOG_ERROR, "找不到视频流\n");ret = AVERROR_STREAM_NOT_FOUND;goto end;}// 初始化解码器if ((ret = init_decoder(&stream_ctx.dec_ctx, input_ctx->streams[video_stream_idx])) < 0)goto end;// 打印视频信息av_log(NULL, AV_LOG_INFO, "视频大小: %dx%d\n", stream_ctx.dec_ctx->width,stream_ctx.dec_ctx->height);av_log(NULL, AV_LOG_INFO, "像素格式: %s\n",av_get_pix_fmt_name(stream_ctx.dec_ctx->pix_fmt));// 分配 packet 和 framepacket = av_packet_alloc();frame = av_frame_alloc();if (!packet || !frame) {av_log(NULL, AV_LOG_ERROR, "无法分配内存\n");ret = AVERROR(ENOMEM);goto end;}// 主处理循环while (1) {ret = av_read_frame(input_ctx, packet);if (ret < 0) {if (ret == AVERROR_EOF) {// 发送一个空包,刷新解码器中的帧ret = avcodec_send_packet(stream_ctx.dec_ctx, NULL);} else {av_log(NULL, AV_LOG_ERROR, "读取帧错误: %s\n", av_err2str(ret));break;}} else if (packet->stream_index != video_stream_idx) {av_packet_unref(packet);continue;} else {ret = avcodec_send_packet(stream_ctx.dec_ctx, packet);}if (ret < 0 && ret != AVERROR_EOF) {av_log(NULL, AV_LOG_ERROR, "发送数据包到解码器失败: %s\n", av_err2str(ret));break;}while (1) {ret = avcodec_receive_frame(stream_ctx.dec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;} else if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "解码帧错误: %s\n", av_err2str(ret));goto end;}// 写入YUV数据ret = write_yuv_frame(stream_ctx.yuv_file, frame);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "写入YUV帧失败: %s\n", av_err2str(ret));goto end;}frame_count++;if (frame_count % 100 == 0) {av_log(NULL, AV_LOG_INFO, "已处理 %d 帧\n", frame_count);}av_frame_unref(frame);}if (ret == AVERROR_EOF) {break;}av_packet_unref(packet);}av_log(NULL, AV_LOG_INFO, "解码完成,共处理 %d 帧\n", frame_count);ret = 0;end:// 清理资源if (stream_ctx.yuv_file)fclose(stream_ctx.yuv_file);avcodec_free_context(&stream_ctx.dec_ctx);avformat_close_input(&input_ctx);av_packet_free(&packet);av_frame_free(&frame);return ret < 0 ? 1 : 0;
} 

通过 gcc 编译,并运行可执行文件:

# 编译
gcc -g -o h264_to_yuv simple_transcode.c \$(pkg-config --cflags --libs libavcodec libavformat libavutil) \-I/usr/local/include -L/usr/local/lib# 运行
./h264_to_yuv ../code/data/video.h264 output.yuv

3.1 运行可能遇到的问题

在运行代码的时候,如果出现 “无法找到 NVMPI 硬件解码器,尝试使用软解码器” 的打印,可能是因为 FFmpeg 库的配置有问题。

这个问题是由于 FFmpeg 的编译配置导致的,之前下载的 ffmpeg_nvmpi.patch 文件,这是一个为FFmpeg 添加 NVMPI 支持的补丁文件。虽然可以使用 ffmpeg 命令行工具进行 NVMPI 硬件解码,但这是因为系统中安装的 FFmpeg 已经被正确编译并启用了 NVMPI 支持。

而当前的代码是直接使用 FFmpeg 的库进行编程,这需要确保链接的 FFmpeg 库也启用了 NVMPI支持。

解决方法如下:

        1. 确保 FFmpeg 库是使用 NVMPI 支持编译的。

   # 首先应用NVMPI补丁patch -p1 < ffmpeg_nvmpi.patch# 然后重新配置和编译FFmpeg./configure --enable-nvmpi --enable-shared # 可按需配置 也可用之前的配置makesudo make install

        2. 确保系统中安装了 NVMPI 的开发库和头文件。

        3. 在编译程序时,需要链接到正确的 FFmpeg 库。

        可以通过以下命令来验证 FFmpeg 库是否支持 NVMPI:

ffmpeg -codecs | grep nvmpi

如果看到类似 h264_nvmpi 这样的编解码器,说明 FFmpeg 命令行工具确实支持 NVMPI。

3.2 补丁应用失败的解决方法

这种情况通常意味着:

        1. 补丁之前已经被应用过

        2. 由于选择反向应用(-R),导致补丁操作变成了移除操作

        3. 最后在 nvmpi_enc.c 文件上失败,可能是因为文件内容与预期不符

按以下步骤来修复这个问题:

1. 首先检查当前的文件状态,恢复到原始状态:

root@desktop:~/work/ffmpeg$ git status

root@desktop:~/work/ffmpeg$ git checkout configure libavcodec/Makefile libavcodec/allcodecs.c

2. 清理之前的补丁文件:

root@desktop:~/work/ffmpeg$ rm -f configure.orig libavcodec/*.orig libavcodec/*.rej

3. 检查一下补丁文件的内容:

root@desktop:~/work/ffmpeg$ head -n 5 ffmpeg_nvmpi.patch

4.  重新应用补丁,但这次使用 -f 参数强制应用,并且不要选择反向应用:

root@desktop:~/work/ffmpeg$ patch -p1 -f < ffmpeg_nvmpi.patch

如果提示 nvmpi_enc.c 文件还有问题。这是因为这个文件已经存在,并且内容可能与补丁不完全匹配。 删除已经存在的 nvmpi 相关文件:

root@desktop:~/work/ffmpeg$ rm -f libavcodec/nvmpi_dec.c libavcodec/nvmpi_enc.c

避免补丁再次被反向应用,这次使用 --reject-file=- 选项来忽略拒绝文件,重新应用补丁:

root@desktop:~/work/ffmpeg$ git checkout configure libavcodec/Makefile libavcodec/allcodecs.c && patch -p1 --reject-file=- < ffmpeg_nvmpi.patch

重新配置和编译 FFmpeg,安装编译好的 FFmpeg: 

./configure --enable-nvmpi --enable-shared && make -j4
sudo make install

最后再重新编译程序:

gcc simple_transcode.c -o h264_to_yuv $(pkg-config --libs --cflags libavcodec libavformat libavutil) -lnvmpi# 运行可执行文件
./h264_to_yuv ../code/data/video.h264 output.yuv

现在就能够通过代码直接调用硬件解码器解码 H264 文件了。

http://www.lryc.cn/news/579428.html

相关文章:

  • 仿真软件介绍 COMSOL Multiphysics 或 ANSYS Fluent 等 MATLAB OpenFOAM,和在化学上的应用实例
  • 2025年6月一区-田忌赛马优化算法Tianji’s horse racing optimization-附Matlab免费代码
  • Springboot3整合ehcache3缓存--XML配置和编程式配置
  • 【PyCharm 2025.1.2配置debug】
  • 【vmware虚拟机使用】 开始安装centos7操作系统
  • Navicat Premium 12连接Oracle时提示oracle library is not loaded的问题解决
  • 分布式部署下如何做接口防抖---使用分布式锁
  • macOS 26正式发布,全新Liquid Glass设计语言亮相
  • 旅游管理实训室:支撑实践教学的核心载体
  • 5118 API智能处理采集数据教程
  • 项目——视频共享系统测试
  • 【C++】状态模式
  • GitHub 解码指南:用 AI 赋能,五步快速掌握任意开源项目
  • MySQL 8.0 OCP 1Z0-908 题目解析(20)
  • MVC 架构设计模式
  • 【Linux仓库】进程优先级及进程调度【进程·肆】
  • 小黑黑日常积累大模型prompt句式2:【以段落的形式输出,不分点列举】【如果没有相关内容则不输出】【可读性强】【输出格式规范】
  • Java学习第八部分——泛型
  • git 中删除提交历史
  • 代码随想录算法训练营第四十五天|动态规划part12
  • Fiddler中文版抓包工具在后端API调试与Mock中的巧用
  • 应用在核电行业的虚拟现实解决方案
  • Laravel8中调取腾讯云文字识别OCR
  • 【前端开发】Uniapp分页器:新增输入框跳转功能
  • SpringCloud系列(49)--SpringCloud Stream消息驱动之实现生产者
  • Rubber Band Algorithm 应力及反作用力测试
  • 运维打铁: 企业运维开发痛点之解决方案
  • ModuleNotFoundError: No module named ‘onnxruntime‘
  • 【免费.NET方案】CSV到PDF与DataTable的快速转换
  • 图论基础算法入门笔记