You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QNX Neutrino OS下RTSP直播流初始画面失真问题求解

QNX Neutrino下RTSP直播初始画面失真问题

我在QNX Neutrino OS上用C代码实现了RTSP直播流播放,开发板和PC通过以太网连接,能正常播放PC推送的RTSP视频,但初始3-4秒画面始终失真。

PC端推流命令

cvlc -vvv ~/Videos/bbb_sunflower_1080p_60fps_normal.mp4 --sout '#transcode{acodec=mp4a}:rtp{sdp=rtsp://:8554/stream}' :network-caching=1500

开发板端播放代码

#include <screen/screen.h>
#include <mm/renderer.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <fcntl.h>

#define WINGRP_NAME_MAX_LEN 128

// I/O devices
static const char *video_device_url    = "screen:?winid=videosamplewindowgroup&wingrp=videosamplewindowgroup";
static const char *audio_device_url    = "audio:default";

// Name of video context
static const char *video_context_name = "samplevideocontextname";

// Window group name
static const char *window_group_name = "videosamplewindowgroup";

// Window Name
const char *window_name = "appwindow";

strm_dict_t* calculate_rect(int width, int height) {
    const int image_width = 640;
    const int image_height = 480;
    const float image_aspect = (float)image_width / (float)image_height;
    const float aspect_tolerance = 0.1;

    char buffer[16];
    strm_dict_t *dict = strm_dict_new();

    if (NULL == dict) {
        return NULL;
    }

    //fullscreen is the default.
    dict = strm_dict_set(dict, "video_dest_x", "0");
    if (NULL == dict)
        goto fail;
    dict = strm_dict_set(dict, "video_dest_y", "0");
    if (NULL == dict)
        goto fail;
    dict = strm_dict_set(dict, "video_dest_w", itoa(width, buffer, 10));
    if (NULL == dict)
        goto fail;
    dict = strm_dict_set(dict, "video_dest_h", itoa(height, buffer, 10));
    if (NULL == dict)
        goto fail;

    float screen_aspect = (float)width/(float)height;

        dict = strm_dict_set(dict, "video_dest_x", itoa((width - image_width) / 2, buffer, 10));
        if (NULL == dict)
            goto fail;

        width = height * image_aspect;

        dict = strm_dict_set(dict, "video_dest_w", itoa(width, buffer, 10));
        if (NULL == dict)
            goto fail;


    return dict;

fail:
    strm_dict_destroy(dict);
    return NULL;
}


