ffmpeg 初始化配置及基本概念与套路

目录

链接库文件查看

[HEVC 错误之一](#HEVC 错误之一)

[Codec type or id mismatches](#Codec type or id mismatches)

测试文件信息

解决方法

[HEVC 错误之二](#HEVC 错误之二)

[Failed to set config: -6](#Failed to set config: -6)

测试文件信息

调试开关

解决方法

帧的分类

[软件帧(System Memory Frame)](#软件帧(System Memory Frame))

[DRM PRIME 帧(DMA-Buf Frame)](#DRM PRIME 帧(DMA-Buf Frame))

验证编码器支持的帧格式

硬件帧之间的转换流程

滤镜链

[buffersink输出 NV12(软件帧)](#buffersink输出 NV12(软件帧))

[buffersink输出 DRM_PRIME(硬件帧)](#buffersink输出 DRM_PRIME(硬件帧))

滤镜链

总体浏览

代码示例RGA出硬件帧

[1) 创建filter,包括buffersrc,filter,以及buffersink](#1) 创建filter,包括buffersrc,filter,以及buffersink)

[2) 设置input即buffersrc的frame上下文](#2) 设置input即buffersrc的frame上下文)

3) 设置output的格式 设置output的格式)

帧流转代码

context

hw_frames_ctx

解码器类型错误

硬件帧送入编码

格式转换

滤镜链参数BGRA

[eglCreateImageKHR failed](#eglCreateImageKHR failed)

系统资源查看

推流搭建

VLC

mediamtx

线程使用scale_rga接口

总结


链接库文件查看

实际编译时链接的静态库文件。

复制代码
root@kylin:~# g++ -o main main.cpp \
>     -I/opt/ffmpeg-6.1/include \
>     -L/opt/ffmpeg-6.1/lib \
>     -lavfilter -lavformat -lavcodec -lswresample -lswscale -lpostproc -lavutil \
>     -lrockchip_mpp -lrga -lz -llzma -lbz2 -lm \
>     -lEGL -lGLESv2 -ldrm -lgbm \
>     -lpthread -lstdc++ -std=c++11 \
>     -Wl,--trace 2>&1 | grep -E "libav(codec|format|util)"
/opt/ffmpeg-6.1/lib/libavformat.a
/opt/ffmpeg-6.1/lib/libavcodec.a
/opt/ffmpeg-6.1/lib/libavutil.a

HEVC 错误之一

Codec type or id mismatches

复制代码
./main test.mp4 output.hevc
FFmpeg version: fb515155ca
avcodec version: 3940198
[hevc_rkmpp @ 0x558c9c7e20] Codec type or id mismatches
avcodec_open2 failed

打印输出为:

Stream codec_type: 0

Stream codec_id: 27

测试文件信息

复制代码
ffprobe test.mp4
ffprobe version 4.2.2-1rk5 Copyright (c) 2007-2019 the FFmpeg developers
  built with gcc 9 (Kylin 9.3.0-10kylin5k0.5)
  configuration: --prefix=/usr --extra-version=1rk5 --toolchain=hardened --libdir=/usr/lib/aarch64-linux-gnu --incdir=/usr/include/aarch64-linux-gnu --arch=arm64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-libdrm --enable-librga --enable-rkmpp --enable-version3 --disable-libopenh264 --disable-vaapi --disable-vdpau --disable-decoder=h264_v4l2m2m --disable-decoder=vp8_v4l2m2m --disable-decoder=mpeg2_v4l2m2m --disable-decoder=mpeg4_v4l2m2m --enable-shared --disable-doc
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libavresample   4.  0.  0 /  4.  0.  0
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
  libpostproc    55.  5.100 / 55.  5.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: mp42isomavc1
    creation_time   : 2012-03-13T08:58:06.000000Z
    encoder         : HandBrake 0.9.6 2012022800
  Duration: 00:00:10.03, start: 0.000000, bitrate: 629 kb/s
    Chapter #0:0: start 0.000000, end 10.000000
    Metadata:
      title           : Chapter 1
    Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, smpte170m/smpte170m/bt709), 320x176, 300 kb/s, 25 fps, 25 tbr, 90k tbn, 180k tbc (default)
    Metadata:
      creation_time   : 2012-03-13T08:58:06.000000Z
      encoder         : JVT/AVC Coding
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 160 kb/s (default)
    Metadata:
      creation_time   : 2012-03-13T08:58:06.000000Z
    Stream #0:2(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 160 kb/s
    Metadata:
      creation_time   : 2012-03-13T08:58:06.000000Z
    Stream #0:3(und): Data: bin_data (text / 0x74786574), 0 kb/s
    Metadata:
      creation_time   : 2012-03-13T08:58:06.000000Z
[h264_rkmpp @ 0x55aba55250] Rga alloc buffer[device:/dev/dma_heap/system-uncached-dma32 size:268435456] successfully.

解决方法

复制代码
// ❌ 删掉这行,不要复制
// avcodec_parameters_to_context(encodec_ctx, video_stream->codecpar);

// ✅ 全部手动设置
encodec_ctx->width = 640;
encodec_ctx->height = 360;
encodec_ctx->pix_fmt = AV_PIX_FMT_NV12;
encodec_ctx->time_base = av_make_q(1, 25);
encodec_ctx->framerate = av_make_q(25, 1);
encodec_ctx->bit_rate = 2000000;

或者如下修改,此种修改更能体现出根因。

复制代码
avcodec_parameters_to_context(encodec_ctx, video_stream->codecpar);

// 关键:覆盖编解码器 ID 和类型
encodec_ctx->codec_id = encodec->id;        // 改成 HEVC
encodec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;

// 其他参数按需修改
encodec_ctx->width = 640;
encodec_ctx->height = 360;
encodec_ctx->pix_fmt = AV_PIX_FMT_NV12;

这里avcodec_parameters_to_context(encodec_ctx, video_stream->codecpar);的作用是配置编码的参数,例如格式、码率等。本测试命令的入参文件为264格式编码,输出为265编码。

video_stream->codecpar 为输入文件264的参数配置,和编码265的不一致,故而报错了。

HEVC 错误之二

Failed to set config: -6

复制代码
./main /home/kylin/1600120060fps.ts  output.hevc
Stream codec_type: 0
Stream codec_id: 173
[hevc_rkmpp @ 0x55ae1bc440] Failed to set config: -6
avcodec_open2 failed

测试文件信息

复制代码
ffprobe /home/kylin/1600120060fps.ts

Stream #0:0[0x100]: Video: hevc (Main) (HEVC / 0x43564548), yuvj420p(pc), 1600x1200, 60 fps, 60 tbr, 90k tbn, 60 tbc
265的文件。

调试开关

复制代码
av_log_set_level(AV_LOG_DEBUG);

代码种增加上述后,可以打开debug信息输出,具体信息如下:

复制代码
mpegts @ 0x5589f80a40] After avformat_find_stream_info() pos: 0 bytes read:1881072 seeks:4 frames:32
Stream codec_type: 0
Stream codec_id: 173
[hevc @ 0x5589fcdc70] nal_unit_type: 32(VPS), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x5589fcdc70] nal_unit_type: 33(SPS), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x5589fcdc70] nal_unit_type: 34(PPS), nuh_layer_id: 0, temporal_id: 0
[hevc @ 0x5589fcdc70] Decoding VPS
[hevc @ 0x5589fcdc70] Main profile bitstream
[hevc @ 0x5589fcdc70] Decoding SPS
[hevc @ 0x5589fcdc70] Main profile bitstream
[hevc @ 0x5589fcdc70] Decoding VUI
[hevc @ 0x5589fcdc70] Decoding PPS
Supported pixel formats: gray yuv420p yuvj420p yuv422p yuvj422p yuv444p yuvj444p nv12 nv21 nv16 nv24 yuyv422 yvyu422 uyvy422 rgb24 bgr24 rgba rgb0 bgra bgr0 argb 0rgb abgr 0bgr drm_prime
[hevc_rkmpp @ 0x5589fcc440] Rate Control mode is set to CBR
[hevc_rkmpp @ 0x5589fcc440] Bitrate Target/Min/Max is set to 0/0/0
[hevc_rkmpp @ 0x5589fcc440] QP Init/Max/Min/Max_I/Min_I is set to 26/48/0/48/0
[hevc_rkmpp @ 0x5589fcc440] Profile is set to MAIN
[hevc_rkmpp @ 0x5589fcc440] Level is set to 0
[hevc_rkmpp @ 0x5589fcc440] Failed to set config: -6
avcodec_open2 failed
复制代码
Bitrate Target/Min/Max is set to 0/0/0  比特率是 0,硬件编码器不接受这个值

解决方法

复制代码
encodec_ctx->bit_rate = 2000000;  

帧的分类

以下由AI汇总,实测也准确。

类别 FFmpeg 格式标识 数据存在哪里 CPU 能否直接读 典型用途
软件帧 AV_PIX_FMT_YUV420PNV12BGRA... 系统内存(RAM) ✅ 可以 软解、滤镜处理、编码输入
DRM_PRIME 帧 AV_PIX_FMT_DRM_PRIME DMA-Buf(内核管理的物理连续内存) ❌ 不能直接读(可 mmap) 硬解输出、zero-copy 显示
通用硬件帧 AV_PIX_FMT_NONE + hw_frames_ctx GPU/VPU 设备内存 ❌ 不能 VAAPI、CUDA、Vulkan 等框架
RGA 硬件帧 AV_PIX_FMT_NONE + hw_frames_ctx RGA 专用内存/MPP buffer ❌ 不能 scale_rkrga 滤镜的输入输出
EGLImage 帧 非 FFmpeg 概念 GPU 显存(通过 EGLImageKHR 引用) ❌ 不能 OpenGL ES 纹理、渲染

软件帧(System Memory Frame)

最普通的帧,像素存在 frame->data[0/1/2] 指向的内存里。

cpp

复制代码
frame->format == AV_PIX_FMT_NV12;  // 或 YUV420P、BGRA 等
frame->data[0];    // Y 平面指针
frame->data[1];    // UV 平面指针

特点:CPU 随便读写,但数据量大时内存带宽压力大。

DRM PRIME 帧(DMA-Buf Frame)

硬件解码器直接把数据写到 DMA-Buf 里,FFmpeg 用 AVDRMFrameDescriptor 描述它:

复制代码
frame->format == AV_PIX_FMT_DRM_PRIME;
AVDRMFrameDescriptor* desc = (AVDRMFrameDescriptor*)frame->data[0];
desc->objects[0].fd;        // DMA-Buf 文件描述符
desc->layers[0].format;     // fourcc,如 NV12、NV16

特点

  • CPU 不能直接读 像素(除非 mmap

  • 可以零拷贝送给 DRM/KMS、EGL、Wayland、V4L2

  • hevc_rkmpp 解码器默认输出这种帧

验证编码器支持的帧格式

复制代码
std::cout << "=== Check if encoder supports DRM_PRIME ===" << std::endl;
const enum AVPixelFormat *pix_fmts = encodec->pix_fmts;
if (pix_fmts) {
    bool supports_drm = false;
    for (int i = 0; pix_fmts[i] != AV_PIX_FMT_NONE; i++) {
        std::cout << av_get_pix_fmt_name(pix_fmts[i]) << " ";
        if (pix_fmts[i] == AV_PIX_FMT_DRM_PRIME) supports_drm = true;
    }
    std::cout << std::endl;
    std::cout << "Supports DRM_PRIME: " << (supports_drm ? "✅ YES" : "❌ NO") << std::endl;
}

输出格式如下:

复制代码
Supported pixel formats: gray yuv420p yuvj420p yuv422p yuvj422p yuv444p yuvj444p nv12 nv21 nv16 nv24 yuyv422 yvyu422 uyvy422 rgb24 bgr24 rgba rgb0 bgra bgr0 argb 0rgb abgr 0bgr drm_prime
分类 说明 对应像素格式
系统内存帧 (System Memory) 数据在普通 RAM,CPU 可直接访问 AV_PIX_FMT_YUV420PAV_PIX_FMT_NV12AV_PIX_FMT_RGB24
硬件内存帧 (Hardware Memory) 数据在 GPU/显存/专用硬件内存,CPU 不能直接访问 AV_PIX_FMT_DRM_PRIMEAV_PIX_FMT_CUDAAV_PIX_FMT_VAAPIAV_PIX_FMT_VDPAUAV_PIX_FMT_D3D11AV_PIX_FMT_MEDIACODEC

在 FFmpeg 中,一个帧的完整类型由 AVFrame->format 这个字段决定,它是一个 enum AVPixelFormat 枚举值。

cpp

复制代码
// 示例
frame->format = AV_PIX_FMT_YUV420P;   // 软件帧
frame->format = AV_PIX_FMT_DRM_PRIME; // 硬件帧
frame->format = AV_PIX_FMT_CUDA;      // NVIDIA 硬件帧

硬件帧之间的转换流程

解码 → 硬件帧(DRM_PRIME) → hwupload → 硬件滤镜(scale_rkrga) → hwdownload → 软件帧(NV12) → 编码

↑ ↑

数据留在显存 下载回内存

滤镜链

解码帧 → buffersrc → hwuploadscale_rkrga → buffersink → 输出帧(NV12)

这里强制输出 NV12

buffersink输出 NV12(软件帧)

复制代码
解码 → buffersrc → hwupload → scale_rkrga → hwdownload → buffersink → NV12(软件帧)
                                                           ↑
                                           pix_fmts = {NV12}

buffersink输出 DRM_PRIME(硬件帧)

复制代码
解码 → buffersrc → hwupload → scale_rkrga → buffersink → DRM_PRIME(硬件帧)
                                                           ↑
                                           pix_fmts = {DRM_PRIME}

落实到具体代码上的差异:

软件帧

硬件帧

复制代码
 enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_DRM_PRIME, AV_PIX_FMT_NONE};
av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,
                        AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);

//滤镜链里面删除hwdownload
const char *filter_descr = "hwupload,scale_rkrga=w=640:h=360:format=nv12";

滤镜链

总体浏览

BOM Management and PCB | Mermaid Chart

组件 全称 角色 说明
解码输出 frame 数据源 解码器解出来的原始帧(可能是 YUV、NV12 或 DRM_PRIME)
buffersrc Buffer Source 入口 滤镜图的"入口",把解码帧"喂"进滤镜图
滤镜链 Filter Chain 处理 中间的处理环节(缩放、格式转换、硬件上传/下载等)
buffersink Buffer Sink 出口 滤镜图的"出口",从滤镜图"取"出处理后的帧
编码输入 filt_frame 数据消费者 从 buffersink 取出的帧,喂给编码器

代码示例RGA出硬件帧

1) 创建filter,包括buffersrc,filter,以及buffersink

复制代码
 const AVFilter* buffersrc = avfilter_get_by_name("buffer");
    const AVFilter* buffersink = avfilter_get_by_name("buffersink");
    
    AVFilterInOut* outputs = avfilter_inout_alloc();
    AVFilterInOut* inputs = avfilter_inout_alloc();
    
    char filter_descr[256];
    
    AVBufferRef* hw_frames_ctx = nullptr;
    AVHWFramesContext* frames_ctx = nullptr;
    AVBufferSrcParameters* params = nullptr;
    AVFilterContext* scale_ctx = nullptr;

    snprintf(filter_descr, sizeof(filter_descr),
             "scale_rkrga=w=%d:h=%d:format=bgra",
             dst_width, dst_height);
    
    std::cout << "Filter: " << filter_descr << std::endl;
    
    filter_graph = avfilter_graph_alloc();
    if (!filter_graph || !outputs || !inputs) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    
    snprintf(args, sizeof(args),
             "video_size=%dx%d:pix_fmt=%d:time_base=1/90000",
             src_width, src_height, AV_PIX_FMT_DRM_PRIME);
    
    ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
                                       args, NULL, filter_graph);
    if (ret < 0) goto end;
    
    ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
                                       NULL, NULL, filter_graph);
    if (ret < 0) goto end;
    
  //  enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_DRM_PRIME, AV_PIX_FMT_NONE};
    av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,
                        AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
    
    outputs->name = av_strdup("in");
    outputs->filter_ctx = buffersrc_ctx;
    outputs->pad_idx = 0;
    outputs->next = NULL;
    
    inputs->name = av_strdup("out");
    inputs->filter_ctx = buffersink_ctx;
    inputs->pad_idx = 0;
    inputs->next = NULL;
    
    ret = avfilter_graph_parse_ptr(filter_graph, filter_descr,
                                   &inputs, &outputs, NULL);
    if (ret < 0) {
        std::cout << "avfilter_graph_parse_ptr failed" << std::endl;
        goto end;
    }

