网站首页分辨率,怎样加入好大夫网站做医生,那些域名可以做后缀做网站,dede个人网站前言
在上一篇文章中#xff0c;我们讲解了如何使用 ffmpeg-rockchip 通过命令来实现 MPP 视频硬件编解码和 RGA 硬件图形加速#xff0c;在这篇文章#xff0c;我将讲解如何使用 ffmpeg-rockchip 用户空间库#xff08;代码#xff09;实现 MPP 硬件编解码。
本文不仅适…前言
在上一篇文章中我们讲解了如何使用 ffmpeg-rockchip 通过命令来实现 MPP 视频硬件编解码和 RGA 硬件图形加速在这篇文章我将讲解如何使用 ffmpeg-rockchip 用户空间库代码实现 MPP 硬件编解码。
本文不仅适用于 RK3588还适用于 RK 家族系列的芯片具体的细节可查看官方 MPP 文档。
前置条件
本文假设你已经了解或掌握如下知识
ffmpeg 用户空间库使用流程视频编解码原理
ffmpeg 的处理流程 上面这张图展示了 ffmpeg 的处理流程
输入源 - 解复用 - 解码成帧 - 执行各种操作如缩放、旋转等 - 编码 - 复用 - 输出
使用 ffmpeg-rochip 的好处
传统的使用硬件编解码的开发思路是使用 ffmpeg 获取视频流然后用 MPP 库进行硬件编解码最后再传给 ffmpeg 进行复用生成容器文件或推流。这样做的缺点是整个开发成本较高需要学习 ffmpeg还要学习 MPP库。
而现在有了 ffmpeg-rochip 之后我们可以省略去学习使用 MPP 库的步骤因为这个库已经帮我们封装好了 MPP 的功能我们只需要像之前那样使用 ffmpeg 即可只需在使用编解码器时换成 xxx_rkmpp比如 h264_rkmpp。这样做的好处就是大大降低我们的开发学习成本。
编写思路
整个编写思路和我们日常编写 ffmpeg 时的思路是一致的ffmpeg-rockchip 只是在 ffmpeg 的基础上封装了 MPP 和 RGA 的 api实现了对应编解码器和过滤器使得我们可以直接使用 ffmpeg 的 api 就能直接调用 MPP 和 RGA 功能。
下面的 demo使用 cpp 语言实现”读取 MP4 文件使用 MPP 的 h264 进行硬件解码再使用 MPP 的 H265 进行硬件编码后输出 output.hevc 文件“的功能。
编写思路如下
初始化各种上下文读取当前目录下的 test.mp4 文件进行解复用获取视频流使用 h264_rkmpp 解码器对视频帧进行硬解码将解码后的视频帧使用 hevc_rkmpp 编码器进行硬编码将编码的视频帧写入 output.hevc 文件中
#include csignal
#include iostream
#include stdio.h
#include stdlib.hextern C {
#include libavcodec/avcodec.h
#include libavformat/avformat.h
#include libavutil/opt.h
#include libavutil/pixfmt.h
}#define MP4_PATH ./test.mp4
#define OUTPUT_FILENAME ./output.hevc
#define DECODEC_NAME h264_rkmpp
#define ENCODEC_NAME hevc_rkmppstatic const AVInputFormat *input_format;
static AVStream *video_in_stream;
static int video_in_stream_idx -1;
static const AVCodec *rk_h264_decodec;
static const AVCodec *rk_hevc_encodec;
static AVCodecContext *rk_decodec_ctx nullptr;
static AVCodecContext *rk_encodec_ctx nullptr;
static AVFormatContext *mp4_fmt_ctx nullptr;
static FILE *ouput_file;
static AVFrame *frame;
static AVPacket *mp4_video_pkt;
static AVPacket *hevc_pkt;static void encode(AVFrame *frame, AVPacket *hevc_pkt, FILE *outfile) {int ret;if (frame)printf(Send frame %3 PRId64 \n, frame-pts);ret avcodec_send_frame(rk_encodec_ctx, frame);if (ret 0) {char errbuf[AV_ERROR_MAX_STRING_SIZE];av_strerror(ret, errbuf, sizeof(errbuf));std::cerr Error sending a frame for encoding: errbuf std::endl;exit(1);}while (ret 0) {ret avcodec_receive_packet(rk_encodec_ctx, hevc_pkt);if (ret AVERROR(EAGAIN) || ret AVERROR_EOF)return;else if (ret 0) {char errbuf[AV_ERROR_MAX_STRING_SIZE];av_strerror(ret, errbuf, sizeof(errbuf));std::cerr Error during encoding: errbuf std::endl;exit(1);}printf(Write packet %3 PRId64 (size%5d)\n, hevc_pkt-pts,hevc_pkt-size);fwrite(hevc_pkt-data, 1, hevc_pkt-size, outfile);av_frame_unref(frame);av_packet_unref(hevc_pkt);}
}static void decode(AVPacket *mp4_video_pkt, AVFrame *frame) {int ret;ret avcodec_send_packet(rk_decodec_ctx, mp4_video_pkt);if (ret 0) {char errbuf[AV_ERROR_MAX_STRING_SIZE];av_strerror(ret, errbuf, sizeof(errbuf));std::cerr Error sending a frame for decoding: errbuf std::endl;exit(1);}while (ret 0) {ret avcodec_receive_frame(rk_decodec_ctx, frame);if (ret AVERROR(EAGAIN) || ret AVERROR_EOF) {return;} else if (ret 0) {char errbuf[AV_ERROR_MAX_STRING_SIZE];av_strerror(ret, errbuf, sizeof(errbuf));std::cerr Error during decoding: errbuf std::endl;exit(1);}encode(frame, hevc_pkt, ouput_file);}
}int main(int argc, char **argv) {int ret;input_format av_find_input_format(mp4);if (!input_format) {std::cerr Could not find input format std::endl;return EXIT_FAILURE;}// 分配一个AVFormatContext。mp4_fmt_ctx avformat_alloc_context();if (!mp4_fmt_ctx) {std::cerr Could not allocate format context std::endl;return EXIT_FAILURE;}// 打开输入流并读取头部信息。此时编解码器尚未开启。if (avformat_open_input(mp4_fmt_ctx, MP4_PATH, input_format, nullptr) 0) {std::cerr Could not open input std::endl;return EXIT_FAILURE;}// 读取媒体文件的数据包以获取流信息。if (avformat_find_stream_info(mp4_fmt_ctx, nullptr) 0) {std::cerr Could not find stream info std::endl;return EXIT_FAILURE;}// 打印视频信息av_dump_format(mp4_fmt_ctx, 0, MP4_PATH, 0);// 查找视频流if ((ret av_find_best_stream(mp4_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1,nullptr, 0)) 0) {std::cerr Could not find video stream std::endl;return EXIT_FAILURE;}video_in_stream_idx ret;video_in_stream mp4_fmt_ctx-streams[video_in_stream_idx];std::cout video_in_stream-duration: video_in_stream-duration std::endl;const char *filename OUTPUT_FILENAME;int i 0;// 查找解码器rk_h264_decodec avcodec_find_decoder_by_name(DECODEC_NAME);if (!rk_h264_decodec) {std::cerr Codec DECODEC_NAME not found std::endl;exit(1);}rk_decodec_ctx avcodec_alloc_context3(rk_h264_decodec);if (!rk_decodec_ctx) {std::cerr Could not allocate video rk_h264_decodec context std::endl;exit(1);}// 将视频参数复制到rk_h264_decodec上下文中。if (avcodec_parameters_to_context(rk_decodec_ctx, video_in_stream-codecpar) 0) {std::cerr Could not copy video parameters to rk_h264_decodec context std::endl;exit(1);}AVDictionary *opts NULL;av_dict_set_int(opts, buf_mode, 1, 0);if (avcodec_open2(rk_decodec_ctx, rk_h264_decodec, opts) 0) {std::cerr Could not open rk_h264_decodec std::endl;exit(1);}// 查找编码器rk_hevc_encodec avcodec_find_encoder_by_name(ENCODEC_NAME);if (!rk_hevc_encodec) {std::cerr Codec ENCODEC_NAME not found std::endl;exit(1);}rk_encodec_ctx avcodec_alloc_context3(rk_hevc_encodec);if (!rk_encodec_ctx) {std::cerr Could not allocate video rk_hevc_encodec context std::endl;exit(1);}// 设置编码器参数rk_encodec_ctx-width video_in_stream-codecpar-width;rk_encodec_ctx-height video_in_stream-codecpar-height;rk_encodec_ctx-pix_fmt AV_PIX_FMT_NV12;rk_encodec_ctx-time_base video_in_stream-time_base;rk_encodec_ctx-framerate video_in_stream-r_frame_rate;rk_encodec_ctx-gop_size 50;rk_encodec_ctx-bit_rate 1024 * 1024 * 10;av_opt_set(rk_encodec_ctx-priv_data, profile, main, 0);av_opt_set(rk_encodec_ctx-priv_data, qp_init, 23, 0);av_opt_set_int(rk_encodec_ctx-priv_data, rc_mode, 0, 0);ret avcodec_open2(rk_encodec_ctx, rk_hevc_encodec, nullptr);if (ret 0) {std::cerr Could not open rk_hevc_encodec: std::endl;exit(1);}mp4_video_pkt av_packet_alloc();if (!mp4_video_pkt)exit(1);hevc_pkt av_packet_alloc();if (!hevc_pkt)exit(1);ouput_file fopen(filename, wb);if (!ouput_file) {std::cerr Could not open filename std::endl;exit(1);}frame av_frame_alloc();if (!frame) {std::cerr Could not allocate video frame std::endl;exit(1);}while (true) {ret av_read_frame(mp4_fmt_ctx, mp4_video_pkt);if (ret 0) {std::cerr Could not read frame std::endl;break;}if (mp4_video_pkt-stream_index video_in_stream_idx) {std::cout mp4_video_pkt-pts: mp4_video_pkt-pts std::endl;decode(mp4_video_pkt, frame);}av_packet_unref(mp4_video_pkt);i;}// 确保将所有帧写入av_packet_unref(mp4_video_pkt);decode(mp4_video_pkt, frame);encode(nullptr, mp4_video_pkt, ouput_file);fclose(ouput_file);avcodec_free_context(rk_encodec_ctx);avformat_close_input(mp4_fmt_ctx);avformat_free_context(mp4_fmt_ctx);av_frame_free(frame);av_packet_free(mp4_video_pkt);av_packet_free(hevc_pkt);return 0;
}将上面的代码放入 main.cpp 中将 test.mp4 文件放入当前目录在开发板中运行如下命令编译并运行
g -o main main.cpp -lavformat -lavcodec -lavutil./main确保你的 rk 开发板环境中有 ffmpeg-rockchip 库如果没有的可以参考我上篇文章的编译教程《瑞芯微 RK 系列 RK3588 使用 ffmpeg-rockchip 实现 MPP 硬件编解码和 RGA 图形加速-命令版》
查看 VPU 的运行情况如下说明成功使用了硬件编解码功能。如果不知道怎么查看 VPU 的运行情况可以参考我这篇文章《瑞芯微 RK 系列 RK3588 CPU、GPU、NPU、VPU、RGA、DDR 状态查看与操作》。 优化点
以上的代码示例有个缺点就是解码时会将视频帧上传到 VPU之后传回内存编码时又上传到 VPU编码后再传回内存。这样就造成了不必要的数据拷贝我们可以将视频帧解码之后在 VPU 编码后再传回内存提高编解码效率。
实现方案是使用 hw_device_ctx 上下文由于篇幅问题这里不给出代码示例。有需要的小伙伴可以在评论区回复或直接私聊我。
结语
本篇文章介绍了如何使用 ffmpeg-rockchip 进行 MPP 硬件编解码在下一篇文章我将介绍如何使用 ffmpeg-rockchip 使用 RGA 2D 图形加速RGA 可以实现图像缩放、旋转、bitBlt、alpha混合等常见的2D图形操作。
如果觉得本文写得不错请麻烦帮忙点赞、收藏、转发你的支持是我继续写作的动力。我是 Leon_Chenl我们下篇文章见~