int main(void) {

    int rc;
    int exit_application = 0;

    printf("DBG-1\n");

    // Screen variables
    screen_context_t    screen_context = 0;
    screen_window_t     screen_window = 0;

    int screen_size[2] = {0,0};

    // Renderer variables
    mmr_connection_t*     mmr_connection = 0;
    mmr_context_t*        mmr_context = 0;
    strm_dict_t*          dict = NULL;

    // I/O variables
    int                    video_device_output_id = -1;
    int                    audio_device_output_id = -1;

    printf("DBG-2\n");

        /*
        * Create the window used for video output.
        */
       if (screen_create_context(&screen_context, SCREEN_APPLICATION_CONTEXT) != 0) {
           return EXIT_FAILURE;
       }
       printf("DBG-3\n");

       if (screen_create_window(&screen_window, screen_context) != 0) {
           screen_destroy_context(screen_context);
           return EXIT_FAILURE;
       }
       printf("DBG-4\n");

       if (screen_create_window_group(screen_window, window_group_name) != 0) {
           return EXIT_FAILURE;
       }
       printf("DBG-5\n");

       int format = SCREEN_FORMAT_RGBA8888;
       if (screen_set_window_property_iv(screen_window, SCREEN_PROPERTY_FORMAT, &format) != 0) {
           return EXIT_FAILURE;
       }

       if(screen_set_window_property_cv(screen_window, SCREEN_PROPERTY_ID_STRING, 32, window_name)){
           perror("Set Window ID Error\n");
           return EXIT_FAILURE;
       }

       printf("DBG-6\n");

       int usage = SCREEN_USAGE_NATIVE;
       if (screen_set_window_property_iv(screen_window, SCREEN_PROPERTY_USAGE, &usage) != 0) {
           return EXIT_FAILURE;
       }
       printf("DBG-7\n");

       if (screen_create_window_buffers(screen_window, 1) != 0) {
           return EXIT_FAILURE;
       }

       printf("DBG-8\n");

       /*
        * Configure mm-renderer.
        */
       mmr_connection = mmr_connect(NULL);
       if (mmr_connection == NULL) {
           return EXIT_FAILURE;
       }
       printf("DBG-9\n");

       mmr_context = mmr_context_create(mmr_connection, video_context_name, 0, S_IRWXU|S_IRWXG|S_IRWXO);
       if (mmr_context == NULL) {
           return EXIT_FAILURE;
       }
       printf("DBG-11\n");
       /*
        * Configure video and audio output.
        */
       video_device_output_id = mmr_output_attach(mmr_context, video_device_url, "video");
       if (video_device_output_id == -1) {
           return EXIT_FAILURE;
       }
       printf("DBG-12\n");

       printf("DBG-13\n");
       // Get the render buffer
       screen_buffer_t temp_buffer[1];
       if (screen_get_window_property_pv( screen_window, SCREEN_PROPERTY_RENDER_BUFFERS, (void**)temp_buffer) != 0) {
           return EXIT_FAILURE;
       }
       printf("DBG-14\n");
       // Fill the buffer with a solid color (black)
       int fill_attributes[3] = {SCREEN_BLIT_COLOR, 0x0, SCREEN_BLIT_END};
       if (screen_fill(screen_context, temp_buffer[0], fill_attributes) != 0) {
           return EXIT_FAILURE;
       }
       printf("DBG-15\n");
       // Make the window visible
       if (screen_get_window_property_iv(screen_window, SCREEN_PROPERTY_SIZE, screen_size) != 0) {
           return EXIT_FAILURE;
       }
       printf("DBG-16\n");
       int temp_rectangle[4] = {0, 0, screen_size[0], screen_size[1]};
       if (screen_post_window(screen_window, temp_buffer[0], 1, temp_rectangle, 0) != 0) {
           return EXIT_FAILURE;
       }
       printf("DBG-17\n");
       // Prevent the backlight from going off
       int idle_mode = SCREEN_IDLE_MODE_KEEP_AWAKE;
       if (screen_set_window_property_iv(screen_window, SCREEN_PROPERTY_IDLE_MODE, &idle_mode) != 0) {
           return EXIT_FAILURE;
       }
       printf("DBG-18\n");
       // Build up the path where our bundled resource is.
       char cwd[PATH_MAX];
       char media_file[PATH_MAX];
       getcwd(cwd,PATH_MAX);

       rc = snprintf(media_file, PATH_MAX, "rtsp://10.42.0.1:8554/stream", cwd);    // Play video RTSP

       if ((rc == -1) || (rc >= PATH_MAX)) {
           return EXIT_FAILURE;
       }
       printf("DBG-19\n");
       /*
        * Start the playback.
        */
       if (mmr_input_attach(mmr_context, media_file, "track") != 0) {
           return EXIT_FAILURE;
       }
       printf("DBG-20\n");
       if (mmr_play(mmr_context) != 0) {
           return EXIT_FAILURE;
       }


       printf("DBG-21\n");
       /* Do some work to make the aspect ratio correct.
        */
       dict = calculate_rect(screen_size[0], screen_size[1]);
       if (NULL == dict) {
           return EXIT_FAILURE;
       }
       printf("DBG-22\n");
       if (mmr_output_parameters(mmr_context, video_device_output_id, dict) != 0) {
           return EXIT_FAILURE;
       }


       dict = NULL;

       sleep(60);

       if (mmr_stop(mmr_context) != 0) {
               return EXIT_FAILURE;
           }

           if (mmr_output_detach(mmr_context, audio_device_output_id) != 0) {
               return EXIT_FAILURE;
           }

           if (mmr_output_detach(mmr_context, video_device_output_id) != 0) {
               return EXIT_FAILURE;
           }

           if (mmr_context_destroy(mmr_context) != 0) {
               return EXIT_FAILURE;
           }

           mmr_context = 0;
           video_device_output_id = -1;
           audio_device_output_id = -1;

           mmr_disconnect(mmr_connection);
           mmr_connection = 0;


           if (screen_destroy_window(screen_window) != 0) {
               return EXIT_FAILURE;
           }

           if (screen_destroy_context(screen_context) != 0) {
               return EXIT_FAILURE;
           }

           screen_context = 0;
           screen_window = 0;

           return EXIT_SUCCESS;
}