2) 设置input即buffersrc的frame上下文

复制代码
 hw_frames_ctx = av_hwframe_ctx_alloc(hw_device_ctx);
    if (!hw_frames_ctx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    
     frames_ctx = (AVHWFramesContext*)hw_frames_ctx->data;
    frames_ctx->format = AV_PIX_FMT_DRM_PRIME;
    frames_ctx->sw_format = AV_PIX_FMT_NV12;
    frames_ctx->width = src_width;
    frames_ctx->height = src_height;
    frames_ctx->initial_pool_size = 16;
    
    ret = av_hwframe_ctx_init(hw_frames_ctx);
    if (ret < 0) {
        std::cout << "av_hwframe_ctx_init failed" << std::endl;
        goto end;
    }
    
     params = av_buffersrc_parameters_alloc();
    params->format = AV_PIX_FMT_DRM_PRIME;
    params->width = src_width;
    params->height = src_height;
    params->time_base = av_make_q(1, 90000);
    params->hw_frames_ctx = av_buffer_ref(hw_frames_ctx);
    
    ret = av_buffersrc_parameters_set(buffersrc_ctx, params);
    av_free(params);
    if (ret < 0) goto end;

3) 设置output的格式

复制代码
enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_DRM_PRIME, AV_PIX_FMT_NONE};
    av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,
                        AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);

