同一路RTSP|RTMP流如何同时回调YUV和RGB数据实现渲染和算法分析

本文涉及的产品
视觉智能开放平台,视频资源包5000点
视觉智能开放平台,图像资源包5000点
视觉智能开放平台,分割抠图1万点
简介: 我们播放RTSP|RTMP流,如果需要同时做渲染和算法分析的话,特别是渲染在上层实现(比如Unity),算法是python这种情况,拉两路流,更耗费带宽和性能,拉一路流,同时回调YUV和RGB数据也可以,但是更灵活的是本文提到的按需转算法期望的RGB数据,然后做算法处理

技术背景

我们在做RTSP|RTMP播放器的时候,有这样的技术诉求,开发者希望同时回调YUV、RGB数据,特别是Unity场景下,YUV数据用于渲染,RGB数据用于做视觉算法分析,拿到的RGB数据,想办法和python通信,发给python做视觉算法处理。

一般来说,如果设备带宽和性能比较好的话,可以直接拉两路流,同时解码回调需要的数据,当然,一般是不建议这么做,特别是4K+分辨率的流,同时解两路,耗费性能,没有必要。

另外一种,可以修改播放器底层逻辑,实现同时回调YUV和RGB数据,但是,我们知道,大多场景,RGB数据做算法分析的话,不一定需要全帧和高分辨率,考虑到算法处理能力,比如,有可能一秒钟只需要处理5-10帧,而且,有的视觉算法对高分辨率数据支持不好(比如分析4K的)。

那么,比较好的方式是,回调YUV数据,然后,RGB数据,提供上层接口,按需转,转过后的RGB数据,发给python或者其他算法就好。

技术实现

基于上述场景,我们做了以下的方案:

image.gif

1. 设置回调YUV数据:

video_frame_call_back_ = new SP_SDKVideoFrameCallBack(SetVideoFrameCallBackV2);
NTSmartPlayerSDK.NT_SP_SetVideoFrameCallBack(player_handle_, (Int32)NT.NTSmartPlayerDefine.NT_SP_E_VIDEO_FRAME_FORMAT.NT_SP_E_VIDEO_FRAME_FROMAT_I420, IntPtr.Zero, video_frame_call_back_);

image.gif

2. 如果是做YUV到RGB数据转换:

/*
 * SmartPlayer.cs
 * Author: https://daniusdk.com
 * WeChat: xinsheng120
 */
rgb_frame.format_ = (int)NT.NTSmartPlayerDefine.NT_SP_E_VIDEO_FRAME_FORMAT.NT_SP_E_VIDEO_FRAME_FORMAT_ARGB;
rgb_frame.width_ = video_frame.width_;
rgb_frame.height_ = video_frame.height_;
rgb_frame.timestamp_ = video_frame.timestamp_;
rgb_frame.stride0_ = video_frame.width_ * 4;
rgb_frame.stride1_ = 0;
rgb_frame.stride2_ = 0;
rgb_frame.stride3_ = 0;
Int32 argb_size = rgb_frame.stride0_ * rgb_frame.height_;
rgb_frame.plane0_ = Marshal.AllocHGlobal(argb_size);
NTSmartPlayerSDK.NT_SP_I420ToARGB(video_frame.plane0_, video_frame.stride0_, video_frame.plane1_, video_frame.stride1_, video_frame.plane2_, video_frame.stride2_,
   rgb_frame.plane0_, rgb_frame.stride0_, video_frame.width_, video_frame.height_);

image.gif

3. 这里可以看到,我们设计了I420到ARGB的接口:

/*
 * smart_player_sdk.cs
 * Author: https://daniusdk.com
 * WeChat: xinsheng120
 */
[DllImport(@"SmartPlayerSDK.dll")]
public static extern UInt32 NT_SP_I420ToARGB(IntPtr src_y_plane, Int32 src_y_stride,
    IntPtr src_u_plane, Int32 src_u_stride,
    IntPtr src_v_plane, Int32 src_v_stride,
    IntPtr dst_argb_plane, Int32 dst_argb_stride,
    Int32 width, Int32 height);

image.gif

好多算法,对高分辨率数据支持并不友好,于是我们做了分辨率缩放接口,拿到yuv数据后,先做缩放,然后再做yuv到rgb的转换。

/*
 * SmartPlayer.cs
 * Author: https://daniusdk.com
 * WeChat: xinsheng120
 */
 
