说来也奇怪,最近RK3588的用户扎堆,近期连续远程了七八个板子都是3588,有firefly的,有野火鲁班猫的,有正点原子的等,总体都大差不差,通过各种性能对比测试,鲁班猫性能更突出。在我们普通的PC机器电脑,播放一个8K,直接GPU干满,除非显卡强劲,普通的显卡一般就只能支撑1路265格式的8K播放,以为这就是极限了,没想到在小小的嵌入式板子上,播放8K也是这么的流畅,不愧是专有的RKMPP硬解方案,这还不是极限,开了4路8K,居然也行,绝对的震惊,颠覆了我的认知,这才几百块的核心板,这么凶残吗?

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
下面是一个使用纯 Qt 和 FFmpeg 实现 Rockchip MPP (RKMPP) 硬件解码的示例代码。这个示例展示了如何初始化 FFmpeg 并使用 RKMPP 进行硬件解码。
准备工作确保已安装 FFmpeg 并启用了 RKMPP 支持确保 Qt 开发环境已配置好需要在 Rockchip 平台上运行(如 RK3399, RK3588 等)完整代码实现 #include <QCoreApplication>#include <QDebug>#include <QImage>#include <QFile>extern "C" {#include <libavcodec/avcodec.h>#include <libavformat/avformat.h>#include <libavutil/imgutils.h>#include <libavutil/opt.h>#include <libavutil/hwcontext.h>#include <libavutil/hwcontext_drm.h>}class RKMPPDecoder {public: RKMPPDecoder() : fmt_ctx(nullptr), video_dec_ctx(nullptr), hw_device_ctx(nullptr) {} ~RKMPPDecoder() { cleanup(); } bool init(const QString &filePath) { // 注册所有 FFmpeg 组件 avformat_network_init(); avdevice_register_all(); // 打开输入文件 if (avformat_open_input(&fmt_ctx, filePath.toUtf8().constData(), nullptr, nullptr) < 0) { qWarning() << "Could not open source file" << filePath; return false; } // 获取流信息 if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) { qWarning() << "Could not find stream information"; return false; } // 查找视频流 int ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0); if (ret < 0) { qWarning() << "Could not find video stream in input file"; return false; } video_stream_idx = ret; AVStream *video_stream = fmt_ctx->streams[video_stream_idx]; // 查找硬件解码器 const AVCodec *decoder = nullptr; if (video_stream->codecpar->codec_id == AV_CODEC_ID_H264) { decoder = avcodec_find_decoder_by_name("h264_rkmpp"); } else if (video_stream->codecpar->codec_id == AV_CODEC_ID_HEVC) { decoder = avcodec_find_decoder_by_name("hevc_rkmpp"); } else if (video_stream->codecpar->codec_id == AV_CODEC_ID_VP9) { decoder = avcodec_find_decoder_by_name("vp9_rkmpp"); } if (!decoder) { qWarning() << "Failed to find RKMPP decoder for codec" << avcodec_get_name(video_stream->codecpar->codec_id); return false; } // 创建解码器上下文 video_dec_ctx = avcodec_alloc_context3(decoder); if (!video_dec_ctx) { qWarning() << "Failed to allocate decoder context"; return false; } // 复制流参数到解码器上下文 if (avcodec_parameters_to_context(video_dec_ctx, video_stream->codecpar) < 0) { qWarning() << "Failed to copy codec parameters to decoder context"; return false; } // 设置硬件加速 if (initHardware() < 0) { qWarning() << "Failed to initialize hardware decoder"; return false; } // 打开解码器 if (avcodec_open2(video_dec_ctx, decoder, nullptr) < 0) { qWarning() << "Failed to open codec"; return false; } return true; } void decodeFrames() { AVPacket *pkt = av_packet_alloc(); AVFrame *frame = av_frame_alloc(); AVFrame *sw_frame = av_frame_alloc(); int ret = 0; if (!pkt || !frame || !sw_frame) { qWarning() << "Failed to allocate packet or frame"; goto end; } // 读取并解码帧 while (av_read_frame(fmt_ctx, pkt) >= 0) { if (pkt->stream_index == video_stream_idx) { ret = avcodec_send_packet(video_dec_ctx, pkt); if (ret < 0) { qWarning() << "Error sending packet to decoder"; continue; } while (ret >= 0) { ret = avcodec_receive_frame(video_dec_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { qWarning() << "Error during decoding"; break; } // 处理硬件帧 if (frame->format == AV_PIX_FMT_DRM_PRIME) { // 从硬件帧获取 DRM 描述符 AVDRMFrameDescriptor *drm_desc = (AVDRMFrameDescriptor *)frame->data[0]; // 这里可以添加处理 DRM 帧的代码 // 例如使用 Qt 的 EGL 或直接渲染到屏幕 qDebug() << "Got DRM PRIME frame, width:" << frame->width << "height:" << frame->height; } else { // 如果不是硬件帧,可能是软件回退 qDebug() << "Got software frame, format:" << frame->format; } av_frame_unref(frame); } } av_packet_unref(pkt); } end: av_packet_free(&pkt); av_frame_free(&frame); av_frame_free(&sw_frame); }private: int initHardware() { int ret = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_DRM, nullptr, nullptr, 0); if (ret < 0) { qWarning() << "Failed to create DRM HW device context"; return ret; } video_dec_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); if (!video_dec_ctx->hw_device_ctx) { qWarning() << "Failed to reference HW device context"; return -1; } // 设置像素格式为 DRM PRIME video_dec_ctx->pix_fmt = AV_PIX_FMT_DRM_PRIME; video_dec_ctx->get_format = get_hw_format; return 0; } static enum AVPixelFormat get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) { const enum AVPixelFormat *p; for (p = pix_fmts; *p != -1; p++) { if (*p == AV_PIX_FMT_DRM_PRIME) { return *p; } } qWarning() << "Failed to get HW surface format"; return AV_PIX_FMT_NONE; } void cleanup() { if (video_dec_ctx) { avcodec_free_context(&video_dec_ctx); video_dec_ctx = nullptr; } if (fmt_ctx) { avformat_close_input(&fmt_ctx); fmt_ctx = nullptr; } if (hw_device_ctx) { av_buffer_unref(&hw_device_ctx); hw_device_ctx = nullptr; } } AVFormatContext *fmt_ctx; AVCodecContext *video_dec_ctx; AVBufferRef *hw_device_ctx; int video_stream_idx;};int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); if (argc < 2) { qWarning() << "Usage: " << argv[0] << " <input file>"; return -1; } RKMPPDecoder decoder; if (!decoder.init(argv[1])) { return -1; } qDebug() << "Starting decoding..."; decoder.decodeFrames(); qDebug() << "Decoding finished"; return a.exec();}代码说明初始化部分:使用 avformat_open_input 打开输入文件查找视频流并确定合适的 RKMPP 解码器 (h264_rkmpp, hevc_rkmpp 或 vp9_rkmpp)初始化硬件解码上下文硬件加速设置:使用 av_hwdevice_ctx_create 创建 DRM 硬件设备上下文设置像素格式为 AV_PIX_FMT_DRM_PRIME实现 get_hw_format 回调函数解码循环:使用 av_read_frame 读取数据包使用 avcodec_send_packet 和 avcodec_receive_frame 进行解码处理返回的 DRM PRIME 帧资源清理:在析构函数中释放所有 FFmpeg 资源编译说明在 Rockchip 平台上编译此代码时,需要链接以下库:
libavcodeclibavformatlibavutillibavdevice典型的编译命令:
g++ -o rkmpp_decoder rkmpp_decoder.cpp \ -I/usr/include/qt \ -I/usr/include/qt/QtCore \ -lQt5Core \ -lavcodec -lavformat -lavutil -lavdevice注意事项此代码需要在 Rockchip 平台上运行,且系统已正确配置 RKMPP 驱动和 FFmpeg 支持解码后的帧是 DRM PRIME 格式,需要额外的代码来渲染或转换为 Qt 可用的格式实际应用中可能需要添加更多的错误处理和状态检查如果需要将解码后的帧显示在 Qt 窗口中,可以考虑使用 Qt 的 EGL 或 OpenGL 集成来直接渲染 DRM PRIME 帧,或者将帧转换为 RGB 格式后使用 QImage/QPixmap 显示。