帧流转代码

复制代码
// 1. 解码输出 (AVFrame *frame)
avcodec_receive_frame(decodec_ctx, frame);

// 2. 推入 buffersrc (入口)
av_buffersrc_write_frame(buffersrc_ctx, frame);

// 3. 滤镜链处理 (自动)
// hwupload,scale_rkrga=w=640:h=360:format=nv12

// 4. 从 buffersink 取出 (出口)
av_buffersink_get_frame(buffersink_ctx, filt_frame);

// 5. 编码输入
avcodec_send_frame(encodec_ctx, filt_frame);

context

hw_frames_ctx

hw_frames_ctx硬件帧池的描述符,它描述了一块已经分配好的硬件内存池。

复制代码
struct AVHWFramesContext {
    AVBufferRef *device_ctx;  // 指向哪个硬件设备
    enum AVPixelFormat format; // 硬件帧格式 (DRM_PRIME/CUDA/VAAPI...)
    enum AVPixelFormat sw_format; // 对应的软件格式 (NV12/YUV420P...)
    int width, height;         // 帧大小
    int initial_pool_size;     // 内存池大小
    // ...
};

上述帧ctx流程

复制代码
解码器 → hw_frames_ctx A (1600x1200)
   ↓
scale_rkrga → 创建 hw_frames_ctx B (480x360) ← 新池子!
   ↓