int scale_width = 1280;
int scale_height = 720;
int scale_y_stride = scale_width;
int scale_u_stride = (scale_width + 1) / 2;
int scale_v_stride = scale_u_stride;
int scale_y_size = scale_y_stride * scale_height;
int scale_u_size = scale_u_stride * ((scale_height + 1) / 2);
int scale_v_size = scale_u_size;
NT_SP_VideoFrame scale_frame = new NT_SP_VideoFrame();
scale_frame.width_ = scale_width;
scale_frame.height_ = scale_height;
scale_frame.plane0_ = Marshal.AllocHGlobal(scale_y_size);
scale_frame.plane1_ = Marshal.AllocHGlobal(scale_u_size);
scale_frame.plane2_ = Marshal.AllocHGlobal(scale_v_size);
NTSmartPlayerSDK.NT_SP_I420Scale(video_frame.plane0_, video_frame.stride0_,
    video_frame.plane1_, video_frame.stride1_,
    video_frame.plane2_, video_frame.stride2_,
    video_frame.width_, video_frame.height_,
    scale_frame.plane0_, scale_y_stride,
    scale_frame.plane1_, scale_u_stride,
    scale_frame.plane2_, scale_v_stride,
    scale_frame.width_, scale_frame.height_, 3);
rgb_frame.format_ = (int)NT.NTSmartPlayerDefine.NT_SP_E_VIDEO_FRAME_FORMAT.NT_SP_E_VIDEO_FRAME_FORMAT_ARGB;
rgb_frame.width_ = scale_frame.width_;
rgb_frame.height_ = scale_frame.height_;
rgb_frame.stride0_ = scale_frame.width_ * 4;
rgb_frame.stride1_ = 0;
rgb_frame.stride2_ = 0;
rgb_frame.stride3_ = 0;
Int32 argb_size = rgb_frame.stride0_ * rgb_frame.height_;
rgb_frame.plane0_ = Marshal.AllocHGlobal(argb_size);
NTSmartPlayerSDK.NT_SP_I420ToARGB(scale_frame.plane0_, scale_y_stride, scale_frame.plane1_, scale_u_stride, scale_frame.plane2_, scale_v_stride,
    rgb_frame.plane0_, rgb_frame.stride0_, scale_frame.width_, scale_frame.height_);

image.gif

有了rgb数据,下一步,就是如何跟python通信的问题,在此之前,我们有专门写过一篇blog,跟python交互,有多种方式,比如共享内存、通过UDP发送或者写bitmap文件,然后python实时读取就好。以写bitmap为例(Linux demo),开始播放后,video frame数据回调处理如下:

extern "C" void NT_SDK_SDKVideoFrameCallBack(NT_HANDLE handle, NT_PVOID user_data, NT_UINT32 status,
    const NT_SP_VideoFrame* frame)
{
    if (!frame)
        return;
    fprintf(stdout, "OnSDKVideoFrameCallBack handle:%p frame:%p, timestamp:%llu\n", handle, frame, frame->timestamp_);
#if NEED_SAVE_BITMAP
    if (NT_SP_E_VIDEO_FRAME_FORMAT_RGB32 == frame->format_
        || NT_SP_E_VIDEO_FRAME_FORMAT_ARGB == frame->format_) {
        struct timeval tv;
        if (gettimeofday(&tv, nullptr) != 0) {
            fprintf(stderr, "save bitmap file call gettimeofday failed");
            return;
        }
        uint64_t local_time_us = tv.tv_sec*UINT64_C(1000000) + tv.tv_usec;
        char file_name[128] = { 0 };
        sprintf(file_name, "./outbitmaps/%llu.bmp", (unsigned long long)local_time_us);
        if (!save_bitmap_file(frame->width_, frame->height_, frame->plane0_, frame->stride0_, frame->stride0_*frame->height_, file_name))
            fprintf(stderr, "save bitmap file failed, name:%s", file_name);
        else
            g_bitmap_file_names_.emplace_back(file_name);
        while (g_bitmap_file_names_.size() > 32) {
            remove(g_bitmap_file_names_.front().c_str());
            g_bitmap_file_names_.pop_front();
        }
    }
#endif // NEED_SAVE_BITMAP
}

image.gif

video frame回调后的数据,直接调用save_bitmap_file()实现bitmap文件写入,python程序,只需要到指定的文件夹下,读取生成的bitmap,然后做算法分析即可。拿到rgb数据,当然也可以通过共享内存或UDP直接发出去。

总结

我们播放RTSP|RTMP流,如果需要同时做渲染和算法分析的话,特别是渲染在上层实现(比如Unity),算法是python这种情况,拉两路流,更耗费带宽和性能,拉一路流,同时回调YUV和RGB数据也可以,但是更灵活的是本文提到的按需转算法期望的RGB数据,然后做算法处理。以上是大概的逻辑实现,感兴趣的开发者,可以单独跟我交流。

