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

目录

链接库文件查看

HEVC 错误之一

Codec type or id mismatches

测试文件信息

解决方法

HEVC 错误之二

Failed to set config: -6

测试文件信息

调试开关

解决方法

帧的分类

软件帧(System Memory Frame)

DRM PRIME 帧(DMA-Buf Frame)

验证编码器支持的帧格式

硬件帧之间的转换流程

滤镜链

buffersink输出 NV12(软件帧)

buffersink输出 DRM_PRIME(硬件帧)

滤镜链

总体浏览

代码示例RGA出硬件帧

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

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

3) 设置output的格式

帧流转代码

context

hw_frames_ctx

解码器类型错误

硬件帧送入编码

格式转换

滤镜链参数BGRA

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_PRIMEDMA-Buf(内核管理的物理连续内存)❌ 不能直接读(可 mmap)硬解输出、zero-copy 显示
通用硬件帧AV_PIX_FMT_NONE+hw_frames_ctxGPU/VPU 设备内存❌ 不能VAAPI、CUDA、Vulkan 等框架
RGA 硬件帧AV_PIX_FMT_NONE+hw_frames_ctxRGA 专用内存/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 → [hwupload] → [scale_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)
buffersrcBuffer Source入口滤镜图的"入口",把解码帧"喂"进滤镜图
滤镜链Filter Chain处理中间的处理环节(缩放、格式转换、硬件上传/下载等)
buffersinkBuffer 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) 拉流对服务端的兼容性。