编码器 → 又创建或使用另一个

加上 上面章节中,不设置input的frame上下文,则在采用硬件帧时报如下错误:

复制代码
Encoder pix_fmt before open: nv12 (23)
args: video_size=1600x1200:pix_fmt=179:time_base=1/90000:pixel_aspect=0/1
avfilter_graph_parse_ptr success
Found scale_rkrga filter, setting hw_device_ctx
nb_filters: 3
+----------+
|    in    |default--[0x0 0:0 ?]--Parsed_scale_rkrga_0:default
| (buffer) |
+----------+

                                                  +--------------+
Parsed_scale_rkrga_0:default--[0x0 0:0 ?]--default|     out      |
                                                  | (buffersink) |
                                                  +--------------+

                                +----------------------+
in:default--[0x0 0:0 ?]--default| Parsed_scale_rkrga_0 |default--[0x0 0:0 ?]--out:default
                                |    (scale_rkrga)     |
                                +----------------------+


[Parsed_scale_rkrga_0 @ 0x556c292370] No hw context provided on input
[Parsed_scale_rkrga_0 @ 0x556c292370] Failed to configure output pad on Parsed_scale_rkrga_0
avfilter_graph_config failed
init_filters failed

jie 总体而言,此部分功能类似于分配一个内存缓冲池,用于解码端和滤镜端共享处理此帧,而不用拷贝。假设我们自己来实现,也要实现一组buffer及对应的管理。作为解码线程输出帧的存放地,并唤醒RGA线程来消费这一帧,而这共享的buffer管理就可以理解为hw_frames_ctx。