相关文章
|
19天前
|
算法 搜索推荐 开发者
别再让复杂度拖你后腿!Python 算法设计与分析实战,教你如何精准评估与优化!
在 Python 编程中,算法的性能至关重要。本文将带您深入了解算法复杂度的概念,包括时间复杂度和空间复杂度。通过具体的例子,如冒泡排序算法 (`O(n^2)` 时间复杂度,`O(1)` 空间复杂度),我们将展示如何评估算法的性能。同时,我们还会介绍如何优化算法,例如使用 Python 的内置函数 `max` 来提高查找最大值的效率,或利用哈希表将查找时间从 `O(n)` 降至 `O(1)`。此外,还将介绍使用 `timeit` 模块等工具来评估算法性能的方法。通过不断实践,您将能更高效地优化 Python 程序。
32 4
|
17天前
|
算法 程序员 Python
程序员必看!Python复杂度分析全攻略,让你的算法设计既快又省内存!
在编程领域,Python以简洁的语法和强大的库支持成为众多程序员的首选语言。然而,性能优化仍是挑战。本文将带你深入了解Python算法的复杂度分析,从时间与空间复杂度入手,分享四大最佳实践:选择合适算法、优化实现、利用Python特性减少空间消耗及定期评估调整,助你写出高效且节省内存的代码,轻松应对各种编程挑战。
26 1
|
1月前
|
算法 数据可视化
基于SSA奇异谱分析算法的时间序列趋势线提取matlab仿真
奇异谱分析(SSA)是一种基于奇异值分解(SVD)和轨迹矩阵的非线性、非参数时间序列分析方法,适用于提取趋势、周期性和噪声成分。本项目使用MATLAB 2022a版本实现从强干扰序列中提取趋势线,并通过可视化展示了原时间序列与提取的趋势分量。代码实现了滑动窗口下的奇异值分解和分组重构,适用于非线性和非平稳时间序列分析。此方法在气候变化、金融市场和生物医学信号处理等领域有广泛应用。
|
21小时前
|
算法 安全 Go
Python与Go语言中的哈希算法实现及对比分析
Python与Go语言中的哈希算法实现及对比分析
7 0
|
1天前
|
存储 算法 搜索推荐
算法进阶之路:Python 归并排序深度剖析,让数据排序变得艺术起来!
算法进阶之路:Python 归并排序深度剖析,让数据排序变得艺术起来!
8 0
|
22天前
|
机器学习/深度学习 存储 人工智能
文本情感识别分析系统Python+SVM分类算法+机器学习人工智能+计算机毕业设计
使用Python作为开发语言,基于文本数据集(一个积极的xls文本格式和一个消极的xls文本格式文件),使用Word2vec对文本进行处理。通过支持向量机SVM算法训练情绪分类模型。实现对文本消极情感和文本积极情感的识别。并基于Django框架开发网页平台实现对用户的可视化操作和数据存储。
26 0
文本情感识别分析系统Python+SVM分类算法+机器学习人工智能+计算机毕业设计
|
1月前
|
存储 算法 测试技术
预见未来?Python线性回归算法:数据中的秘密预言家
【9月更文挑战第11天】在数据的海洋中,线性回归算法犹如智慧的预言家,助我们揭示未知。本案例通过收集房屋面积、距市中心距离等数据,利用Python的pandas和scikit-learn库构建房价预测模型。经过训练与测试,模型展现出较好的预测能力,均方根误差(RMSE)低,帮助房地产投资者做出更明智决策。尽管现实关系复杂多变,线性回归仍提供了有效工具,引领我们在数据世界中自信前行。
45 5
|
3天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于GA遗传优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
该算法结合了遗传算法(GA)与分组卷积神经网络(GroupCNN),利用GA优化GroupCNN的网络结构和超参数,提升时间序列预测精度与效率。遗传算法通过模拟自然选择过程中的选择、交叉和变异操作寻找最优解;分组卷积则有效减少了计算成本和参数数量。本项目使用MATLAB2022A实现,并提供完整代码及视频教程。注意:展示图含水印,完整程序运行无水印。
|
2天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于MSER和HOG特征提取的SVM交通标志检测和识别算法matlab仿真
### 算法简介 1. **算法运行效果图预览**:展示算法效果,完整程序运行后无水印。 2. **算法运行软件版本**:Matlab 2017b。 3. **部分核心程序**:完整版代码包含中文注释及操作步骤视频。 4. **算法理论概述**: - **MSER**:用于检测显著区域,提取图像中稳定区域,适用于光照变化下的交通标志检测。 - **HOG特征提取**:通过计算图像小区域的梯度直方图捕捉局部纹理信息,用于物体检测。 - **SVM**:寻找最大化间隔的超平面以分类样本。 整个算法流程图见下图。
|
2天前
|
算法 决策智能
基于禁忌搜索算法的VRP问题求解matlab仿真,带GUI界面,可设置参数
该程序基于禁忌搜索算法求解车辆路径问题(VRP),使用MATLAB2022a版本实现,并带有GUI界面。用户可通过界面设置参数并查看结果。禁忌搜索算法通过迭代改进当前解,并利用记忆机制避免陷入局部最优。程序包含初始化、定义邻域结构、设置禁忌列表等步骤,最终输出最优路径和相关数据图表。

热门文章

最新文章