在Rockchip上使用libav(ffmpeg)并调用rkmpp硬件加速。

Rockchip你官方sdk被dcma是hyw啊 如何在Rockchip上安装支持rkmpp加速的ffmpeg。

armbian不管是debian还是ubuntu系统均无法使用rkmpp硬件编码器。我翻看源码发现源码里有相关代码,并且内核日志里这个硬件被正确初始化了,但是就是不能用。

简而言之就是当下你目前必须使用开发版提供的固件(我用的orangepi5)

需要存在以下设备才证明当前编码器可用:

ls /dev/mpp_service

理论上那几个/dev/vide*的设备上M2M的编码器,你可以使用以下命令查看功能:

v4l2-ctl -d /dev/videoX --list-formats

在我这里这个设备无法被使用,说不定以后armbian更新会修复。

简易安装:

最简单的方案是在你的apt源里添加jellyfin的源.

deb [signed-by=/etc/apt/keyrings/jellyfin.gpg] https://repo.jellyfin.org/ubuntu jammy main
apt update && apt install jellyfin-ffmpeg7

你可以通过以下命令来确保ffmpeg正确的支持了硬件加速:

ffmpeg -encoders | grep mpp

正确输出类似于:

V..... h264_rkmpp Rockchip MPP (Media Process Platform) H264 encoder (codec h264) V..... hevc_rkmpp Rockchip MPP (Media Process Platform) HEVC encoder (codec hevc) V..... mjpeg_rkmpp Rockchip MPP (Media Process Platform) MJPEG encoder (codec mjpeg)

但是如果你需要使用libav库进行开发,一切都会变得复杂

编译支持mpp加速的ffmpeg

(我折腾了很久也没有成功的交叉编译ffmpeg,因此这里提供的思路是在开发版上编译ffmpeg,并将sysroot挂载到上位机。在上位机开发应用程序,在上位机通过远程挂载的sysroot交叉编译程序。ffmpeg本身不需要频繁的重新编译因此大体上可以接收)

  1. 安装常用工具包
sudo apt update
sudo apt install -y \
  git build-essential pkg-config cmake meson ninja-build \
  libdrm-dev
  1. 编译rkmpp(这个git仓库似乎是jellyfin fork的mpp库,不是官方库,但是官方的github仓库目前是被DCMA的状态。)
mkdir -p ~/dev && cd ~/dev
git clone -b jellyfin-mpp --depth=1 https://gitee.com/nyanmisaka/mpp.git rkmpp
# 官方git仓库:
# git clone -b jellyfin-mpp --depth=1 https://github.com/rockchip-linux/mpp rkmpp

pushd rkmpp
mkdir rkmpp_build
pushd rkmpp_build

cmake \
  -DCMAKE_INSTALL_PREFIX=/usr \
  -DCMAKE_BUILD_TYPE=Release \
  -DBUILD_SHARED_LIBS=ON \
  -DBUILD_TEST=OFF \
  ..

make -j"$(nproc)"
sudo make install

popd
popd

确认安装成功:

pkg-config --modversion rockchip_mpp 2>/dev/null || echo "pkg-config里没找到rockchip_mpp(不一定致命,但要留意)"
ldconfig -p | grep -i mpp || true
  1. 编译并安装 rkrga(同样用jellyfin给的分支)
mkdir -p ~/dev && cd ~/dev
git clone -b jellyfin-rga --depth=1 https://gitee.com/nyanmisaka/rga.git rkrga

meson setup rkrga rkrga_build \
  --prefix=/usr \
  --libdir=lib \
  --buildtype=release \
  --default-library=shared \
  -Dcpp_args=-fpermissive \
  -Dlibdrm=false \
  -Dlibrga_demo=false

meson configure rkrga_build
sudo ninja -C rkrga_build install

确认安装成功:

ldconfig -p | grep -i rga || true
  1. 编译 ffmpeg-rockchip
mkdir -p ~/dev && cd ~/dev
git clone --depth=1 https://github.com/nyanmisaka/ffmpeg-rockchip.git ffmpeg

cd ffmpeg
# 避免静态链接地狱
./configure --prefix=/usr \
  --enable-gpl --enable-version3 \
  --enable-libdrm --enable-rkmpp \
  --enable-rkrga --enable-shared --disable-static

make -j"$(nproc)"
sudo make install

验证:

ffmpeg -decoders | grep rkmpp
ffmpeg -encoders | grep rkmpp
ffmpeg -filters  | grep rkrga

尝试写一个demo

  1. 挂载sysroot
sshfs orangepi@orangepi5:/ /mnt/rk3588-sysroot -o ro,reconnect,ServerAliveInterval=15

我不怎么想写C/C++,所以我的demo是zig写的,如果你想用C来写也差不多,把build.zig的内容放到CMakeLists.txt里就行了。

// main.zig
const std = @import("std");

const c = @cImport({
    @cInclude("libavcodec/avcodec.h");
    @cInclude("libavutil/avutil.h");
});

const HW_TAGS = [_][]const u8{
    "nvenc",
    "qsv",
    "vaapi",
    "amf",
    "videotoolbox",
    "v4l2m2m",
    "omx",
    "rkmpp",
    "mediacodec",
    "mmal",
};