如果不配置此ctx,则默认为软件帧。

解码器类型错误

错误提示:

复制代码
[hevc @ 0x55879ffd50] Error parsing NAL unit #1.
Error while sending a packet to the decoder

解决方法:

主要从流里获取的解码器可能不对,这里直接指定。

复制代码
// ❌ 注释掉原来的
// ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decodec, 0);
// if (ret < 0) {
//     av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file\n");
//     return ret;
// }

// ✅ 手动指定硬件解码器
decodec = avcodec_find_decoder_by_name("hevc_rkmpp");
if (!decodec) {
    std::cout << "hevc_rkmpp not found" << std::endl;
    return -1;
}

// 找到视频流索引
ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (ret < 0) {
    av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file\n");
    return ret;
}
video_stream_index = ret;
video_stream = fmt_ctx->streams[video_stream_index];

硬件帧送入编码

基于目前的配置,采用RGA直出的硬件帧给编码器,最终落盘到文件的数据没有视频。将RGA输出的硬件帧通过下面接口先转换成软件帧则OK。

复制代码
av_hwframe_transfer_data(sw_frame, filt_frame, 0);

格式转换

滤镜链参数BGRA

复制代码
//修改滤镜连参数
scale_rkrga=w=%d:h=%d:format=bgra",

eglCreateImageKHR failed

