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

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

技术背景

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


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

0b3fce3f9afe4855882ce5e864816e94.png

技术实现

本文以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播放或推流,对接更容易,因为基本不涉及到页面交互,感兴趣的开发者可以尝试看。

相关文章
|
编解码 vr&ar 图形学
Unity下如何实现低延迟的全景RTMP|RTSP流渲染
随着虚拟现实技术的发展,全景视频成为新的媒体形式。本文详细介绍了如何在Unity中实现低延迟的全景RTMP或RTSP流渲染,包括环境准备、引入依赖、初始化客户端、解码与渲染、优化低延迟等步骤,并提供了具体的代码示例。适用于远程教育、虚拟旅游等实时交互场景。
304 5
|
编解码 vr&ar 图形学
Unity下如何实现低延迟的全景RTMP|RTSP流渲染
随着虚拟现实技术的发展,全景视频逐渐成为新的媒体形式。本文详细介绍了如何在Unity中实现低延迟的全景RTMP或RTSP流渲染,包括环境准备、引入依赖、初始化客户端、解码与渲染、优化低延迟等步骤,并提供了具体的代码示例。适用于远程教育、虚拟旅游等实时交互场景。
341 2
|
开发者 图形学 iOS开发
掌握Unity的跨平台部署与发布秘籍,让你的游戏作品在多个平台上大放异彩——从基础设置到高级优化,深入解析一站式游戏开发解决方案的每一个细节,带你领略高效发布流程的魅力所在
【8月更文挑战第31天】跨平台游戏开发是当今游戏产业的热点,尤其在移动设备普及的背景下更为重要。作为领先的游戏开发引擎,Unity以其卓越的跨平台支持能力脱颖而出,能够将游戏轻松部署至iOS、Android、PC、Mac、Web及游戏主机等多个平台。本文通过杂文形式探讨Unity在各平台的部署与发布策略,并提供具体实例,涵盖项目设置、性能优化、打包流程及发布前准备等关键环节,助力开发者充分利用Unity的强大功能,实现多平台游戏开发。
760 0
|
安全 Linux 图形学
Linux平台Unity下RTMP|RTSP低延迟播放器技术实现
本文介绍了在国产操作系统及Linux平台上,利用Unity实现RTMP/RTSP直播播放的方法。通过设置原生播放模块的回调函数,可将解码后的YUV数据传递给Unity进行渲染,实现低延迟播放。此外,还提供了播放器启动、参数配置及停止的相关代码示例,并概述了如何在Unity中更新纹理以显示视频帧。随着国产操作系统的发展,此类跨平台直播解决方案的需求日益增长,为开发者提供了灵活高效的开发方式。
296 6
|
图形学 Android开发 iOS开发
穿越数字洪流,揭秘Unity3d中的视频魔法!Windows、Android和iOS如何征服RTSP与RTMP的终极指南!
【8月更文挑战第15天】在数字媒体的海洋中,实时视频流是连接世界的桥梁。对于那些渴望在Unity3d中搭建这座桥梁的开发者来说,本文将揭示如何在Windows、Android和iOS平台上征服RTSP与RTMP的秘密。我们将深入探讨这两种协议的特性,以及在不同平台上实现流畅播放的技巧。无论你是追求稳定性的RTSP拥趸,还是低延迟的RTMP忠实粉丝,这里都有你需要的答案。让我们一起穿越数字洪流,探索Unity3d中视频魔法的世界吧!
401 2
|
编解码 vr&ar 图形学
惊世骇俗!Unity下如何实现低至毫秒级的全景RTMP|RTSP流渲染,颠覆你的视觉体验!
【8月更文挑战第14天】随着虚拟现实技术的进步,全景视频作为一种新兴媒体形式,在Unity中实现低延迟的RTMP/RTSP流渲染变得至关重要。这不仅能够改善用户体验,还能广泛应用于远程教育、虚拟旅游等实时交互场景。本文介绍如何在Unity中实现全景视频流的低延迟渲染,并提供代码示例。首先确保Unity开发环境及所需插件已就绪,然后利用`unity-rtsp-rtmp-client`插件初始化客户端并设置回调。通过FFmpeg等工具解码视频数据并更新至全景纹理,同时采用硬件加速、调整缓冲区大小等策略进一步降低延迟。此方案需考虑网络状况与异常处理,确保应用程序的稳定性和可靠性。
599 1
|
Linux 开发工具 图形学
Unity下实现跨平台的RTMP推流|轻量级RTSP服务|RTMP播放|RTSP播放低延迟解决方案
自2018年起,我们成功实现了Unity环境下的低延迟RTSP|RTMP播放,达到毫秒级延迟,获得业界广泛认可。现已覆盖Windows、Android、iOS与Linux平台的RTMP推送、轻量级RTSP服务及RTSP|RTMP播放。通过高效采集Unity窗口或摄像头数据,并利用原生SDK进行编码与推送,确保了数据传输的高速性。此外,播放器支持多路视频同时播放,适应不同分辨率,并保持长时间运行稳定。更多技术细节和技术博文,请参考相关链接。
678 1
|
图形学 iOS开发 Android开发
从Unity开发到移动平台制胜攻略:全面解析iOS与Android应用发布流程,助你轻松掌握跨平台发布技巧,打造爆款手游不是梦——性能优化、广告集成与内购设置全包含
【8月更文挑战第31天】本书详细介绍了如何在Unity中设置项目以适应移动设备,涵盖性能优化、集成广告及内购功能等关键步骤。通过具体示例和代码片段,指导读者完成iOS和Android应用的打包与发布,确保应用顺利上线并获得成功。无论是性能调整还是平台特定的操作,本书均提供了全面的解决方案。
556 0
|
开发者 图形学 API
从零起步,深度揭秘:运用Unity引擎及网络编程技术,一步步搭建属于你的实时多人在线对战游戏平台——详尽指南与实战代码解析,带你轻松掌握网络化游戏开发的核心要领与最佳实践路径
【8月更文挑战第31天】构建实时多人对战平台是技术与创意的结合。本文使用成熟的Unity游戏开发引擎,从零开始指导读者搭建简单的实时对战平台。内容涵盖网络架构设计、Unity网络API应用及客户端与服务器通信。首先,创建新项目并选择适合多人游戏的模板,使用推荐的网络传输层。接着,定义基本玩法,如2D多人射击游戏,创建角色预制件并添加Rigidbody2D组件。然后,引入网络身份组件以同步对象状态。通过示例代码展示玩家控制逻辑,包括移动和发射子弹功能。最后,设置服务器端逻辑,处理客户端连接和断开。本文帮助读者掌握构建Unity多人对战平台的核心知识,为进一步开发打下基础。
675 0
|
Apache 图形学
WebGL☀️Unity WebGL适配到各平台的教程
WebGL☀️Unity WebGL适配到各平台的教程

热门文章

最新文章