• Unity实现Camera和Audio数据的低延迟RTMP推送技术探讨


    关于Unity实现RTMP直播推送技术方案,之前零散的写过几篇介绍,得到了好多开发者的关注。以Android平台为例,目前视频这块,我们demo实现的是Camera数据的采集,然后编码投递到底层,如果设备没有性能瓶颈,可达到高帧率(60帧)均匀的RTMP推送效果。

    视频采集这块,不再是难题,用ReadPixels从当前Render Target读取到图像数据即可,视频编码的话,我们分软编码、硬编码两块,硬编码,我们又实现了native层的硬编(5.0+以上版本),效率更高,native层我们做的可圈可点的一个地方是,armv7a也可以支持。

    视频采集这块,需要考虑的是,如果场景分辨率发送变化,需要自动适配,帧与帧之间的连贯问题。

    数据的采集,也可以参考官方给出来的示例:

    1. using System.IO;
    2. using UnityEngine;
    3. using UnityEngine.Networking;
    4. using System.Collections;
    5. public class ExampleClass : MonoBehaviour
    6. {
    7. // Take a shot immediately
    8. IEnumerator Start()
    9. {
    10. UploadPNG();
    11. yield return null;
    12. }
    13. IEnumerator UploadPNG()
    14. {
    15. // We should only read the screen buffer after rendering is complete
    16. yield return new WaitForEndOfFrame();
    17. // Create a texture the size of the screen, RGB24 format
    18. int width = Screen.width;
    19. int height = Screen.height;
    20. Texture2D tex = new Texture2D(width, height, TextureFormat.RGB24, false);
    21. // Read screen contents into the texture
    22. tex.ReadPixels(new Rect(0, 0, width, height), 0, 0);
    23. tex.Apply();
    24. // Encode texture into PNG
    25. byte[] bytes = tex.EncodeToPNG();
    26. Destroy(tex);
    27. // For testing purposes, also write to a file in the project folder
    28. // File.WriteAllBytes(Application.dataPath + "/../SavedScreen.png", bytes);
    29. // Create a Web Form
    30. WWWForm form = new WWWForm();
    31. form.AddField("frameCount", Time.frameCount.ToString());
    32. form.AddBinaryData("fileUpload", bytes);
    33. // Upload to a cgi script
    34. var w = UnityWebRequest.Post("http://localhost/cgi-bin/env.cgi?post", form);
    35. yield return w.SendWebRequest();
    36. if (w.result != UnityWebRequest.Result.Success)
    37. print(w.error);
    38. else
    39. print("Finished Uploading Screenshot");
    40. yield return null;
    41. }
    42. }

    有人说官方示例太简单,实际上官方示例,拿到数据,直接投递到底层即可,底层接口设计如下:

    1. /*
    2. * Github: https://github.com/daniulive/SmarterStreaming
    3. */
    4. private const String OnPostLayerImageRGBA8888Native_MethodName = "OnPostLayerImageRGBA8888Native";
    5. private int postLayerImageRGBA8888Native(long handle, int index, int left, int top,
    6. long rgba_plane, int offset, int row_stride, int width, int height,
    7. int is_vertical_flip, int is_horizontal_flip,
    8. int scale_width, int scale_height, int scale_filter_mode,
    9. int rotation_degree){
    10. return obj_.Call<int>(OnPostLayerImageRGBA8888Native_MethodName, handle, index, left, top,
    11. rgba_plane, offset, row_stride, width, height,
    12. is_vertical_flip, is_horizontal_flip,
    13. scale_width, scale_height, scale_filter_mode, rotation_degree);
    14. }

    音频的话,我们支持了几种模式:

    1. /*定义Audio源选项*/
    2. public enum PB_AUDIO_OPTION : uint
    3. {
    4. AUDIO_OPTION_CAPTURE_MIC = 0x0, /*采集麦克风音频*/
    5. AUDIO_OPTION_EXTERNAL_PCM_DATA = 0x1, /*外部PCM数据*/
    6. AUDIO_OPTION_MIC_EXTERNAL_PCM_MIXER = 0x2, /*麦克风+外部PCM数据混音*/
    7. AUDIO_OPTION_TWO_EXTERNAL_PCM_MIXER = 0x3, /* 两路外部PCM数据混音*/
    8. }

    分别是采集麦克风、外部PCM、麦克风和外部PCM混音、两路外部PCM的混音,几种模式。

    麦克风的数据采集,我们是直接基于原生的Android,通过Unity调用Android实现数据采集推送,外部PCM数据,我们以AudioClip为例,读取到数据,每隔10ms传下去,两路外部PCM也就是两路AudioClip数据投递,JNI层做混音。麦克风和外部PCM数据混音,实际上是为了达到类似授课或者讲解过程中,自带背景音的效果,需要注意的是,AudioClip读到的是float类型的数据,有些音频编码器需要sint16格式,也可以在上层或者底层做下转换。

    音频这块,如果是读取文件,还需要考虑的是,如果audio source读过之后,是从头读,还是后面静音?当然不管哪种实现都不难。

    数据有了,实现RTMP推送这块,小菜一碟了,由于我们有多年的RTMP推送方面的技术积累,对我们来说,无非就是多一种类型的数据源而已。

    经过实际测试,配合我们自研的RTMP播放器,轻松实现超过50帧的RTMP毫秒级延迟的体验,足够应对大多数行业场景了。

  • 相关阅读:
    S7-1500和精智触摸屏通过Program_Alarm指令实现离散量报警的具体方法
    Go基础11-理解Go语言的包导入
    【matplotlib 实战】--南丁格尔玫瑰图
    C++Qt开发——动画框架、状态机框架
    zk的二阶段提交图解
    通关GO语言20 协作开发:模块化管理为什么能够提升研发效能?
    【SA8295P 源码分析 (一)】01 - SA8295P 芯片介绍
    【Java】线程&程序计数器
    STM32与ZigBee技术在智能家居无线通信中的应用研究
    安全+Linux!IBM新一代大型机Z14全新发布
  • 原文地址:https://blog.csdn.net/renhui1112/article/details/126683565