Unity平台如何实现RTSP转RTMP推送?

技术背景

Unity平台下,RTSP、RTMP播放和RTMP推送,甚至包括轻量级RTSP服务这块都不再赘述,今天探讨的一位开发者提到的问题,如果在Unity下,实现RTSP播放的同时,随时转RTMP推送出去?

RTSP转RTMP,在原生环境下老早已经有了,这里,其实就是把原生的挪到Unity即可,相关流程如下:

技术实现

本文以Windows平台为例,在RTSP播放模块的基础上,加个RTSP转RTMP推送模块,废话不多说,上代码:

实时播放、停止播放

/** SmartPlayerWinMono.cs.cs* * Author: daniusdk.com* Created on 2017/04/19.*/
public void StartPlayer(int sel)
{Debug.Log("StartPlayer++, sel: " + sel);if (videoctrl[sel].is_playing_){Debug.Log("StartPlayer, already started.. sel: " + sel);return;}lock (videoctrl[sel].frame_lock_){videoctrl[sel].cur_video_frame_ = null;}if (!videoctrl[sel].is_recording_ && !videoctrl[sel].is_pulling_){if (!OpenPlayerHandle(sel)){Debug.LogError("call OpenPlayerHandle failed, sel:" + sel);return;}}if (is_enable_hardware_decoder_){NTSmartPlayerSDK.NT_SP_SetH264HardwareDecoder(videoctrl[sel].player_handle_, is_support_h264_hardware_decoder_ ? 1 : 0, 0);NTSmartPlayerSDK.NT_SP_SetH265HardwareDecoder(videoctrl[sel].player_handle_, is_support_h265_hardware_decoder_ ? 1 : 0, 0);}else{NTSmartPlayerSDK.NT_SP_SetH264HardwareDecoder(videoctrl[sel].player_handle_, 0, 0);NTSmartPlayerSDK.NT_SP_SetH265HardwareDecoder(videoctrl[sel].player_handle_, 0, 0);}//video frame callback (YUV/RGB)videoctrl[sel].sdk_video_frame_call_back_ = new VideoControl.SetVideoFrameCallBack(SDKVideoFrameCallBack);videoctrl[sel].video_frame_call_back_ = new SP_SDKVideoFrameCallBack(NT_SP_SetVideoFrameCallBack);NTSmartPlayerSDK.NT_SP_SetVideoFrameCallBack(videoctrl[sel].player_handle_, (Int32)NT.NTSmartPlayerDefine.NT_SP_E_VIDEO_FRAME_FORMAT.NT_SP_E_VIDEO_FRAME_FROMAT_I420, window_handle_, videoctrl[sel].video_frame_call_back_);UInt32 flag = NTSmartPlayerSDK.NT_SP_StartPlay(videoctrl[sel].player_handle_);if (flag == DANIULIVE_RETURN_OK){videoctrl[sel].is_need_get_frame_ = true;Debug.Log("NT_SP_StartPlay succeed, sel:" + sel);}else{videoctrl[sel].is_need_get_frame_ = false;Debug.LogError("NT_SP_StartPlay failed, sel:" + sel);}videoctrl[sel].is_playing_ = true;
}private void StopPlayer(int sel)
{Debug.Log("StopPlayer++, sel: " + sel);videoctrl[sel].is_need_get_frame_ = false;videoctrl[sel].is_need_init_texture_ = false;if (videoctrl[sel].player_handle_ == IntPtr.Zero){return;}UInt32 flag = NTSmartPlayerSDK.NT_SP_StopPlay(videoctrl[sel].player_handle_);if (flag == DANIULIVE_RETURN_OK){Debug.Log("call NT_SP_StopPlay succeed, sel: " + sel);}else{Debug.LogError("call NT_SP_StopPlay failed, sel: " + sel);}if (!videoctrl[sel].is_recording_ && !videoctrl[sel].is_pulling_){NTSmartPlayerSDK.NT_SP_Close(videoctrl[sel].player_handle_);videoctrl[sel].player_handle_ = IntPtr.Zero;}videoctrl[sel].is_playing_ = false;
}

如果需要转RTMP出去,首先拉流端,需要调用拉流接口:

开始拉流、停止拉流