故障报错及采用的配置参数,注意此时的格式参数写死为: BGRA8888

eglCreateImageKHR failed eglCreateImageKHR failed eglCreateImageKHR failed

复制代码
EGLint attribs[] = {
    EGL_WIDTH, width,
    EGL_HEIGHT, height,
    EGL_LINUX_DRM_FOURCC_EXT, BGRA8888,  
    EGL_DMA_BUF_PLANE0_FD_EXT, fd,
    EGL_DMA_BUF_PLANE0_OFFSET_EXT, offset,
    EGL_DMA_BUF_PLANE0_PITCH_EXT, pitch,
    EGL_NONE
};

实际解码RGA输出的格式

复制代码
// 在 display_drm_frame 中,desc 里已经有格式信息
AVDRMFrameDescriptor* desc = (AVDRMFrameDescriptor*)frame->data[0];

// ✅ 直接使用 desc 里的四字节格式
uint32_t fourcc = desc->layers[0].format;

std::cout << "DRM fourcc: " << std::hex << fourcc << std::dec << std::endl;

DRM fourcc: 34325241
0x34 = '4'
0x32 = '2'
0x52 = 'R'
0x41 = 'A'
→ "42RA" 倒过来是 "AR24" → ARGB8888

格式不对,因而导入就不对了嘛。所以整条链路要确认好格式,而如果做通用型的,则动态获取,不要写死啦。

系统资源查看

复制代码
watch -n 1 cat /sys/kernel/debug/rkrga/load

推流搭建

VLC

复制代码
vlc /home/kylin/1600120060fps.ts --input-repeat=-1 --sout '#rtp{dst=192.168.1.61,port=8554,sdp=rtsp://192.168.1.61:8554/live}'

客户端链接的时候,总是报:461 Unsupported Transport 表示 FFmpeg 和 VLC 的传输协商失败。

mediamtx

复制代码
https://github.com/bluenviron/mediamtx/releases/tag/v1.20.0
./mediamtx &

ffmpeg -re -i /home/kylin/1600120060fps.ts \
       -c copy -f rtsp rtsp://127.0.0.1:8554/live

如果用 -re 推流,ffmpeg 会按文件原始时间戳发送,延迟测量会更准确

如果推流端没有 -re,文件会以最快速度推流,延迟测量不准确

打印时间戳的推流命令

复制代码
ffmpeg -re -i /home/kylin/1600120060fps.ts \
       -c copy -f rtsp rtsp://127.0.0.1:8554/live \
       -debug_ts 2>&1 | grep -E "demuxer|pts"