fn isHwEncoder(name: []const u8) bool {
    var buf: [256]u8 = undefined;

    // std.ascii.lowerString 会 assert(output.len >= input.len),先防一下
    const lower: []const u8 = if (name.len <= buf.len)
        std.ascii.lowerString(&buf, name)
    else
        name; // 超长就退化为原串(一般 codec 名都很短)

    for (HW_TAGS) |tag| {
        if (std.mem.indexOf(u8, lower, tag) != null) return true;
    }
    return false;
}

const Item = struct {
    name: []u8,
    long_name: ?[]u8,
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const alloc = gpa.allocator();

    var iter: ?*anyopaque = null;

    // Zig 0.15+: ArrayList 用 .empty,所有操作显式传 allocator
    var found: std.ArrayList(Item) = .empty;
    defer {
        for (found.items) |it| {
            alloc.free(it.name);
            if (it.long_name) |ln| alloc.free(ln);
        }
        found.deinit(alloc);
    }

    while (true) {
        const codec: ?*const c.AVCodec = c.av_codec_iterate(&iter);
        if (codec == null) break;

        if (c.av_codec_is_encoder(codec) == 0) continue;
        if (codec.?.type != c.AVMEDIA_TYPE_VIDEO) continue;

        const cname = std.mem.span(codec.?.name);
        if (!isHwEncoder(cname)) continue;

        const name_copy = try alloc.dupe(u8, cname);

        var long_copy: ?[]u8 = null;
        if (codec.?.long_name != null) {
            const clong = std.mem.span(codec.?.long_name);
            long_copy = try alloc.dupe(u8, clong);
        }

        try found.append(alloc, .{ .name = name_copy, .long_name = long_copy });
    }

    // Zig 0.15.1+ 新 stdout 写法:提供 buffer + flush
    var stdout_buffer: [4096]u8 = undefined;
    var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
    const out = &stdout_writer.interface;

    if (found.items.len == 0) {
        try out.print("没有检测到硬件视频编码器(来自已链接的 libavcodec)。\n", .{});
        try out.flush();
        return;
    }

    try out.print("当前可用的硬件视频编码器(来自 libavcodec):\n", .{});
    for (found.items) |it| {
        if (it.long_name) |ln| {
            try out.print("- {s}: {s}\n", .{ it.name, ln });
        } else {
            try out.print("- {s}\n", .{it.name});
        }
    }

    try out.flush();
}
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const sysroot_opt = b.option([]const u8, "sysroot", "Sysroot path (e.g. /mnt/rk3588)");

    const mod = b.createModule(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    const exe = b.addExecutable(.{
        .name = "hwenc_list",
        .root_module = mod,
    });

    // 你项目里有 @cImport + @cInclude,需要 libc
    exe.linkLibC(); // 官方文档/示例做法 :contentReference[oaicite:1]{index=1}

    // 如果提供了 sysroot,就把头文件/库路径加进去
    if (sysroot_opt) |sysroot| {
        // ---- headers: IMPORTANT (multiarch first) ----
        mod.addIncludePath(.{ .cwd_relative = b.fmt("{s}/usr/include/aarch64-linux-gnu", .{sysroot}) });
        mod.addIncludePath(.{ .cwd_relative = b.fmt("{s}/usr/include", .{sysroot}) });
        mod.addIncludePath(.{ .cwd_relative = b.fmt("{s}/include", .{sysroot}) });

        // ---- libraries: put multiarch first ----
        exe.addLibraryPath(.{ .cwd_relative = b.fmt("{s}/usr/lib/aarch64-linux-gnu", .{sysroot}) });
        exe.addLibraryPath(.{ .cwd_relative = b.fmt("{s}/lib/aarch64-linux-gnu", .{sysroot}) });
        exe.addLibraryPath(.{ .cwd_relative = b.fmt("{s}/usr/lib", .{sysroot}) });
        exe.addLibraryPath(.{ .cwd_relative = b.fmt("{s}/lib", .{sysroot}) });
    }

    // 链接 FFmpeg / libav(官方“Linking to System Libraries”示例就是 exe.linkSystemLibrary) :contentReference[oaicite:2]{index=2}
    exe.linkSystemLibrary("avcodec");
    exe.linkSystemLibrary("avutil");

    // 常见依赖(缺啥报错再补)
    exe.linkSystemLibrary("z");
    exe.linkSystemLibrary("m");
    exe.linkSystemLibrary("pthread");
    exe.linkSystemLibrary("dl");

    b.installArtifact(exe);

    // 可选:zig build run
    const run_cmd = b.addRunArtifact(exe);
    if (b.args) |args| run_cmd.addArgs(args);
    b.step("run", "Run the app").dependOn(&run_cmd.step);
}

编译程序:

zig build -Dtarget=aarch64-linux-gnu -Dsysroot=/mnt/rk3588-sysroot

连接上开发版的ssh并运行:

./hwenc_list

输出:

当前可用的硬件视频编码器(来自 libavcodec): - h263_v4l2m2m: V4L2 mem2mem H.263 encoder wrapper - h264_v4l2m2m: V4L2 mem2mem H.264 encoder wrapper - h264_rkmpp: Rockchip MPP (Media Process Platform) H264 encoder - hevc_v4l2m2m: V4L2 mem2mem HEVC encoder wrapper - hevc_rkmpp: Rockchip MPP (Media Process Platform) HEVC encoder - mjpeg_rkmpp: Rockchip MPP (Media Process Platform) MJPEG encoder - mpeg4_v4l2m2m: V4L2 mem2mem MPEG4 encoder wrapper - vp8_v4l2m2m: V4L2 mem2mem VP8 encoder wrapper