public void StartPull(int sel)
{if (videoctrl[sel].is_pulling_){Debug.Log("StartPull, already started.. sel: " + sel);return;}if (!videoctrl[sel].is_playing_ && !videoctrl[sel].is_recording_ ){if (!OpenPlayerHandle(sel)){Debug.LogError("call OpenPlayerHandle failed, sel:" + sel);return;}}videoctrl[sel].pull_stream_video_data_call_back_ = new SP_SDKPullStreamVideoDataCallBack(OnVideoDataHandle);videoctrl[sel].pull_stream_audio_data_call_back_ = new SP_SDKPullStreamAudioDataCallBack(OnAudioDataHandle);NTSmartPlayerSDK.NT_SP_SetPullStreamVideoDataCallBack(videoctrl[sel].player_handle_, IntPtr.Zero, videoctrl[sel].pull_stream_video_data_call_back_);NTSmartPlayerSDK.NT_SP_SetPullStreamAudioDataCallBack(videoctrl[sel].player_handle_, IntPtr.Zero, videoctrl[sel].pull_stream_audio_data_call_back_);int is_transcode_aac = 1;   //PCMA/PCMU/Speex格式转AAC后 再转发NTSmartPlayerSDK.NT_SP_SetPullStreamAudioTranscodeAAC(videoctrl[sel].player_handle_, is_transcode_aac);UInt32 ret = NTSmartPlayerSDK.NT_SP_StartPullStream(videoctrl[sel].player_handle_);if (NTBaseCodeDefine.NT_ERC_OK != ret){if (!videoctrl[sel].is_playing_ && !videoctrl[sel].is_recording_){NTSmartPlayerSDK.NT_SP_Close(videoctrl[sel].player_handle_);videoctrl[sel].player_handle_ = IntPtr.Zero;}return;}videoctrl[sel].is_pulling_ = true;
}public void StopPull(int sel)
{if (!videoctrl[sel].is_pulling_)return;NTSmartPlayerSDK.NT_SP_StopPullStream(videoctrl[sel].player_handle_);if (!videoctrl[sel].is_playing_ && !videoctrl[sel].is_recording_){NTSmartPlayerSDK.NT_SP_Close(videoctrl[sel].player_handle_);videoctrl[sel].player_handle_ = IntPtr.Zero;}videoctrl[sel].is_pulling_ = false;
}

拉流设置的时候,需要注意的是,如果是其他比如PCMA、PCMU的,考虑到通用性,可以转AAC后再回调数据上来,此外,拉流或播放的时候,判断是不是已经打开了RTSP URL,确保同一路流在一个实例内,不要开两个实例,占用额外的资源。

此外,关闭播放或拉流的时候,需要判断是不是处于拉流或播放状态,只要二者有一个还没关闭,实例就无法关闭。

开始转推RTMP、停止转推:

public bool StartPush(int sel, String url)
{if (videoctrl[sel].is_pushing_)return false;if (String.IsNullOrEmpty(url))return false;if (!OpenPushHandle(sel))return false;if (GetPushHandle(sel) == IntPtr.Zero)return false;IntPtr push_handle = GetPushHandle(sel);if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_SetURL(push_handle, url, IntPtr.Zero)){NTSmartPublisherSDK.NT_PB_Close(push_handle);SetPushHandle(sel, IntPtr.Zero);return false;}if (NTBaseCodeDefine.NT_ERC_OK != NTSmartPublisherSDK.NT_PB_StartPublisher(push_handle, IntPtr.Zero)){NTSmartPublisherSDK.NT_PB_Close(push_handle);SetPushHandle(sel, IntPtr.Zero);return false;}videoctrl[sel].is_pushing_ = true;return true;
}public void StopPush(int sel)
{if (!videoctrl[sel].is_pushing_)return;videoctrl[sel].is_pushing_ = false;lock (videoctrl[sel].push_handle_mutex_){if (videoctrl[sel].push_handle_ == IntPtr.Zero)return;NTSmartPublisherSDK.NT_PB_StopPublisher(videoctrl[sel].push_handle_);NTSmartPublisherSDK.NT_PB_Close(videoctrl[sel].push_handle_);videoctrl[sel].push_handle_ = IntPtr.Zero;}
}

音视频数据回调