线程使用scale_rga接口

在主进程中运行上述代码流程,完全没有问题。

而如果把拉流、解码、scale_rga处理放到一个线程中则报如下错误。而采用Timer则也可以正常运行。

复制代码
Filter: scale_rkrga=w=1920:h=1080:format=bgra
rga_api version 1.10.1_[10]
Filter graph configured successfully
 RgaBlit(1507) RGA_BLIT fail: 无效的参数
 RgaBlit(1508) RGA_BLIT fail: 无效的参数
handl-fd-vir-phy-hnd-format[0, 68, (nil), (nil), 0, 2560]
rect[0, 0, 1600, 1200, 1792, 1200, 2560, 0]
f-blend-size-rotation-col-log-mmu[2560, 0, 0, 0, 0, 0, 1]
handl-fd-vir-phy-hnd-format[0, 335, (nil), (nil), 0, 768]
rect[0, 0, 1440, 1080, 1440, 1814, 768, 0]
f-blend-size-rotation-col-log-mmu[768, 0, 0, 0, 0, 0, 1]
[Parsed_scale_rkrga_0 @ 0x7f9c045db0] RGA blit failed: -22
 RgaBlit(1507) RGA_BLIT fail: 无效的参数
 RgaBlit(1508) RGA_BLIT fail: 无效的参数
handl-fd-vir-phy-hnd-format[0, 59, (nil), (nil), 0, 2560]
rect[0, 0, 1600, 1200, 1792, 1200, 2560, 0]
f-blend-size-rotation-col-log-mmu[2560, 0, 0, 0, 0, 0, 1]
handl-fd-vir-phy-hnd-format[0, 335, (nil), (nil), 0, 768]
rect[0, 0, 1440, 1080, 1440, 1814, 768, 0]
f-blend-size-rotation-col-log-mmu[768, 0, 0, 0, 0, 0, 1]
[Parsed_scale_rkrga_0 @ 0x7f9c045db0] RGA blit failed: -22
 RgaBlit(1507) RGA_BLIT fail: 无效的参数
 RgaBlit(1508) RGA_BLIT fail: 无效的参数
handl-fd-vir-phy-hnd-format[0, 223, (nil), (nil), 0, 2560]
rect[0, 0, 1600, 1200, 1792, 1200, 2560, 0]
f-blend-size-rotation-col-log-mmu[2560, 0, 0, 0, 0, 0, 1]
handl-fd-vir-phy-hnd-format[0, 335, (nil), (nil), 0, 768]
rect[0, 0, 1440, 1080, 1440, 1814, 768, 0]

总结

1) 硬件帧直接进编码器的可行性。

2) scale_rga 在线程操作中的局限性。

3) 拉流对服务端的兼容性。

相关推荐
程序员老陆11 小时前
FFmpeg的AVFrame全部数据操作函数详解
ffmpeg·音视频·avframe
glumes11 小时前
从《牛来》到 DCP:一部电影是怎么走进电影院的?
ffmpeg·音视频开发
程序员老陆1 天前
FFmpeg RTSP 解复用器(rtsp demuxer)完全解析:从「能播」到「稳播」的参数圣经
ffmpeg·音视频·拉流·rtsp
诸葛大钢铁3 天前
视频转音频怎么转换?在线工具、VLC、FFmpeg 三种方法详解
ffmpeg·音视频·mp4转mp3·视频转音频·视频转mp3·视频提取音频
qingmiaozhuan3 天前
电脑录屏软件追剧高光截取:FFmpeg帧裁剪与可视化方案对比
ffmpeg·电脑·音视频开发·录屏技术·电脑录屏
程序员老陆3 天前
FFmpeg6操作 RTMP参数详解
ffmpeg·音视频·rtmp
戒了,最后一次3 天前
Windows 安装 FFmpeg 完整教程(附环境变量配置 + 常用命令示例)
windows·ffmpeg
≮傷£≯√4 天前
FFmpeg + qt 音频播放
qt·ffmpeg·音视频
程序员老陆4 天前
Qt的QThread::usleep和FFmpeg的libavutil模块的av_usleep哪个精度高一些?
开发语言·qt·ffmpeg·音视频