初始失真画面描述

初始画面存在色块、画面撕裂、色彩错位等失真现象,持续3-4秒后恢复正常。

我尝试过给RTSP输入加2秒缓冲但没成功,也不确定是不是流本身的问题,求解决方法。


解决方案

1. 优化推流端参数,确保流的稳定性

调整VLC推流命令,增加关键帧控制和缓存配置:

cvlc -vvv ~/Videos/bbb_sunflower_1080p_60fps_normal.mp4 --sout '#transcode{acodec=mp4a,vcodec=h264,vb=8000,keyint=60}:rtp{sdp=rtsp://:8554/stream}' :network-caching=2000 :sout-mux-caching=1000
  • keyint=60:设置关键帧间隔为60帧(对应60fps视频的1秒间隔),让播放端能快速获取完整的参考帧
  • 提升network-caching到2000ms,增加推流端的网络缓存,减少流的抖动
  • 添加sout-mux-caching=1000,优化复用器缓存,避免帧数据不完整

2. 配置播放端mm-renderer的缓存参数

修改播放代码,在mmr_input_attach前添加RTSP缓存配置:

// 创建输入参数字典,设置RTSP缓存
strm_dict_t *input_dict = strm_dict_new();
strm_dict_set(input_dict, "rtsp_caching", "2000"); // 设置2秒缓存
strm_dict_set(input_dict, "buffer_size", "4096000"); // 增大缓冲区容量

// 使用带参数的接口挂载输入流
if (mmr_input_attach_with_dict(mmr_context, media_file, "track", input_dict) != 0) {
    strm_dict_destroy(input_dict);
    return EXIT_FAILURE;
}
strm_dict_destroy(input_dict);

通过mm-renderer的内置缓存机制,先缓冲足够数据再开始播放,避免初始帧缺失导致的失真。

3. 调整输出参数设置时机

当前代码在播放启动后才设置显示参数,可能导致初始帧渲染格式错误。调整顺序,先配置输出参数再启动播放:

// 先计算并设置输出显示参数
dict = calculate_rect(screen_size[0], screen_size[1]);
if (NULL == dict) {
    return EXIT_FAILURE;
}
if (mmr_output_parameters(mmr_context, video_device_output_id, dict) != 0) {
    return EXIT_FAILURE;
}
strm_dict_destroy(dict);

// 再挂载输入并启动播放
if (mmr_input_attach(mmr_context, media_file, "track") != 0) {
    return EXIT_FAILURE;
}
if (mmr_play(mmr_context) != 0) {
    return EXIT_FAILURE;
}

确保渲染参数在播放前就生效,初始帧按正确格式渲染。

4. 添加手动预缓冲逻辑

如果内置缓存不够用,可在挂载输入后手动等待缓冲:

// 挂载输入流
if (mmr_input_attach(mmr_context, media_file, "track") != 0) {
    return EXIT_FAILURE;
}

// 预缓冲2秒后再播放
sleep(2);

if (mmr_play(mmr_context) != 0) {
    return EXIT_FAILURE;
}

这种方式简单直接,虽增加启动延迟,但能有效避免初始画面失真。

5. 检查视频格式兼容性

  • 尝试将播放端屏幕格式改为SCREEN_FORMAT_YUV420(如果硬件支持),减少格式转换带来的初始卡顿
  • 推流时明确指定vcodec=h264,确保编码格式为标准H.264,降低播放端解码适配成本

内容的提问来源于stack exchange,提问作者Faruk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 02:35:55