private void OnVideoDataHandle(IntPtr handle, IntPtr user_data,UInt32 video_codec_id, IntPtr data, UInt32 size,IntPtr info, IntPtr reserve)
{int cur_sel = -1;for ( int i = 0; i < videoctrl.Length; i++){if(handle == videoctrl[i].player_handle_){cur_sel = i;break;}}if (cur_sel < 0)return;if (!videoctrl[cur_sel].is_pushing_)return;if (data == IntPtr.Zero)return;if (size < 1)return;if (info == IntPtr.Zero)return;NT_SP_PullStreamVideoDataInfo video_info = (NT_SP_PullStreamVideoDataInfo)Marshal.PtrToStructure(info, typeof(NT_SP_PullStreamVideoDataInfo));lock (videoctrl[cur_sel].push_handle_mutex_){if (!videoctrl[cur_sel].is_pushing_)return;if (videoctrl[cur_sel].push_handle_ == IntPtr.Zero)return;//新接口NTSmartPublisherSDK.NT_PB_PostVideoEncodedDataV2(videoctrl[cur_sel].push_handle_, video_codec_id,data, size, video_info.is_key_frame_, video_info.timestamp_, video_info.presentation_timestamp_);}
}private void OnAudioDataHandle(IntPtr handle, IntPtr user_data,UInt32 audio_codec_id, IntPtr data, UInt32 size,IntPtr info, IntPtr reserve)
{int cur_sel = -1;for (int i = 0; i < videoctrl.Length; i++){if (handle == videoctrl[i].player_handle_){cur_sel = i;break;}}if (cur_sel < 0)return;if (!videoctrl[cur_sel].is_pushing_)return;if (data == IntPtr.Zero)return;if (size < 1)return;if (info == IntPtr.Zero)return;NT_SP_PullStreamAuidoDataInfo audio_info = (NT_SP_PullStreamAuidoDataInfo)Marshal.PtrToStructure(info, typeof(NT_SP_PullStreamAuidoDataInfo));lock (videoctrl[cur_sel].push_handle_mutex_){if (!videoctrl[cur_sel].is_pushing_)return;if (videoctrl[cur_sel].push_handle_ == IntPtr.Zero)return;NTSmartPublisherSDK.NT_PB_PostAudioEncodedData(videoctrl[cur_sel].push_handle_, audio_codec_id, data, size,audio_info.is_key_frame_, audio_info.timestamp_,audio_info.parameter_info_, audio_info.parameter_info_size_);}
}

总结

实际上,Unity环境下的RTSP转RTMP推送,相对RTMP、RTSP播放或推流,对接更容易,因为基本不涉及到页面交互,感兴趣的开发者可以尝试看。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/3682.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浙大数据结构第四周之04-树6 Complete Binary Search Tree

题目详情&#xff1a; A Binary Search Tree (BST) is recursively defined as a binary tree which has the following properties: The left subtree of a node contains only nodes with keys less than the nodes key.The right subtree of a node contains only nodes w…

[javascript核心-08] V8 内存管理机制及性能优化

V8 内存管理 V8 本身也是程序&#xff0c;它本身也会申请内存&#xff0c;它申请的内存称为常驻内存&#xff0c;而它又将内存分为堆和栈 栈内存 栈内存介绍 栈用于存放JS 中的基本类型和引用类型指针栈空间是连续的&#xff0c;增加删除只需要移动指针&#xff0c;操作速度…

华为OD真题--分苹果-带答案

有A&#xff0c;B两个同学想要分苹果。A的想法是使用二进制进行&#xff0c;1 1相加不进一位&#xff0c;如&#xff08;9 5 1001 101 12&#xff09;。B同学的想法是使用十进制进行&#xff0c;并且进一位。会输入两组数据&#xff0c;一组是苹果总数&#xff0c;一组分别…

代码随香录day21

235. 二叉搜索树的最近公共祖先 本题思路&#xff1a; 还是要利用二叉搜索树的特性&#xff0c;中序遍历为有序数组。如果pq两个节点都小于root&#xff0c;那么最近公共祖肯定是在他的左子树&#xff0c;如果都大于那么&#xff0c;肯定就在右子树。然后直接return root 代码…

Linux Ubuntu安装RabbitMQ服务

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…

【前端知识】React 基础巩固(二十七)——Fragment

React 基础巩固(二十七)——Fragment Fragment Fragment 允许将子列表分组&#xff0c;而无需向 DOM 添加额外节点可以采用语法糖<></>来替代 Fragment&#xff0c;但在需要添加 key 的场景下不能使用此短语 import React, { PureComponent, Fragment } from &q…

Echarts 实现温度计

先上图 <div id="mainOne" style="width: 230px;height:130px;"></div> var dom1 = document.getElementById(mainOne) 核心代码 setTemperature(){var dom1 = document.getElementById(mainOne)var dom2 = document.getElementById(mainTw…

正则表达式与文本处理器

文本处理器三剑客&#xff1a;grep&#xff08;查找&#xff09; sed awk 正则表达式&#xff1a;由一类特殊字符以及文本字符所编写的一种模式&#xff0c;处理文本当中的内容 其中的一些字符不表示字符的字面含义&#xff0c;这些字符表示控制或者通配的功能 通配符&…

在分区工具上,格式化分区和删除分区. 两者有什么不一样吗?

1.格式化分区&#xff1a;就是重建文件系统&#xff0c;等于把目标分区的数据全部清掉。 删除分区&#xff1a;你删除后可以再重新分区&#xff0c;可以分区多个分区&#xff0c;前提是“删除分区”的大小足够大。分了区&#xff0c;还必须格式化&#xff0c;才能用。 只有分了…

ThreadLocal内存泄露原因,如何避免

内存泄露为程序在申请内存后&#xff0c;无法释放已经申请的内存空间&#xff0c;一次内存泄露可以忽略&#xff0c;但是内存泄露堆积后果很严重&#xff0c;无论多少内存迟早被用光。 不再会被使用的对象或者变量占用的内存不能被回收&#xff0c;就是内存泄露。 强引用&…

掌握无人机遥感数据预处理的全链条理论与实践流程、典型农林植被性状的估算理论与实践方法、利用MATLAB进行编程实践(脚本与GUI开发)以及期刊论文插图制作等

目录 专题一 认识主被动无人机遥感数据 专题二 预处理无人机遥感数据 专题三 定量估算农林植被关键性状 专题四 期刊论文插图精细制作与Appdesigner应用开发 近地面无人机植被定量遥感与生理参数反演 更多推荐 遥感技术作为一种空间大数据手段&#xff0c;能够从多时、多…

LayUi之手风琴的趣味案例

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于LayUi的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.手风琴是什么 二.手风琴在什么时候使用…

Vue 3 功能实现

Vue 3 功能实现 本论旨在研究和分析 Vue 3 的功能实现&#xff0c;深入探讨 Vue 3 作为下一代前端开发框架的特色与创新。论文首先介绍了 Vue 3 的背景和发展历程&#xff0c;随后重点讨论了其引入的核心功能和改进之处&#xff0c;包括响应式系统的全面重构、Composition API…

性能测试之性能问题分析

开始性能测试前需要了解的内容&#xff1a; 1、项目具体需求。 2、指标&#xff1a;响应时间在多少以内&#xff0c;并发数多少&#xff0c;tps多少&#xff0c;总tps多少&#xff0c;稳定性交易总量多少&#xff0c;事务成功率&#xff0c;交易波动范围&#xff0c;稳定运行时…

浅谈chatgpt如何改变人们生活

ChatGPT&#xff0c;作为一种自然语言处理模型&#xff0c;确实有潜力改变人们的生活。以下是ChatGPT如何可能改变人们生活的几个方面&#xff1a;1.智能助手和客户支持&#xff1a;ChatGPT可以作为智能助手嵌入到各种应用中&#xff0c;为用户提供即时的帮助和解答问题。在客户…

【iOS】—— 面向对象,Runtime,ARC等问题总结

对于暑假学习大多数是对之前学习的一个复习&#xff0c;在这里只做对之前学习欠缺知识的补充以及这些知识点涉及的一些问题&#xff0c;从问题入手学习。 文章目录 面向对象1.一个NSObject对象占多少内存&#xff1f;2.对象的isa指针指向哪里&#xff1f;3.OC的类信息存放在哪…

spring复习:(41)全注解方式的事务

一、创建事务、数据源相关的配置类&#xff1a; package cn.edu.tju.study.service.transaction;import com.zaxxer.hikari.HikariDataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import or…

诚迈科技子公司智达诚远精耕智能驾驶,为商用落地注入创新力量

近期&#xff0c;工业和信息化部副部长辛国斌在新闻发布会上表示&#xff0c;将启动智能网联汽车准入和上路通行试点&#xff0c;组织开展城市级“车路云一体化”示范应用&#xff0c;将支持L3级及更高级别的自动驾驶功能商业化应用。根据工信部最新消息&#xff0c;《智能网联…

使用matlab中的SVM进行数据回归预测

在MATLAB中使用支持向量机&#xff08;SVM&#xff09;进行数据回归预测&#xff0c;你可以遵循以下步骤&#xff1a; 准备数据集&#xff1a; 将你的特征矩阵X和目标变量向量y加载到MATLAB工作空间中。确保X和y的维度匹配。 拆分数据集&#xff1a; 将数据集划分为训练集和测…

AI编程助手体验

一、背景 最近在接触各种AI插件&#xff0c;用copilot的话要10美元每月&#xff0c;太贵&#xff0c;就下载了清华的CodeGeeX 刚好&#xff0c;有个需求&#xff0c;需要将枚举的所有值&#xff0c;随机组合求和&#xff0c;并返回所有组合之和。 ”假设你有一个名为 enum_v…