逆天Qt/在嵌入式板子上同時播放4路8K視頻/硬解碼GPU繪制/RK3588性能太兇殘
說來也奇怪,最近RK3588的用戶扎堆,近期連續(xù)遠(yuǎn)程了七八個板子都是3588,有firefly的,有野火魯班貓的,有正點(diǎn)原子的等,總體都大差不差,通過各種性能對比測試,魯班貓性能更突出。在我們普通的PC機(jī)器電腦,播放一個8K,直接GPU干滿,除非顯卡強(qiáng)勁,普通的顯卡一般就只能支撐1路265格式的8K播放,以為這就是極限了,沒想到在小小的嵌入式板子上,播放8K也是這么的流暢,不愧是專有的RKMPP硬解方案,這還不是極限,開了4路8K,居然也行,絕對的震驚,顛覆了我的認(rèn)知,這才幾百塊的核心板,這么兇殘嗎?



下面是一個使用純 Qt 和 FFmpeg 實(shí)現(xiàn) Rockchip MPP (RKMPP) 硬件解碼的示例代碼。這個示例展示了如何初始化 FFmpeg 并使用 RKMPP 進(jìn)行硬件解碼。
準(zhǔn)備工作
- 確保已安裝 FFmpeg 并啟用了 RKMPP 支持
- 確保 Qt 開發(fā)環(huán)境已配置好
- 需要在 Rockchip 平臺上運(yùn)行(如 RK3399, RK3588 等)
完整代碼實(shí)現(xiàn)
#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;
}
// 創(chuàng)建解碼器上下文
video_dec_ctx = avcodec_alloc_context3(decoder);
if (!video_dec_ctx) {
qWarning() << "Failed to allocate decoder context";
return false;
}
// 復(fù)制流參數(shù)到解碼器上下文
if (avcodec_parameters_to_context(video_dec_ctx, video_stream->codecpar) < 0) {
qWarning() << "Failed to copy codec parameters to decoder context";
return false;
}
// 設(shè)置硬件加速
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;
}
// 設(shè)置像素格式為 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)
- 初始化硬件解碼上下文
- 使用
-
硬件加速設(shè)置:
- 使用
av_hwdevice_ctx_create創(chuàng)建 DRM 硬件設(shè)備上下文 - 設(shè)置像素格式為
AV_PIX_FMT_DRM_PRIME - 實(shí)現(xiàn)
get_hw_format回調(diào)函數(shù)
- 使用
-
解碼循環(huán):
- 使用
av_read_frame讀取數(shù)據(jù)包 - 使用
avcodec_send_packet和avcodec_receive_frame進(jìn)行解碼 - 處理返回的 DRM PRIME 幀
- 使用
-
資源清理:
- 在析構(gòu)函數(shù)中釋放所有 FFmpeg 資源
編譯說明
在 Rockchip 平臺上編譯此代碼時,需要鏈接以下庫:
- libavcodec
- libavformat
- libavutil
- libavdevice
典型的編譯命令:
g++ -o rkmpp_decoder rkmpp_decoder.cpp \
-I/usr/include/qt \
-I/usr/include/qt/QtCore \
-lQt5Core \
-lavcodec -lavformat -lavutil -lavdevice
注意事項(xiàng)
- 此代碼需要在 Rockchip 平臺上運(yùn)行,且系統(tǒng)已正確配置 RKMPP 驅(qū)動和 FFmpeg 支持
- 解碼后的幀是 DRM PRIME 格式,需要額外的代碼來渲染或轉(zhuǎn)換為 Qt 可用的格式
- 實(shí)際應(yīng)用中可能需要添加更多的錯誤處理和狀態(tài)檢查
如果需要將解碼后的幀顯示在 Qt 窗口中,可以考慮使用 Qt 的 EGL 或 OpenGL 集成來直接渲染 DRM PRIME 幀,或者將幀轉(zhuǎn)換為 RGB 格式后使用 QImage/QPixmap 顯示。

浙公網(wǎng)安備 33010602011771號