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;操作速度…

代码随香录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;才能用。 只有分了…

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

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

LayUi之手风琴的趣味案例

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

性能测试之性能问题分析

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

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

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

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

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

实际上手体验maven面对冲突Jar包的加载规则 | 京东云技术团队

一、问题背景 相信大家在日常的开发过程中都遇到过Jar包冲突的问题&#xff0c;emm&#xff0c;在最近处理业务需求时我也遇到了不同版本jar包冲突导致项目加载出错的问题。主要是一个完整的项目会不可避免的使用第三方的Jar包来实现功能开发&#xff0c;各种第三方包之间可能…

Python 3 拷贝、浅拷贝、直接引用

诸神缄默不语-个人CSDN博文目录 复杂的以后再补。 总的来说&#xff0c;像常数、字符串这种比较简单的变量无所谓&#xff0c;但是对于一些复杂对象&#xff08;比如list等&#xff09;&#xff0c;如果直接使ba&#xff0c;相当于直接把a的路径给了b&#xff0c;b这个对象的…

day35-Postman/ajax

0目录 1.postman 2.ajax 1.Postman 1.1 定义&#xff1a;postman用于测试http协议接口&#xff0c;无论是开发还是测试人员 1.2 Servlet中的doGet&#xff08;&#xff09;/doPost…

建造者模式-复杂对象的组装与创建

生产一辆车&#xff0c;主要有以下步骤&#xff1a;安装骨架、安装发动机及安装轮胎。这些步骤有指定的执行顺序&#xff0c;步骤缺一不可。 图 传统方案 传统方案存在的问题&#xff1a; 传参不便&#xff0c;虽可在构造函数那传参&#xff0c;但是传参时需要注意参数顺序等…

出租屋智能电表系统

随着科技的不断发展&#xff0c;智能化逐渐成为人们生活中不可或缺的一部分。在房屋租赁市场中&#xff0c;智能电表系统成为越来越多出租屋的标配&#xff0c;为房东和租户带来了便捷和安全。本文将从以下几个方面介绍出租屋智能电表系统的特点和优势。 一、出租屋智能电表系统…

LCD-STM32液晶显示中英文-(7.字模及显示原理)

目录 字模介绍 什么是字模 字模的构成 字模显示原理 字模制作 如何制作字模 字模寻址公式 存储字模文件 字模介绍 什么是字模 有了编码&#xff0c;我们就能在计算机中处理、存储字符了&#xff0c;但是如果计算机处理完字符后直接以编码的形式输出&#xff0c;人类将难…

Flutter:网络图像缓存插件——cached_network_image

前言 为什么要使用这个插件&#xff0c;有什么用呢&#xff1f;毕竟官方提供了Image.network来进行网络图片加载 Image.network和CachedNetworkImage都可以用于在Flutter中加载网络图片&#xff0c;但它们之间有一些区别。 Image.network是Flutter核心库提供的一个构造函数&…