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
相关产品推荐
相关产品推荐

