├── VideoStreamingServerPackages
├── Native~
│ ├── H264Encoder
│ │ ├── stdafx.cpp
│ │ ├── H264Encoder.vcxproj.user
│ │ ├── targetver.h
│ │ ├── stdafx.h
│ │ ├── dllmain.cpp
│ │ ├── H264Encoder.vcxproj.filters
│ │ └── H264Encoder.vcxproj
│ └── H264Encoder.sln
├── Plugins
│ ├── H264Encoder.dll
│ └── H264Encoder.dll.meta
├── Runtime
│ ├── Sdp.meta
│ ├── Internal.meta
│ ├── Messages.meta
│ ├── com.unity.ig.video-streaming.server.asmdef.meta
│ ├── Messages
│ │ ├── RTSPRequestRecord.cs
│ │ ├── PortCouple.cs.meta
│ │ ├── RTSPChunk.cs.meta
│ │ ├── RTSPData.cs.meta
│ │ ├── RTSPHeaderNames.cs.meta
│ │ ├── RTSPMessage.cs.meta
│ │ ├── RTSPRequest.cs.meta
│ │ ├── RTSPRequestPlay.cs.meta
│ │ ├── RTSPResponse.cs.meta
│ │ ├── RTSPTransport.cs.meta
│ │ ├── RTSPRequestAnnounce.cs.meta
│ │ ├── RTSPRequestDescribe.cs.meta
│ │ ├── RTSPRequestOptions.cs.meta
│ │ ├── RTSPRequestPause.cs.meta
│ │ ├── RTSPRequestRecord.cs.meta
│ │ ├── RTSPRequestSetup.cs.meta
│ │ ├── RTSPRequestTeardown.cs.meta
│ │ ├── RTSPRequestGetParameter.cs.meta
│ │ ├── RTSPRequestPlay.cs
│ │ ├── RTSPRequestPause.cs
│ │ ├── RTSPRequestAnnounce.cs
│ │ ├── RTSPRequestTeardown.cs
│ │ ├── RTSPRequestDescribe.cs
│ │ ├── RTSPRequestGetParameter.cs
│ │ ├── RTSPHeaderNames.cs
│ │ ├── RTSPRequestOptions.cs
│ │ ├── RTSPRequestSetup.cs
│ │ ├── RTSPChunk.cs
│ │ ├── RTSPData.cs
│ │ ├── PortCouple.cs
│ │ ├── RTSPRequest.cs
│ │ ├── RTSPResponse.cs
│ │ ├── RTSPMessage.cs
│ │ └── RTSPTransport.cs
│ ├── BitStream.cs.meta
│ ├── RTSPUtils.cs.meta
│ ├── Sdp
│ │ ├── Media.cs.meta
│ │ ├── Attribut.cs.meta
│ │ ├── Bandwidth.cs.meta
│ │ ├── Connection.cs.meta
│ │ ├── Origin.cs.meta
│ │ ├── SdpFile.cs.meta
│ │ ├── Timing.cs.meta
│ │ ├── AttributFmtp.cs.meta
│ │ ├── AttributRtpMap.cs.meta
│ │ ├── ConnectionIP4.cs.meta
│ │ ├── ConnectionIP6.cs.meta
│ │ ├── EncriptionKey.cs.meta
│ │ ├── H264Parameter.cs.meta
│ │ ├── H265Parameter.cs.meta
│ │ ├── SdpTimeZone.cs.meta
│ │ ├── Bandwidth.cs
│ │ ├── Timing.cs
│ │ ├── EncriptionKey.cs
│ │ ├── SdpTimeZone.cs
│ │ ├── ConnectionIP6.cs
│ │ ├── ConnectionIP4.cs
│ │ ├── Connection.cs
│ │ ├── Media.cs
│ │ ├── AttributRtpMap.cs
│ │ ├── AttributFmtp.cs
│ │ ├── Attribut.cs
│ │ ├── Origin.cs
│ │ ├── H264Parameter.cs
│ │ ├── H265Parameter.cs
│ │ └── SdpFile.cs
│ ├── UdpSocket.cs.meta
│ ├── AACPayload.cs.meta
│ ├── AMRPayload.cs.meta
│ ├── Authentication.cs.meta
│ ├── G711Payload.cs.meta
│ ├── H264Encoder.cs.meta
│ ├── H264Payload.cs.meta
│ ├── H265Payload.cs.meta
│ ├── IRTSPTransport.cs.meta
│ ├── RTPPacketUtil.cs.meta
│ ├── RTSPListener.cs.meta
│ ├── RtspServer.cs.meta
│ ├── RTSPTCPTransport.cs.meta
│ ├── RTSPMessageEventArgs.cs.meta
│ ├── VideoStreamingServer.cs.meta
│ ├── Internal
│ │ ├── H264EncoderPlugin.cs.meta
│ │ └── H264EncoderPlugin.cs
│ ├── com.unity.ig.video-streaming.server.asmdef
│ ├── RTSPUtils.cs
│ ├── RTSPMessageEventArgs.cs
│ ├── AMRPayload.cs
│ ├── IRTSPTransport.cs
│ ├── RTPPacketUtil.cs
│ ├── G711Payload.cs
│ ├── BitStream.cs
│ ├── RTSPTCPTransport.cs
│ ├── AACPayload.cs
│ ├── H264Encoder.cs
│ ├── Authentication.cs
│ ├── UdpSocket.cs
│ ├── H264Payload.cs
│ ├── VideoStreamingServer.cs
│ └── H265Payload.cs
├── Shaders
│ ├── RGBToNV12.shader.meta
│ └── RGBToNV12.shader
└── package.json
├── .github
└── ISSUE_TEMPLATE
│ └── custom.md
├── README.md
└── Packages
└── manifest.json
/VideoStreamingServerPackages/Native~/H264Encoder/stdafx.cpp:
--------------------------------------------------------------------------------
1 | #include "stdafx.h"
2 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Plugins/H264Encoder.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ezharjan/UnityVideoStreamingPackage/HEAD/VideoStreamingServerPackages/Plugins/H264Encoder.dll
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Custom issue template
3 | about: Describe this issue template's purpose here.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Native~/H264Encoder/H264Encoder.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5df94805077263c4aaf1f9bfb11234fd
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Internal.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 87ebdf4f18b7b034c8a147d73aeaef51
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 318328a1821c6d547ab4e2f085f48dca
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/com.unity.ig.video-streaming.server.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 144152992fa66ea47938c83ea250308d
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Shaders/RGBToNV12.shader.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 027b7d2de9ddae1469c9062583e9ee6a
3 | ShaderImporter:
4 | externalObjects: {}
5 | defaultTextures: []
6 | nonModifiableTextures: []
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestRecord.cs:
--------------------------------------------------------------------------------
1 | namespace Unity.Ig.VideoStreaming.Server.Messages
2 | {
3 | public class RtspRequestRecord : RtspRequest
4 | {
5 | public RtspRequestRecord()
6 | {
7 | Command = "RECORD * RTSP/1.0";
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/BitStream.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 53076b446d7104f4381e17017a38dd7d
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/RTSPUtils.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e9adfaf54d2ca1644a6450f189c6d357
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/Media.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d7c81ae5695daab42a87e9c9a9016d1c
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/UdpSocket.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 225aa63e79bc0d74d86f7fb8cde0aa13
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/AACPayload.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: db64fbce65b5d2e4da2fd9b1779db575
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/AMRPayload.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 64fd405d92c453f4a91253b39ade4d81
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Authentication.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 653a84ab7b23b7d4bb27eddb1a5e6289
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/G711Payload.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 676dc96f37ada454a995ffb7468a45a8
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/H264Encoder.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: fe90b55e11ed7e04ba873c72ae53d14a
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/H264Payload.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5e9fda91b28d1ef4aaad474784293832
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/H265Payload.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: baafe81c99db6a94881fa83f50193019
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/IRTSPTransport.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 97fc6e928c6718643889368441f39c21
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/RTPPacketUtil.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b74aee1e95228c949a15c569744b3b8d
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/RTSPListener.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 6fd4c7ebb0ed21c469c30ffba2e43ca9
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/RtspServer.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9f0067a727dc7ec49be20ffc44586597
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/Attribut.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d318e08f19991fc4494690cacc0becaa
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/Bandwidth.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: df1d50d2e58785a4fabc15059f33da8c
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/Connection.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 44697c9949e51104a86463b45c3b2e07
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/Origin.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 6d888a2228180fc4c8de84b7ed1a0135
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/SdpFile.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 860d6a8dd9dfa8440be2e3bd5b5a185a
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/Timing.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 91c990a7b4c0c064a871245e57d558b6
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/PortCouple.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: aff663461cd63db41b3ab0c6d8b14967
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPChunk.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0d92562925fa88844bb76a00ed9b228d
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPData.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 985d9a5256acba44c9dec6976aeceaa4
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/RTSPTCPTransport.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3c13b11f4d1b6cc45988ae017975aa35
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/AttributFmtp.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c4705f176fed1d14ea19c991fef528f0
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/AttributRtpMap.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 53d3cb3126247484fae61430e4662f21
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/ConnectionIP4.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c19b65fca37deb24e96d02b10f7040e7
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/ConnectionIP6.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: dfe4c8163dd1f7246b3f3fdbd65cea33
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/EncriptionKey.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7051b4295c5367f488a198fe88634ed9
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/H264Parameter.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 57c5da9b14e7227479a56399f2340f47
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/H265Parameter.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 135699988c31b634f94a8b0f77811650
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/SdpTimeZone.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: bbeafe6176f214a438587c96c2f600b4
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPHeaderNames.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 127b57b12b01ced4a8a973da24fc09f5
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPMessage.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ee7dc418d14757e4eb5df313326125ba
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequest.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1bd9f783ac360bb4bbd3298d979dc086
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestPlay.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1f9b1816423266d4393d61321e146f61
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPResponse.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8f9108ab8774e43439deb1670b0b9da6
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPTransport.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d85cd3863ea2a7747988253cd14798c0
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/RTSPMessageEventArgs.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e4b5b89a1c5710940b04f4a58b65fcdf
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/VideoStreamingServer.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c339252bdd8d1ba4ab70c785ef665a2b
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Internal/H264EncoderPlugin.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 81d8f6787b48b264f9c29a5c9865546e
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestAnnounce.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b4f3a122a2c2adc4481b275dd30f49cf
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestDescribe.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: cb44387fb1ea71c4b853042f823c06f9
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestOptions.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 560d7a6b5da792d4f959bf4483eba52c
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestPause.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 36385b58c4c57c34a90e8bcc6b4e0401
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestRecord.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 163d2aa8dbff4a7488f30bf2d197c855
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestSetup.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7f41b5afc32fb06409178073c2f67eb1
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestTeardown.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 19f6c0d6760a4a942a8f06107287543c
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestGetParameter.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: dfd7ae93280f80a49a646f445ef181f2
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Native~/H264Encoder/targetver.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | // Including SDKDDKVer.h defines the highest available Windows platform.
4 |
5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
7 |
8 | #include
9 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestPlay.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server.Messages
7 | {
8 | public class RtspRequestPlay : RtspRequest
9 | {
10 |
11 | // Constructor
12 | public RtspRequestPlay()
13 | {
14 | Command = "PLAY * RTSP/1.0";
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/com.unity.ig.video-streaming.server.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Unity.Ig.VideoStreaming.Server",
3 | "references": ["Unity.Collections"],
4 | "optionalUnityReferences": [],
5 | "includePlatforms": [],
6 | "excludePlatforms": [],
7 | "allowUnsafeCode": true,
8 | "overrideReferences": false,
9 | "precompiledReferences": [],
10 | "autoReferenced": true,
11 | "defineConstraints": []
12 | }
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestPause.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server.Messages
7 | {
8 | public class RtspRequestPause : RtspRequest
9 | {
10 |
11 | // Constructor
12 | public RtspRequestPause()
13 | {
14 | Command = "PAUSE * RTSP/1.0";
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestAnnounce.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server.Messages
7 | {
8 | public class RtspRequestAnnounce : RtspRequest
9 | {
10 | // constructor
11 |
12 | public RtspRequestAnnounce()
13 | {
14 | Command = "ANNOUNCE * RTSP/1.0";
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestTeardown.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server.Messages
7 | {
8 | public class RtspRequestTeardown : RtspRequest
9 | {
10 |
11 | // Constructor
12 | public RtspRequestTeardown()
13 | {
14 | Command = "TEARDOWN * RTSP/1.0";
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestDescribe.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server.Messages
7 | {
8 | public class RtspRequestDescribe : RtspRequest
9 | {
10 |
11 | // constructor
12 |
13 | public RtspRequestDescribe()
14 | {
15 | Command = "DESCRIBE * RTSP/1.0";
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestGetParameter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server.Messages
7 | {
8 | public class RtspRequestGetParameter : RtspRequest
9 | {
10 |
11 | // Constructor
12 | public RtspRequestGetParameter()
13 | {
14 | Command = "GET_PARAMETER * RTSP/1.0";
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/Bandwidth.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server.Sdp
7 | {
8 | public class Bandwidth
9 | {
10 | public Bandwidth()
11 | {
12 | }
13 |
14 | internal static Bandwidth Parse(string value)
15 | {
16 | //TODO really parse.
17 | return new Bandwidth();
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Native~/H264Encoder/stdafx.h:
--------------------------------------------------------------------------------
1 | // stdafx.h : include file for standard system include files,
2 | // or project specific include files that are used frequently, but
3 | // are changed infrequently
4 | //
5 |
6 | #pragma once
7 |
8 | #include "targetver.h"
9 |
10 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
11 | // Windows Header Files
12 | #include
13 |
14 |
15 |
16 | // reference additional headers your program requires here
17 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/Timing.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server.Sdp
7 | {
8 | public class Timing
9 | {
10 | private string timing;
11 | private string repeat;
12 |
13 | public Timing(string timing, string repeat)
14 | {
15 | // TODO: Complete member initialization
16 | this.timing = timing;
17 | this.repeat = repeat;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/RTSPUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server
7 | {
8 | public static class RtspUtils
9 | {
10 | ///
11 | /// Registers the URI.
12 | ///
13 | public static void RegisterUri()
14 | {
15 | if (!UriParser.IsKnownScheme("rtsp"))
16 | UriParser.Register(new HttpStyleUriParser(), "rtsp", 554);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Native~/H264Encoder/dllmain.cpp:
--------------------------------------------------------------------------------
1 | // dllmain.cpp : Defines the entry point for the DLL application.
2 | #include "stdafx.h"
3 |
4 | BOOL APIENTRY DllMain( HMODULE hModule,
5 | DWORD ul_reason_for_call,
6 | LPVOID lpReserved
7 | )
8 | {
9 | switch (ul_reason_for_call)
10 | {
11 | case DLL_PROCESS_ATTACH:
12 | case DLL_THREAD_ATTACH:
13 | case DLL_THREAD_DETACH:
14 | case DLL_PROCESS_DETACH:
15 | break;
16 | }
17 | return TRUE;
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.unity.ig.video-streaming.server",
3 | "displayName": "VideoStreaming Server",
4 | "version": "0.0.1",
5 | "unity": "2018.4",
6 | "description": "Wraps native platform APIs for performing H.264 encoding and RTP streaming.",
7 | "dependencies": {
8 | "com.unity.collections": "0.0.9-preview12"
9 | },
10 | "keywords": [
11 | "unity",
12 | "media",
13 | "video",
14 | "streaming",
15 | "live",
16 | "h.264",
17 | "rtp",
18 | "rtsp"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/EncriptionKey.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Diagnostics.Contracts;
6 |
7 | namespace Unity.Ig.VideoStreaming.Server.Sdp
8 | {
9 | public class EncriptionKey
10 | {
11 | public EncriptionKey(string p)
12 | {
13 | }
14 |
15 | public static EncriptionKey ParseInvariant(string value)
16 | {
17 | if (value == null)
18 | throw new ArgumentNullException("value");
19 |
20 | Contract.EndContractBlock();
21 |
22 | throw new NotImplementedException();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/SdpTimeZone.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Diagnostics.Contracts;
6 |
7 | namespace Unity.Ig.VideoStreaming.Server.Sdp
8 | {
9 | public class SdpTimeZone
10 | {
11 | public SdpTimeZone()
12 | {
13 | }
14 |
15 | public static SdpTimeZone ParseInvariant(string value)
16 | {
17 | if (value == null)
18 | throw new ArgumentNullException("value");
19 | Contract.EndContractBlock();
20 |
21 | SdpTimeZone returnValue = new SdpTimeZone();
22 |
23 | throw new NotImplementedException();
24 | // return returnValue;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPHeaderNames.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server.Messages
7 | {
8 | ///
9 | /// Class containing helper constant for general use headers.
10 | ///
11 | public static class RtspHeaderNames
12 | {
13 | public const string ContentBase = "Content-Base";
14 | public const string ContentEncoding = "Content-Encoding";
15 | public const string ContentType = "Content-Type";
16 |
17 | public const string Public = "Public";
18 | public const string Session = "Session";
19 | public const string Transport = "Transport";
20 |
21 | public const string WWWAuthenticate = "WWW-Authenticate";
22 | public const string Authorization = "Authorization";
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/RTSPMessageEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server
7 | {
8 | using Messages;
9 | ///
10 | /// Event args containing information for message events.
11 | ///
12 | public class RtspChunkEventArgs :EventArgs
13 | {
14 |
15 | ///
16 | /// Initializes a new instance of the class.
17 | ///
18 | /// A message.
19 | public RtspChunkEventArgs(RtspChunk aMessage)
20 | {
21 | Message = aMessage;
22 | }
23 |
24 | ///
25 | /// Gets or sets the message.
26 | ///
27 | /// The message.
28 | public RtspChunk Message { get; set; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/AMRPayload.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace Unity.Ig.VideoStreaming.Server
6 | {
7 | // This class handles the AMR Payload
8 | // It has methods to process the RTP Payload
9 |
10 | public class AMRPayload
11 | {
12 | // Constructor
13 | public AMRPayload()
14 | {
15 | }
16 |
17 | public List Process_AMR_RTP_Packet(byte[] rtp_payload, int rtp_marker) {
18 |
19 | // Octet-Aligned Mode (RFC 4867 Section 4.4.1)
20 |
21 | // First byte is the Payload Header
22 | if (rtp_payload.Length < 1) return null;
23 | byte payloadHeader = rtp_payload[0];
24 |
25 | // The rest of the RTP packet is the AMR data
26 | List audio_data = new List();
27 |
28 | byte[] amr_data = new byte[rtp_payload.Length - 1];
29 | System.Array.Copy(rtp_payload,1,amr_data,0,rtp_payload.Length-1);
30 | audio_data.Add(amr_data);
31 |
32 | return audio_data;
33 | }
34 |
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server.Messages
7 | {
8 | public class RtspRequestOptions : RtspRequest
9 | {
10 |
11 | // Constructor
12 | public RtspRequestOptions()
13 | {
14 | Command = "OPTIONS * RTSP/1.0";
15 | }
16 |
17 | ///
18 | /// Gets the assiociate OK response with the request.
19 | ///
20 | ///
21 | /// an Rtsp response corresponding to request.
22 | ///
23 | public override RtspResponse CreateResponse()
24 | {
25 | RtspResponse response = base.CreateResponse();
26 | // Add genric suported operations.
27 | response.Headers.Add(RtspHeaderNames.Public, "OPTIONS,DESCRIBE,ANNOUNCE,SETUP,PLAY,PAUSE,TEARDOWN,GET_PARAMETER,SET_PARAMETER,REDIRECT");
28 |
29 | return response;
30 | }
31 |
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Unity Video Streaming Server Package
2 |
3 |
4 |
5 | ## Introduction
6 |
7 | This is a Unity 3D package for live video streaming using RTP and the server here includes RTSP server which wraps native platform APIs for performing H.264 encoding and RTP streaming.
8 |
9 |
10 |
11 | ## Utilization
12 |
13 | 1. Replace `Packages` folder in your Unity 3D project to the one in this folder, which namely means that you should add this folder and other two packages as dependencies in your `Packages/manifest.json` file.
14 | 2. Place `VideoStreamingServerPackages` folder into your Unity 3D project's root folder in order to make the path described in `manifest.json` the same as in real path, otherwise you should change the path described in `manifest.json` file.
15 | 3. Use it as you want. The native C++ file is in `Native~` folder and you can even redevelop it yourself.
16 | 4. The main script is `VideoStreamingServer` and you can use it as a component.
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Alexander Ezharjan
9th Sep, 2020
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/ConnectionIP6.cs:
--------------------------------------------------------------------------------
1 | namespace Unity.Ig.VideoStreaming.Server.Sdp
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Net;
6 | using System.Globalization;
7 |
8 | public class ConnectionIP6 : Connection
9 | {
10 | internal new static ConnectionIP6 Parse(string ipAddress)
11 | {
12 | string[] parts = ipAddress.Split('/');
13 |
14 | if (parts.Length > 2)
15 | throw new FormatException("Too much address subpart in " + ipAddress);
16 |
17 | ConnectionIP6 result = new ConnectionIP6();
18 |
19 | result.Host = parts[0];
20 |
21 | int numberOfAddress;
22 | if (parts.Length > 1)
23 | {
24 | if (!int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out numberOfAddress))
25 | throw new FormatException("Invalid number of address : " + parts[1]);
26 | result.NumberOfAddress = numberOfAddress;
27 | }
28 |
29 | return result;
30 |
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/IRTSPTransport.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Unity.Ig.VideoStreaming.Server
4 | {
5 | ///
6 | /// Interface for Transport of Rtsp (TCP, TCP+SSL,..)
7 | ///
8 | public interface IRtspTransport
9 | {
10 | ///
11 | /// Gets the stream of the transport.
12 | ///
13 | /// A stream
14 | System.IO.Stream GetStream();
15 |
16 | ///
17 | /// Gets the remote address.
18 | ///
19 | /// The remote address.
20 | string RemoteAddress
21 | {
22 | get;
23 | }
24 |
25 | ///
26 | /// Closes this instance.
27 | ///
28 | void Close();
29 |
30 | ///
31 | /// Gets a value indicating whether this is connected.
32 | ///
33 | /// true if connected; otherwise, false.
34 | bool Connected { get; }
35 |
36 | ///
37 | /// Reconnect this instance.
38 | /// Must do nothing if already connected.
39 | ///
40 | /// Error during socket
41 | void Reconnect();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequestSetup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server.Messages
7 | {
8 | public class RtspRequestSetup : RtspRequest
9 | {
10 |
11 | // Constructor
12 | public RtspRequestSetup()
13 | {
14 | Command = "SETUP * RTSP/1.0";
15 | }
16 |
17 |
18 | ///
19 | /// Gets the transports associate with the request.
20 | ///
21 | /// The transport.
22 | public RtspTransport[] GetTransports()
23 | {
24 |
25 | if (!Headers.ContainsKey(RtspHeaderNames.Transport))
26 | return new RtspTransport[] { new RtspTransport() };
27 |
28 | string[] items = Headers[RtspHeaderNames.Transport].Split(',');
29 | return Array.ConvertAll(items,
30 | new Converter(RtspTransport.Parse));
31 |
32 | }
33 |
34 | public void AddTransport(RtspTransport newTransport)
35 | {
36 | string actualTransport = string.Empty;
37 | if(Headers.ContainsKey(RtspHeaderNames.Transport))
38 | actualTransport = Headers[RtspHeaderNames.Transport] + ",";
39 | Headers[RtspHeaderNames.Transport] = actualTransport + newTransport.ToString();
40 |
41 |
42 |
43 | }
44 |
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/ConnectionIP4.cs:
--------------------------------------------------------------------------------
1 | namespace Unity.Ig.VideoStreaming.Server.Sdp
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Globalization;
6 | using System.Net;
7 |
8 | public class ConnectionIP4 : Connection
9 | {
10 |
11 | public int Ttl { get; set; }
12 |
13 | internal new static ConnectionIP4 Parse(string ipAddress)
14 | {
15 | string[] parts = ipAddress.Split('/');
16 |
17 | if (parts.Length > 3)
18 | throw new FormatException("Too much address subpart in " + ipAddress);
19 |
20 | ConnectionIP4 result = new ConnectionIP4();
21 |
22 | result.Host = parts[0];
23 |
24 | int ttl;
25 | if (parts.Length > 1)
26 | {
27 | if (!int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out ttl))
28 | throw new FormatException("Invalid TTL format : " + parts[1]);
29 | result.Ttl = ttl;
30 | }
31 | int numberOfAddress;
32 | if (parts.Length > 2)
33 | {
34 | if (!int.TryParse(parts[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out numberOfAddress))
35 | throw new FormatException("Invalid number of address : " + parts[2]);
36 | result.NumberOfAddress = numberOfAddress;
37 | }
38 |
39 | return result;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/RTPPacketUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 |
8 | public static class RTPPacketUtil
9 | {
10 |
11 | public static void WriteHeader(byte[] rtp_packet, int rtp_version, int rtp_padding, int rtp_extension, int rtp_csrc_count, int rtp_marker, int rtp_payload_type)
12 | {
13 | rtp_packet[0] = (byte)((rtp_version << 6) | (rtp_padding << 5) | (rtp_extension << 4) | rtp_csrc_count);
14 | rtp_packet[1] = (byte)((rtp_marker << 7) | (rtp_payload_type & 0x7F));
15 | }
16 |
17 | public static void WriteSequenceNumber(byte[] rtp_packet, uint empty_sequence_id)
18 | {
19 | rtp_packet[2] = ((byte)((empty_sequence_id >> 8) & 0xFF));
20 | rtp_packet[3] = ((byte)((empty_sequence_id >> 0) & 0xFF));
21 | }
22 |
23 | public static void WriteTS(byte[] rtp_packet, uint ts)
24 | {
25 | rtp_packet[4] = ((byte)((ts >> 24) & 0xFF));
26 | rtp_packet[5] = ((byte)((ts >> 16) & 0xFF));
27 | rtp_packet[6] = ((byte)((ts >> 8) & 0xFF));
28 | rtp_packet[7] = ((byte)((ts >> 0) & 0xFF));
29 | }
30 |
31 | public static void WriteSSRC(byte[] rtp_packet, uint ssrc)
32 | {
33 | rtp_packet[8] = ((byte)((ssrc >> 24) & 0xFF));
34 | rtp_packet[9] = ((byte)((ssrc >> 16) & 0xFF));
35 | rtp_packet[10] = ((byte)((ssrc >> 8) & 0xFF));
36 | rtp_packet[11] = ((byte)((ssrc >> 0) & 0xFF));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Native~/H264Encoder/H264Encoder.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Header Files
20 |
21 |
22 | Header Files
23 |
24 |
25 |
26 |
27 | Source Files
28 |
29 |
30 | Source Files
31 |
32 |
33 | Source Files
34 |
35 |
36 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Native~/H264Encoder.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28307.645
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "H264Encoder", "H264Encoder\H264Encoder.vcxproj", "{45BDF642-D8AA-400A-A088-A66D09B1E247}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Debug|x86 = Debug|x86
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {45BDF642-D8AA-400A-A088-A66D09B1E247}.Debug|x64.ActiveCfg = Debug|x64
17 | {45BDF642-D8AA-400A-A088-A66D09B1E247}.Debug|x64.Build.0 = Debug|x64
18 | {45BDF642-D8AA-400A-A088-A66D09B1E247}.Debug|x86.ActiveCfg = Debug|Win32
19 | {45BDF642-D8AA-400A-A088-A66D09B1E247}.Debug|x86.Build.0 = Debug|Win32
20 | {45BDF642-D8AA-400A-A088-A66D09B1E247}.Release|x64.ActiveCfg = Release|x64
21 | {45BDF642-D8AA-400A-A088-A66D09B1E247}.Release|x64.Build.0 = Release|x64
22 | {45BDF642-D8AA-400A-A088-A66D09B1E247}.Release|x86.ActiveCfg = Release|Win32
23 | {45BDF642-D8AA-400A-A088-A66D09B1E247}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {00188A34-F7F4-42F0-B379-438E7DCCEDDA}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Internal/H264EncoderPlugin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | internal struct H264EncoderPlugin
5 | {
6 | [DllImport("H264Encoder", EntryPoint ="Create")]
7 | extern public static IntPtr CreateEncoder(uint width, uint height, uint frameRateNumerator, uint frameRateDenominator, uint averageBitRate);
8 |
9 | [DllImport("H264Encoder", EntryPoint = "Destroy")]
10 | [return: MarshalAs(UnmanagedType.U1)]
11 | extern public static bool DestroyEncoder(IntPtr encoder);
12 |
13 | [DllImport("H264Encoder", EntryPoint = "Encode")]
14 | [return: MarshalAs(UnmanagedType.U1)]
15 | extern public unsafe static bool EncodeFrame(IntPtr encoder, byte* pixelData, ulong timeStampNs);
16 |
17 | [DllImport("H264Encoder", EntryPoint = "BeginConsume")]
18 | [return: MarshalAs(UnmanagedType.U1)]
19 | extern public static bool BeginConsumeEncodedBuffer(IntPtr encoder, out uint sizeOut);
20 |
21 | [DllImport("H264Encoder", EntryPoint = "EndConsume")]
22 | [return: MarshalAs(UnmanagedType.U1)]
23 | extern public unsafe static bool EndConsumeEncodedBuffer(
24 | IntPtr encoder,
25 | byte* dst,
26 | out ulong timeStampNs,
27 | [MarshalAs(UnmanagedType.U1)] out bool isKeyFrame);
28 |
29 | [DllImport("H264Encoder", EntryPoint = "GetSps")]
30 | extern public unsafe static uint GetSpsNAL(IntPtr encoder, byte* spsData);
31 |
32 | [DllImport("H264Encoder", EntryPoint = "GetPps")]
33 | extern public unsafe static uint GetPpsNAL(IntPtr encoder, byte* ppsData);
34 | }
35 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPChunk.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | namespace Unity.Ig.VideoStreaming.Server.Messages
5 | {
6 | ///
7 | /// Class wich represent each message echanged on Rtsp socket.
8 | ///
9 | public abstract class RtspChunk : ICloneable
10 | {
11 | ///
12 | /// Logs the message to debug.
13 | ///
14 | public void LogMessage()
15 | {
16 | LogMessage(LogType.Log);
17 | }
18 |
19 | ///
20 | /// Logs the message.
21 | ///
22 | /// The log level.
23 | public abstract void LogMessage(LogType aLevel);
24 |
25 | ///
26 | /// Gets or sets the data associate with the message.
27 | ///
28 | /// Array of byte transmit with the message.
29 | public byte[] Data
30 | { get; set; }
31 |
32 | ///
33 | /// Gets or sets the source port wich receive the message.
34 | ///
35 | /// The source port.
36 | public RtspListener SourcePort { get; set; }
37 |
38 | #region ICloneable Membres
39 |
40 | ///
41 | /// Crée un nouvel objet qui est une copie de l'instance en cours.
42 | ///
43 | ///
44 | /// Nouvel objet qui est une copie de cette instance.
45 | ///
46 | public abstract object Clone();
47 |
48 | #endregion
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPData.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace Unity.Ig.VideoStreaming.Server.Messages
4 | {
5 | ///
6 | /// Message wich represent data. ($ limited message)
7 | ///
8 | public class RtspData : RtspChunk
9 | {
10 | private static ILogger _logger = new Logger(Debug.unityLogger.logHandler);
11 |
12 | static RtspData()
13 | {
14 | _logger.logEnabled = false;
15 | }
16 |
17 | ///
18 | /// Logs the message to debug.
19 | ///
20 | public override void LogMessage(LogType aLevel)
21 | {
22 | // if the level is not logged directly return
23 | if (!_logger.IsLogTypeAllowed(aLevel))
24 | return;
25 | _logger.Log(aLevel, "Data message");
26 | if (Data == null)
27 | _logger.Log(aLevel, "Data : null");
28 | else
29 | _logger.Log(aLevel, "Data length :-{0}-", Data.Length);
30 | }
31 |
32 | public int Channel { get; set; }
33 |
34 | ///
35 | /// Clones this instance.
36 | /// Listner is not cloned
37 | ///
38 | /// a clone of this instance
39 | public override object Clone()
40 | {
41 | RtspData result = new RtspData();
42 | result.Channel = this.Channel;
43 | if (this.Data != null)
44 | result.Data = this.Data.Clone() as byte[];
45 | result.SourcePort = this.SourcePort;
46 | return result;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/Connection.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Globalization;
6 | using System.Net;
7 |
8 | namespace Unity.Ig.VideoStreaming.Server.Sdp
9 | {
10 | public abstract class Connection
11 | {
12 | public Connection()
13 | {
14 | //Default value from spec
15 | NumberOfAddress = 1;
16 | }
17 |
18 | public string Host { get; set; }
19 |
20 | ///
21 | /// Gets or sets the number of address specifed in connection.
22 | ///
23 | /// The number of address.
24 | //TODO handle it a different way (list of adress ?)
25 | public int NumberOfAddress { get; set; }
26 |
27 | public static Connection Parse(string value)
28 | {
29 | if(value ==null)
30 | throw new ArgumentNullException("value");
31 |
32 | string[] parts = value.Split(' ');
33 |
34 | if (parts.Length != 3)
35 | throw new FormatException("Value do not contain 3 parts as needed.");
36 |
37 | if (parts[0] != "IN")
38 | throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Net type {0} not suported", parts[0]));
39 |
40 | switch (parts[1])
41 | {
42 | case "IP4":
43 | return ConnectionIP4.Parse(parts[2]);
44 | case "IP6":
45 | return ConnectionIP6.Parse(parts[2]);
46 | default:
47 | throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Address type {0} not suported", parts[1]));
48 | }
49 |
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Plugins/H264Encoder.dll.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2642012849465054ea2a1780f18ba369
3 | PluginImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | iconMap: {}
7 | executionOrder: {}
8 | defineConstraints: []
9 | isPreloaded: 0
10 | isOverridable: 1
11 | isExplicitlyReferenced: 0
12 | validateReferences: 1
13 | platformData:
14 | - first:
15 | '': Any
16 | second:
17 | enabled: 0
18 | settings:
19 | Exclude Editor: 0
20 | Exclude Linux: 0
21 | Exclude Linux64: 0
22 | Exclude LinuxUniversal: 0
23 | Exclude OSXUniversal: 0
24 | Exclude Win: 1
25 | Exclude Win64: 0
26 | - first:
27 | Any:
28 | second:
29 | enabled: 1
30 | settings: {}
31 | - first:
32 | Editor: Editor
33 | second:
34 | enabled: 1
35 | settings:
36 | CPU: AnyCPU
37 | DefaultValueInitialized: true
38 | OS: AnyOS
39 | - first:
40 | Facebook: Win
41 | second:
42 | enabled: 0
43 | settings:
44 | CPU: None
45 | - first:
46 | Facebook: Win64
47 | second:
48 | enabled: 0
49 | settings:
50 | CPU: AnyCPU
51 | - first:
52 | Standalone: Linux
53 | second:
54 | enabled: 1
55 | settings:
56 | CPU: x86
57 | - first:
58 | Standalone: Linux64
59 | second:
60 | enabled: 1
61 | settings:
62 | CPU: x86_64
63 | - first:
64 | Standalone: LinuxUniversal
65 | second:
66 | enabled: 1
67 | settings:
68 | CPU: AnyCPU
69 | - first:
70 | Standalone: OSXUniversal
71 | second:
72 | enabled: 1
73 | settings:
74 | CPU: AnyCPU
75 | - first:
76 | Standalone: Win
77 | second:
78 | enabled: 0
79 | settings:
80 | CPU: None
81 | - first:
82 | Standalone: Win64
83 | second:
84 | enabled: 1
85 | settings:
86 | CPU: AnyCPU
87 | userData:
88 | assetBundleName:
89 | assetBundleVariant:
90 |
--------------------------------------------------------------------------------
/Packages/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "com.unity.ads": "2.0.8",
4 | "com.unity.analytics": "3.2.2",
5 | "com.unity.collab-proxy": "1.2.15",
6 | "com.unity.collections": "0.0.9-preview.12",
7 | "com.unity.ig.video-streaming.server": "file:../VideoStreamingServerPackages/",
8 | "com.unity.mathematics": "1.1.0",
9 | "com.unity.package-manager-ui": "2.0.8",
10 | "com.unity.purchasing": "2.0.3",
11 | "com.unity.textmeshpro": "1.4.1",
12 | "com.unity.modules.ai": "1.0.0",
13 | "com.unity.modules.animation": "1.0.0",
14 | "com.unity.modules.assetbundle": "1.0.0",
15 | "com.unity.modules.audio": "1.0.0",
16 | "com.unity.modules.cloth": "1.0.0",
17 | "com.unity.modules.director": "1.0.0",
18 | "com.unity.modules.imageconversion": "1.0.0",
19 | "com.unity.modules.imgui": "1.0.0",
20 | "com.unity.modules.jsonserialize": "1.0.0",
21 | "com.unity.modules.particlesystem": "1.0.0",
22 | "com.unity.modules.physics": "1.0.0",
23 | "com.unity.modules.physics2d": "1.0.0",
24 | "com.unity.modules.screencapture": "1.0.0",
25 | "com.unity.modules.terrain": "1.0.0",
26 | "com.unity.modules.terrainphysics": "1.0.0",
27 | "com.unity.modules.tilemap": "1.0.0",
28 | "com.unity.modules.ui": "1.0.0",
29 | "com.unity.modules.uielements": "1.0.0",
30 | "com.unity.modules.umbra": "1.0.0",
31 | "com.unity.modules.unityanalytics": "1.0.0",
32 | "com.unity.modules.unitywebrequest": "1.0.0",
33 | "com.unity.modules.unitywebrequestassetbundle": "1.0.0",
34 | "com.unity.modules.unitywebrequestaudio": "1.0.0",
35 | "com.unity.modules.unitywebrequesttexture": "1.0.0",
36 | "com.unity.modules.unitywebrequestwww": "1.0.0",
37 | "com.unity.modules.vehicles": "1.0.0",
38 | "com.unity.modules.video": "1.0.0",
39 | "com.unity.modules.vr": "1.0.0",
40 | "com.unity.modules.wind": "1.0.0",
41 | "com.unity.modules.xr": "1.0.0"
42 | }
43 | }
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/Media.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace Unity.Ig.VideoStreaming.Server.Sdp
8 | {
9 | public class Media
10 | {
11 | private string mediaString;
12 |
13 | public Media(string mediaString)
14 | {
15 | // Example is 'video 0 RTP/AVP 26;
16 | this.mediaString = mediaString;
17 |
18 | var parts = mediaString.Split(new char[] { ' ' } , 4);
19 |
20 | if (parts.Count() >= 1) {
21 | if (parts[0].Equals("video")) MediaType = MediaTypes.video;
22 | else if (parts[0].Equals("audio")) MediaType = MediaTypes.audio;
23 | else if (parts[0].Equals("text")) MediaType = MediaTypes.text;
24 | else if (parts[0].Equals("application")) MediaType = MediaTypes.application;
25 | else if (parts[0].Equals("message")) MediaType = MediaTypes.message;
26 | else MediaType = MediaTypes.unknown; // standard does allow for future types to be defined
27 | }
28 |
29 | int pt;
30 | if (parts.Count() >= 4) {
31 | if(int.TryParse(parts[3], out pt))
32 | {
33 | PayloadType = pt;
34 | } else {
35 | PayloadType = 0;
36 | }
37 | }
38 | }
39 |
40 | // RFC4566 Media Types
41 | public enum MediaTypes { video, audio, text, application, message, unknown };
42 |
43 | public Connection Connection { get; set; }
44 |
45 | public Bandwidth Bandwidth { get; set; }
46 |
47 | public EncriptionKey EncriptionKey { get; set; }
48 |
49 | public MediaTypes MediaType { get; set; }
50 |
51 | public int PayloadType { get; set; }
52 |
53 | private readonly List attributs = new List();
54 |
55 | public IList Attributs
56 | {
57 | get
58 | {
59 | return attributs;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/AttributRtpMap.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server.Sdp
7 | {
8 | public class AttributRtpMap : Attribut
9 | {
10 | // Format
11 | // rtpmap: / [/]
12 | // Examples
13 | // rtpmap:96 H264/90000
14 | // rtpmap:8 PCMA/8000
15 |
16 | public const string NAME = "rtpmap";
17 |
18 | public AttributRtpMap()
19 | {
20 | }
21 |
22 | public override string Key
23 | {
24 | get
25 | {
26 | return NAME;
27 | }
28 | }
29 |
30 | public override string Value
31 | {
32 | get
33 | {
34 | if(string.IsNullOrEmpty(EncodingParameters))
35 | {
36 | return string.Format("{0} {1}/{2}", PayloadNumber, EncodingName, ClockRate);
37 | } else {
38 | return string.Format("{0} {1}/{2}/{3}", PayloadNumber, EncodingName, ClockRate, EncodingParameters);
39 | }
40 | }
41 | protected set
42 | {
43 | ParseValue(value);
44 | }
45 | }
46 |
47 | public int PayloadNumber { get; set; }
48 | public String EncodingName { get; set; }
49 | public String ClockRate { get; set; }
50 | public String EncodingParameters { get; set; }
51 |
52 | protected override void ParseValue(string value)
53 | {
54 | var parts = value.Split(new char[] { ' ', '/' });
55 |
56 | if (parts.Length >= 1) {
57 | if (int.TryParse(parts[0], out int tmp_payloadNumber))
58 | {
59 | PayloadNumber = tmp_payloadNumber;
60 | }
61 | }
62 | if (parts.Length >= 2)
63 | {
64 | EncodingName = parts[1];
65 | }
66 | if (parts.Length >= 3)
67 | {
68 | ClockRate = parts[2];
69 | }
70 | if (parts.Length >= 4)
71 | {
72 | EncodingParameters = parts[3];
73 | }
74 |
75 |
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/AttributFmtp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server.Sdp
7 | {
8 | public class AttributFmtp : Attribut
9 | {
10 | public const string NAME = "fmtp";
11 |
12 | private Dictionary parameters = new Dictionary();
13 |
14 | public AttributFmtp()
15 | {
16 | }
17 |
18 | public override string Key
19 | {
20 | get
21 | {
22 | return NAME;
23 | }
24 | }
25 |
26 | public override string Value
27 | {
28 | get
29 | {
30 | return string.Format("{0} {1}", PayloadNumber, FormatParameter);
31 | }
32 | protected set
33 | {
34 | ParseValue(value);
35 | }
36 | }
37 |
38 | public int PayloadNumber { get; set; }
39 |
40 | // temporary aatibute to store remaning data not parsed
41 | public string FormatParameter { get; set; }
42 |
43 |
44 | // Extract the Payload Number and the Format Parameters
45 | protected override void ParseValue(string value)
46 | {
47 | var parts = value.Split(new char[] { ' ' }, 2);
48 |
49 | int payloadNumber;
50 | if(int.TryParse(parts[0], out payloadNumber))
51 | {
52 | this.PayloadNumber = payloadNumber;
53 | }
54 | if(parts.Length > 1)
55 | {
56 | FormatParameter = parts[1];
57 |
58 | // Split on ';' to get a list of items.
59 | // Then Trim each item and then Split on the first '='
60 | // Add them to the dictionary
61 | parameters.Clear();
62 | foreach (var pair in parts[1].Split(';').Select(x => x.Trim().Split(new char[] { '=' }, 2))) {
63 | if (!string.IsNullOrWhiteSpace(pair[0]))
64 | parameters[pair[0]] = pair.Length > 1 ? pair[1] : null;
65 | }
66 | }
67 | }
68 |
69 | public String GetParameter(String index)
70 | {
71 | if (parameters.ContainsKey(index)) return parameters[index];
72 | else return "";
73 | }
74 |
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/Attribut.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Diagnostics.Contracts;
6 |
7 | namespace Unity.Ig.VideoStreaming.Server.Sdp
8 | {
9 | public class Attribut
10 | {
11 | private static readonly Dictionary attributMap = new Dictionary()
12 | {
13 | {AttributRtpMap.NAME,typeof(AttributRtpMap)},
14 | {AttributFmtp.NAME,typeof(AttributFmtp)},
15 | };
16 |
17 |
18 | public virtual string Key { get; private set; }
19 | public virtual string Value { get; protected set; }
20 |
21 | public static void RegisterNewAttributeType(string key, Type attributType)
22 | {
23 | if(!attributType.IsSubclassOf(typeof(Attribut)))
24 | throw new ArgumentException("Type must be subclass of Rtsp.Sdp.Attribut","attributType");
25 |
26 | attributMap[key] = attributType;
27 | }
28 |
29 |
30 |
31 | public Attribut()
32 | {
33 | }
34 |
35 | public Attribut(string key)
36 | {
37 | Key = key;
38 | }
39 |
40 |
41 | public static Attribut ParseInvariant(string value)
42 | {
43 | if(value == null)
44 | throw new ArgumentNullException("value");
45 |
46 | Contract.EndContractBlock();
47 |
48 | var listValues = value.Split(new char[] {':'}, 2);
49 |
50 |
51 | Attribut returnValue;
52 |
53 | // Call parser of child type
54 | Type childType;
55 | attributMap.TryGetValue(listValues[0], out childType);
56 | if (childType != null)
57 | {
58 | var defaultContructor = childType.GetConstructor(Type.EmptyTypes);
59 | returnValue = defaultContructor.Invoke(Type.EmptyTypes) as Attribut;
60 | }
61 | else
62 | {
63 | returnValue = new Attribut(listValues[0]);
64 | }
65 | // Parse the value. Note most attributes have a value but recvonly does not have a value
66 | if (listValues.Count() > 1) returnValue.ParseValue(listValues[1]);
67 |
68 | return returnValue;
69 | }
70 |
71 | protected virtual void ParseValue(string value)
72 | {
73 | Value = value;
74 | }
75 |
76 |
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/G711Payload.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace Unity.Ig.VideoStreaming.Server
6 | {
7 | // This class handles the G711 Payload
8 | // It has methods to process the RTP Payload
9 |
10 | public class G711Payload
11 | {
12 | // Constructor
13 | public G711Payload()
14 | {
15 | }
16 |
17 | public List Process_G711_RTP_Packet(byte[] rtp_payload, int rtp_marker) {
18 |
19 | List audio_data = new List();
20 | audio_data.Add(rtp_payload);
21 |
22 | return audio_data;
23 | }
24 |
25 | /* Untested - used with G711.1 and PCMA-WB and PCMU-WB Codec Names */
26 | public List Process_G711_1_RTP_Packet(byte[] rtp_payload, int rtp_marker) {
27 |
28 | // Look at the Header. This tells us the G711 mode being used
29 |
30 | // Mode Index (MI) is
31 | // 1 - R1 40 octets containg Layer 0 data
32 | // 2 - R2a 50 octets containing Layer 0 plus Layer 1 data
33 | // 3 - R2b 50 octets containing Layer 0 plus Layer 2 data
34 | // 4 - R3 60 octets containing Layer 0 plus Layer 1 plus Layer 2 data
35 |
36 | byte mode_index = (byte)(rtp_payload[0] & 0x07);
37 |
38 | int size_of_one_frame = 0; // will be in bytes
39 | switch (mode_index) {
40 | case 1: size_of_one_frame = 40; break;
41 | case 2: size_of_one_frame = 50; break;
42 | case 3: size_of_one_frame = 50; break;
43 | case 4: size_of_one_frame = 60; break;
44 | default: return null; // invalid Mode Index
45 | }
46 |
47 | int number_frames = (rtp_payload.Length - 1) / size_of_one_frame;
48 |
49 |
50 | // Return just the basic u-Law or A-Law audio (the Layer 0 audio)
51 |
52 | List audio_data = new List();
53 |
54 | // Extract each audio frame and place in the audio_data List
55 | int frame_start = 1; // starts just after the MI header
56 | while (frame_start + size_of_one_frame < rtp_payload.Length) {
57 | byte[] layer_0_audio = new byte[40];
58 | System.Array.Copy(rtp_payload,frame_start,layer_0_audio,0,40); // 40 octets in Layer 0 data
59 | audio_data.Add(layer_0_audio);
60 |
61 | frame_start += size_of_one_frame;
62 | }
63 | return audio_data;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Shaders/RGBToNV12.shader:
--------------------------------------------------------------------------------
1 | Shader "Video/RGBToNV12"
2 | {
3 | Properties
4 | {
5 | _MainTex ("Texture", 2D) = "white" {}
6 | }
7 | SubShader
8 | {
9 | Pass
10 | {
11 | CGPROGRAM
12 | #pragma vertex vert
13 | #pragma fragment frag
14 | #pragma multi_compile _ UNITY_COLORSPACE_GAMMA
15 |
16 | #include "UnityCG.cginc"
17 |
18 | struct appdata
19 | {
20 | float4 vertex : POSITION;
21 | float2 uv : TEXCOORD0;
22 | };
23 |
24 | struct v2f
25 | {
26 | float2 uv : TEXCOORD0;
27 | float4 vertex : SV_POSITION;
28 | };
29 |
30 | sampler2D _MainTex;
31 | float4 _MainTex_TexelSize;
32 | float4 _MainTex_ST;
33 |
34 | v2f vert (appdata v)
35 | {
36 | v2f o;
37 | o.vertex = UnityObjectToClipPos(v.vertex);
38 | o.uv = TRANSFORM_TEX(v.uv, _MainTex);
39 | return o;
40 | }
41 |
42 | // Adobe-flavored HDTV Rec.709 (2.2 gamma, 16-235 limit)
43 | fixed3 RGB2YUV(half3 rgb)
44 | {
45 | const half K_B = 0.0722;
46 | const half K_R = 0.2126;
47 |
48 | #if !UNITY_COLORSPACE_GAMMA
49 | rgb = LinearToGammaSpace(rgb);
50 | #endif
51 | half y = dot(half3(K_R, 1 - K_B - K_R, K_B), rgb);
52 | half u = ((rgb.b - y) / (1 - K_B) * 112 + 128) / 255;
53 | half v = ((rgb.r - y) / (1 - K_R) * 112 + 128) / 255;
54 |
55 | y = (y * 219 + 16) / 255;
56 |
57 | return fixed3(y, u, v);
58 | }
59 |
60 | fixed3 RGB2YUV601(half3 rgb)
61 | {
62 | #if !UNITY_COLORSPACE_GAMMA
63 | rgb = LinearToGammaSpace(rgb);
64 | #endif
65 | half y = ( 16.0F/255.0F + 0.258348F * rgb.r + 0.50676F * rgb.g + 0.099756F * rgb.b);
66 | half u = (128.0F/255.0F - 0.150534F * rgb.r - 0.295278F * rgb.g + 0.445811F * rgb.b);
67 | half v = (128.0F/255.0F + 0.440022F * rgb.r - 0.367650F * rgb.g - 0.072372F * rgb.b);
68 |
69 | return fixed3(y, u, v);
70 | }
71 |
72 | fixed frag(v2f i) : SV_Target
73 | {
74 | if (3.0F * i.uv.y < 2.0F)
75 | {
76 | // Y
77 | i.uv.y = i.uv.y * 1.5F;
78 | return RGB2YUV(tex2D(_MainTex, i.uv)).r;
79 | }
80 |
81 | // Move the y coordinate back into the main image to do the chroma subsampling.
82 | i.uv.y = 3.0F * i.uv.y - 2.0F;
83 | i.uv.y += _MainTex_TexelSize.y * 0.5F; // Offset by 1/2 line to sample in the middle of the 4 pixels.
84 |
85 | float halfTexelWidth = _MainTex_TexelSize.x * 0.5F;
86 |
87 | int x = (int)floor(i.uv.x * (_MainTex_TexelSize.z - 0.5F) + 0.5F);
88 | if (fmod(x, 2.0F) == 0.0F)
89 | {
90 | // Even columns: U
91 | // Get x position between current pixel and the one to the right, so U and V are sampled at the same column.
92 | i.uv.x += halfTexelWidth;
93 | return RGB2YUV(tex2D(_MainTex, i.uv)).g;
94 | }
95 |
96 | // Odd columns: V
97 | // Get x position between current pixel and the one to the left, so U and V are sampled at the same column.
98 | i.uv.x -= halfTexelWidth;
99 | return RGB2YUV(tex2D(_MainTex, i.uv)).b;
100 | }
101 |
102 | ENDCG
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/BitStream.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | // (c) 2018 Roger Hardiman, RJH Technical Consultancy Ltd
5 | // Simple class to Read and Write bits in a bit stream.
6 | // Data is written to the end of the bit stream and the bit stream can be returned as a Byte Array
7 | // Data can be read from the head of the bit stream
8 | // Example
9 | // bitstream.AddValue(0xA,4); // Write 4 bit value
10 | // bitstream.AddValue(0xB,4);
11 | // bitstream.AddValue(0xC,4);
12 | // bitstream.AddValue(0xD,4);
13 | // bitstream.ToArray() -> {0xAB, 0xCD} // Return Byte Array
14 | // bitstream.Read(8) -> 0xAB // Read 8 bit value
15 |
16 | namespace Unity.Ig.VideoStreaming.Server
17 | {
18 |
19 | // Very simple bitstream
20 | public class BitStream {
21 |
22 | private List data = new List(); // List only stores 0 or 1 (one 'bit' per List item)
23 |
24 | // Constructor
25 | public BitStream() {
26 | }
27 |
28 | public void AddValue(int value, int num_bits) {
29 | // Add each bit to the List
30 | for (int i = num_bits-1; i >= 0; i--) {
31 | data.Add((byte)((value>>i) & 0x01));
32 | }
33 | }
34 |
35 | public void AddHexString(String hex_string) {
36 | char[] hex_chars = hex_string.ToUpper().ToCharArray();
37 | foreach (char c in hex_chars) {
38 | if ((c.Equals('0'))) this.AddValue(0,4);
39 | else if ((c.Equals('1'))) this.AddValue(1, 4);
40 | else if ((c.Equals('2'))) this.AddValue(2, 4);
41 | else if ((c.Equals('3'))) this.AddValue(3, 4);
42 | else if ((c.Equals('4'))) this.AddValue(4, 4);
43 | else if ((c.Equals('5'))) this.AddValue(5, 4);
44 | else if ((c.Equals('6'))) this.AddValue(6, 4);
45 | else if ((c.Equals('7'))) this.AddValue(7, 4);
46 | else if ((c.Equals('8'))) this.AddValue(8, 4);
47 | else if ((c.Equals('9'))) this.AddValue(9, 4);
48 | else if ((c.Equals('A'))) this.AddValue(10, 4);
49 | else if ((c.Equals('B'))) this.AddValue(11, 4);
50 | else if ((c.Equals('C'))) this.AddValue(12, 4);
51 | else if ((c.Equals('D'))) this.AddValue(13, 4);
52 | else if ((c.Equals('E'))) this.AddValue(14, 4);
53 | else if ((c.Equals('F'))) this.AddValue(15, 4);
54 | }
55 | }
56 |
57 | public uint Read(int num_bits) {
58 | // Read and remove items from the front of the list of bits
59 | if (data.Count < num_bits) return 0;
60 | uint result = 0;
61 | for (int i = 0; i < num_bits; i++) {
62 | result = result << 1;
63 | result = result + data[0];
64 | data.RemoveAt(0);
65 | }
66 | return result;
67 | }
68 |
69 | public byte[] ToArray() {
70 | int num_bytes = (int)Math.Ceiling((double)data.Count/8.0);
71 | byte[] array = new byte[num_bytes];
72 | int ptr = 0;
73 | int shift = 7;
74 | for (int i = 0; i < data.Count; i++) {
75 | array[ptr] += (byte)(data[i] << shift);
76 | if (shift == 0) {
77 | shift = 7;
78 | ptr++;
79 | }
80 | else {
81 | shift--;
82 | }
83 | }
84 |
85 | return array;
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/PortCouple.cs:
--------------------------------------------------------------------------------
1 | namespace Unity.Ig.VideoStreaming.Server.Messages
2 | {
3 | using System;
4 | using System.Diagnostics.Contracts;
5 | using System.Globalization;
6 |
7 | ///
8 | /// Describe a couple of port used to transfer video and command.
9 | ///
10 | public class PortCouple
11 | {
12 | ///
13 | /// Gets or sets the first port number.
14 | ///
15 | /// The first port.
16 | public int First { get; set; }
17 | ///
18 | /// Gets or sets the second port number.
19 | ///
20 | /// If not present the value is 0
21 | /// The second port.
22 | public int Second { get; set; }
23 |
24 | ///
25 | /// Initializes a new instance of the class.
26 | ///
27 | public PortCouple()
28 | { }
29 | ///
30 | /// Initializes a new instance of the class.
31 | ///
32 | /// The first port.
33 | public PortCouple(int first)
34 | {
35 | First = first;
36 | Second = 0;
37 | }
38 | ///
39 | /// Initializes a new instance of the class.
40 | ///
41 | /// The first port.
42 | /// The second port.
43 | public PortCouple(int first, int second)
44 | {
45 | First = first;
46 | Second = second;
47 | }
48 |
49 | ///
50 | /// Gets a value indicating whether this instance has second port.
51 | ///
52 | ///
53 | /// true if this instance has second port; otherwise, false.
54 | ///
55 | public bool IsSecondPortPresent
56 | {
57 | get { return Second != 0; }
58 | }
59 |
60 | ///
61 | /// Parses the int values of port.
62 | ///
63 | /// A string value.
64 | /// The port couple
65 | public static PortCouple Parse(string stringValue)
66 | {
67 | if (stringValue == null)
68 | throw new ArgumentNullException("stringValue");
69 | Contract.Requires(!string.IsNullOrEmpty(stringValue));
70 |
71 | string[] values = stringValue.Split('-');
72 |
73 | int tempValue;
74 |
75 | int.TryParse(values[0], out tempValue);
76 | PortCouple result = new PortCouple(tempValue);
77 |
78 | tempValue = 0;
79 | if (values.Length > 1)
80 | int.TryParse(values[1], out tempValue);
81 |
82 | result.Second = tempValue;
83 |
84 | return result;
85 | }
86 |
87 | ///
88 | /// Returns a that represents this instance.
89 | ///
90 | ///
91 | /// A that represents this instance.
92 | ///
93 | public override string ToString()
94 | {
95 | if (IsSecondPortPresent)
96 | return First.ToString(CultureInfo.InvariantCulture) + "-" + Second.ToString(CultureInfo.InvariantCulture);
97 | else
98 | return First.ToString(CultureInfo.InvariantCulture);
99 | }
100 |
101 |
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/Origin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Globalization;
6 |
7 | namespace Unity.Ig.VideoStreaming.Server.Sdp
8 | {
9 | ///
10 | /// Object ot represent orgin in an Session Description Protocol
11 | ///
12 | public class Origin
13 | {
14 | public Origin()
15 | {
16 | }
17 |
18 | ///
19 | /// Parses the specified origin string.
20 | ///
21 | /// The string to convert to origin object.
22 | ///
23 | public static Origin Parse(string originString)
24 | {
25 | if (originString == null)
26 | throw new ArgumentNullException("originString");
27 |
28 | string[] parts = originString.Split(' ');
29 |
30 | if (parts.Length != 6)
31 | throw new FormatException("Number of element invalid in origin string.");
32 |
33 | Origin result = new Origin();
34 | result.Username = parts[0];
35 | result.SessionId = parts[1];
36 | result.SessionVersion = parts[2];
37 | result.NetType = parts[3];
38 | result.AddressType = parts[4];
39 | result.UnicastAddress = parts[5];
40 |
41 | return result;
42 | }
43 |
44 | ///
45 | /// Gets or sets the username.
46 | ///
47 | /// It is the user's login on the originating host, or it is "-"
48 | /// if the originating host does not support the concept of user IDs.
49 | /// This MUST NOT contain spaces
50 | /// The username.
51 | public string Username { get; set; }
52 |
53 | ///
54 | /// Gets or sets the session id.
55 | ///
56 | /// It is a numeric string such that the tuple of ,
57 | /// , , , and forms a
58 | /// globally unique identifier for the session. The method of
59 | /// allocation is up to the creating tool, but it has been
60 | /// suggested that a Network Time Protocol (NTP) format timestamp be
61 | /// used to ensure uniqueness
62 | /// The session id.
63 | public string SessionId { get; set; }
64 |
65 | ///
66 | /// Gets or sets the session version.
67 | ///
68 | /// The session version.
69 | public string SessionVersion { get; set; }
70 |
71 | ///
72 | /// Gets or sets the type of the net.
73 | ///
74 | /// The type of the net.
75 | public string NetType { get; set; }
76 |
77 | ///
78 | /// Gets or sets the type of the address.
79 | ///
80 | /// The type of the address.
81 | public string AddressType { get; set; }
82 |
83 | ///
84 | /// Gets or sets the unicast address (IP or FDQN).
85 | ///
86 | /// The unicast address.
87 | public string UnicastAddress { get; set; }
88 |
89 | public override string ToString()
90 | {
91 | return String.Join(" ",
92 | new string[]
93 | {
94 | Username,
95 | SessionId,
96 | SessionVersion.ToString(CultureInfo.InvariantCulture),
97 | NetType,
98 | AddressType,
99 | UnicastAddress,
100 | }
101 | );
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/RTSPTCPTransport.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Net;
4 | using System.Net.Sockets;
5 | using System.Diagnostics.Contracts;
6 | using System.Globalization;
7 |
8 | namespace Unity.Ig.VideoStreaming.Server
9 | {
10 | ///
11 | /// TCP Connection for Rtsp
12 | ///
13 | public class RtspTcpTransport : IRtspTransport, IDisposable
14 | {
15 | private IPEndPoint _currentEndPoint;
16 | private TcpClient _RtspServerClient;
17 |
18 | ///
19 | /// Initializes a new instance of the class.
20 | ///
21 | /// The underlying TCP connection.
22 | public RtspTcpTransport(TcpClient tcpConnection)
23 | {
24 | if (tcpConnection == null)
25 | throw new ArgumentNullException("tcpConnection");
26 | Contract.EndContractBlock();
27 |
28 | _currentEndPoint = (IPEndPoint)tcpConnection.Client.RemoteEndPoint;
29 | _RtspServerClient = tcpConnection;
30 | }
31 |
32 | ///
33 | /// Initializes a new instance of the class.
34 | ///
35 | /// A host.
36 | /// A port number.
37 | public RtspTcpTransport(string aHost, int aPortNumber)
38 | : this(new TcpClient(aHost, aPortNumber))
39 | {
40 | }
41 |
42 |
43 | #region IRtspTransport Membres
44 |
45 | ///
46 | /// Gets the stream of the transport.
47 | ///
48 | /// A stream
49 | public Stream GetStream()
50 | {
51 | return _RtspServerClient.GetStream();
52 | }
53 |
54 | ///
55 | /// Gets the remote address.
56 | ///
57 | /// The remote address.
58 | public string RemoteAddress
59 | {
60 | get
61 | {
62 | return string.Format(CultureInfo.InvariantCulture,"{0}:{1}", _currentEndPoint.Address, _currentEndPoint.Port);
63 | }
64 | }
65 |
66 | ///
67 | /// Closes this instance.
68 | ///
69 | public void Close()
70 | {
71 | Dispose(true);
72 | }
73 |
74 | ///
75 | /// Gets a value indicating whether this is connected.
76 | ///
77 | /// true if connected; otherwise, false.
78 | public bool Connected
79 | {
80 | get { return _RtspServerClient.Client != null && _RtspServerClient.Connected; }
81 | }
82 |
83 | ///
84 | /// Reconnect this instance.
85 | /// Must do nothing if already connected.
86 | ///
87 | /// Error during socket
88 | public void Reconnect()
89 | {
90 | if (Connected)
91 | return;
92 | _RtspServerClient = new TcpClient();
93 | _RtspServerClient.Connect(_currentEndPoint);
94 | }
95 |
96 | #endregion
97 |
98 | public void Dispose()
99 | {
100 | Dispose(true);
101 | GC.SuppressFinalize(this);
102 | }
103 |
104 | protected virtual void Dispose(bool disposing)
105 | {
106 | if (disposing)
107 | {
108 | _RtspServerClient.Close();
109 | /* // free managed resources
110 | if (managedResource != null)
111 | {
112 | managedResource.Dispose();
113 | managedResource = null;
114 | }*/
115 | }
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/H264Parameter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace Unity.Ig.VideoStreaming.Server.Sdp
8 | {
9 | public class H264Parameters : IDictionary
10 | {
11 | private readonly Dictionary parameters = new Dictionary();
12 |
13 | public List SpropParameterSets
14 | {
15 | get
16 | {
17 | List result = new List();
18 |
19 | if (ContainsKey("sprop-parameter-sets")&& this["sprop-parameter-sets"] != null)
20 | {
21 | result.AddRange(this["sprop-parameter-sets"].Split(',').Select(x => Convert.FromBase64String(x)));
22 | }
23 |
24 | return result;
25 | }
26 | }
27 |
28 | public static H264Parameters Parse(String parameterString)
29 | {
30 | var result = new H264Parameters();
31 | foreach (var pair in parameterString.Split(';').Select(x => x.Trim().Split(new char[] { '=' }, 2)))
32 | {
33 | if(!string.IsNullOrWhiteSpace(pair[0]))
34 | result[pair[0]] = pair.Length > 1 ? pair[1] : null;
35 | }
36 | return result;
37 | }
38 |
39 | public override string ToString()
40 | {
41 | return parameters.Select(p => p.Key + (p.Value != null ? "=" + p.Value : string.Empty)).Aggregate((x, y) => x + ";" + y);
42 | }
43 |
44 | public String this[String index]
45 | {
46 | get { return parameters[index]; }
47 | set { parameters[index] = value; }
48 | }
49 |
50 | public int Count
51 | {
52 | get
53 | {
54 | return parameters.Count;
55 | }
56 | }
57 |
58 | public bool IsReadOnly
59 | {
60 | get
61 | {
62 | return ((IDictionary)parameters).IsReadOnly;
63 | }
64 | }
65 |
66 | public ICollection Keys
67 | {
68 | get
69 | {
70 | return ((IDictionary)parameters).Keys;
71 | }
72 | }
73 |
74 | public ICollection Values
75 | {
76 | get
77 | {
78 | return ((IDictionary)parameters).Values;
79 | }
80 | }
81 |
82 | public void Add(KeyValuePair item)
83 | {
84 | ((IDictionary)parameters).Add(item);
85 | }
86 |
87 | public void Add(string key, string value)
88 | {
89 | parameters.Add(key, value);
90 | }
91 |
92 | public void Clear()
93 | {
94 | parameters.Clear();
95 | }
96 |
97 | public bool Contains(KeyValuePair item)
98 | {
99 | return ((IDictionary)parameters).Contains(item);
100 | }
101 |
102 | public bool ContainsKey(string key)
103 | {
104 | return parameters.ContainsKey(key);
105 | }
106 |
107 | public void CopyTo(KeyValuePair[] array, int arrayIndex)
108 | {
109 | ((IDictionary)parameters).CopyTo(array, arrayIndex);
110 | }
111 |
112 | public IEnumerator> GetEnumerator()
113 | {
114 | return ((IDictionary)parameters).GetEnumerator();
115 | }
116 |
117 | public bool Remove(KeyValuePair item)
118 | {
119 | return ((IDictionary)parameters).Remove(item);
120 | }
121 |
122 | public bool Remove(string key)
123 | {
124 | return parameters.Remove(key);
125 | }
126 |
127 | public bool TryGetValue(string key, out string value)
128 | {
129 | return parameters.TryGetValue(key, out value);
130 | }
131 |
132 | IEnumerator IEnumerable.GetEnumerator()
133 | {
134 | return ((IDictionary)parameters).GetEnumerator();
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/H265Parameter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | // Parse 'fmtp' attribute in SDP
8 | // Extract H265 fields
9 | // By Roger Hardiman, RJH Technical Consultancy Ltd
10 |
11 | namespace Unity.Ig.VideoStreaming.Server.Sdp
12 | {
13 | public class H265Parameters : IDictionary
14 | {
15 | private readonly Dictionary parameters = new Dictionary();
16 |
17 | public List SpropParameterSets
18 | {
19 | get
20 | {
21 | List result = new List();
22 |
23 | if (ContainsKey("sprop-vps")&& this["sprop-vps"] != null)
24 | {
25 | result.AddRange(this["sprop-vps"].Split(',').Select(x => Convert.FromBase64String(x)));
26 | }
27 |
28 | if (ContainsKey("sprop-sps") && this["sprop-sps"] != null)
29 | {
30 | result.AddRange(this["sprop-sps"].Split(',').Select(x => Convert.FromBase64String(x)));
31 | }
32 |
33 | if (ContainsKey("sprop-pps") && this["sprop-pps"] != null)
34 | {
35 | result.AddRange(this["sprop-pps"].Split(',').Select(x => Convert.FromBase64String(x)));
36 | }
37 | return result;
38 | }
39 | }
40 |
41 | public static H265Parameters Parse(String parameterString)
42 | {
43 | var result = new H265Parameters();
44 | foreach (var pair in parameterString.Split(';').Select(x => x.Trim().Split(new char[] { '=' }, 2)))
45 | {
46 | if(!string.IsNullOrWhiteSpace(pair[0]))
47 | result[pair[0]] = pair.Length > 1 ? pair[1] : null;
48 | }
49 | return result;
50 | }
51 |
52 | public override string ToString()
53 | {
54 | return parameters.Select(p => p.Key + (p.Value != null ? "=" + p.Value : string.Empty)).Aggregate((x, y) => x + ";" + y);
55 | }
56 |
57 | public String this[String index]
58 | {
59 | get { return parameters[index]; }
60 | set { parameters[index] = value; }
61 | }
62 |
63 | public int Count
64 | {
65 | get
66 | {
67 | return parameters.Count;
68 | }
69 | }
70 |
71 | public bool IsReadOnly
72 | {
73 | get
74 | {
75 | return ((IDictionary)parameters).IsReadOnly;
76 | }
77 | }
78 |
79 | public ICollection Keys
80 | {
81 | get
82 | {
83 | return ((IDictionary)parameters).Keys;
84 | }
85 | }
86 |
87 | public ICollection Values
88 | {
89 | get
90 | {
91 | return ((IDictionary)parameters).Values;
92 | }
93 | }
94 |
95 | public void Add(KeyValuePair item)
96 | {
97 | ((IDictionary)parameters).Add(item);
98 | }
99 |
100 | public void Add(string key, string value)
101 | {
102 | parameters.Add(key, value);
103 | }
104 |
105 | public void Clear()
106 | {
107 | parameters.Clear();
108 | }
109 |
110 | public bool Contains(KeyValuePair item)
111 | {
112 | return ((IDictionary)parameters).Contains(item);
113 | }
114 |
115 | public bool ContainsKey(string key)
116 | {
117 | return parameters.ContainsKey(key);
118 | }
119 |
120 | public void CopyTo(KeyValuePair[] array, int arrayIndex)
121 | {
122 | ((IDictionary)parameters).CopyTo(array, arrayIndex);
123 | }
124 |
125 | public IEnumerator> GetEnumerator()
126 | {
127 | return ((IDictionary)parameters).GetEnumerator();
128 | }
129 |
130 | public bool Remove(KeyValuePair item)
131 | {
132 | return ((IDictionary)parameters).Remove(item);
133 | }
134 |
135 | public bool Remove(string key)
136 | {
137 | return parameters.Remove(key);
138 | }
139 |
140 | public bool TryGetValue(string key, out string value)
141 | {
142 | return parameters.TryGetValue(key, out value);
143 | }
144 |
145 | IEnumerator IEnumerable.GetEnumerator()
146 | {
147 | return ((IDictionary)parameters).GetEnumerator();
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/AACPayload.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace Unity.Ig.VideoStreaming.Server
6 | {
7 | // This class handles the AAC-hbd (High Bitrate) Payload
8 | // It has methods to process the RTP Payload
9 |
10 | // (c) 2018 Roger Hardiman, RJH Technical Consultancy Ltd
11 |
12 |
13 | /*
14 | RFC 3640
15 | 3.3.6. High Bit-rate AAC
16 |
17 | This mode is signaled by mode=AAC-hbr.This mode supports the
18 | transportation of variable size AAC frames.In one RTP packet,
19 | either one or more complete AAC frames are carried, or a single
20 | fragment of an AAC frame is carried.In this mode, the AAC frames
21 | are allowed to be interleaved and hence receivers MUST support de-
22 | interleaving.The maximum size of an AAC frame in this mode is 8191
23 | octets.
24 |
25 | In this mode, the RTP payload consists of the AU Header Section,
26 | followed by either one AAC frame, several concatenated AAC frames or
27 | one fragmented AAC frame.The Auxiliary Section MUST be empty. For
28 | each AAC frame contained in the payload, there MUST be an AU-header
29 | in the AU Header Section to provide:
30 |
31 | a) the size of each AAC frame in the payload and
32 |
33 | b) index information for computing the sequence(and hence timing) of
34 | each AAC frame.
35 |
36 | To code the maximum size of an AAC frame requires 13 bits.
37 | Therefore, in this configuration 13 bits are allocated to the AU-
38 | size, and 3 bits to the AU-Index(-delta) field.Thus, each AU-header
39 | has a size of 2 octets.Each AU-Index field MUST be coded with the
40 | value 0. In the AU Header Section, the concatenated AU-headers MUST
41 | be preceded by the 16-bit AU-headers-length field, as specified in
42 | section 3.2.1.
43 |
44 | In addition to the required MIME format parameters, the following
45 | parameters MUST be present: sizeLength, indexLength, and
46 | indexDeltaLength.AAC frames always have a fixed duration per Access
47 | Unit; when interleaving in this mode, this specific duration MUST be
48 | signaled by the MIME format parameter constantDuration.In addition,
49 | the parameter maxDisplacement MUST be present when interleaving.
50 |
51 | For example:
52 |
53 | m= audio 49230 RTP/AVP 96
54 | a= rtpmap:96 mpeg4-generic/48000/6
55 | a= fmtp:96 streamtype= 5; profile-level-id= 16; mode= AAC-hbr;config= 11B0; sizeLength= 13; indexLength= 3;indexDeltaLength= 3; constantDuration= 1024
56 |
57 | The hexadecimal value of the "config" parameter is the AudioSpecificConfig(), as defined in ISO/IEC 14496-3.
58 | AudioSpecificConfig() specifies a 5.1 channel AAC stream with a sampling rate of 48 kHz.For the description of MIME parameters, see
59 | section 4.1.
60 |
61 | */
62 |
63 |
64 | public class AACPayload
65 | {
66 | public uint ObjectType = 0;
67 | public uint FrequencyIndex = 0;
68 | public uint ChannelConfiguration = 0;
69 |
70 | // Constructor
71 | public AACPayload(String config_string)
72 | {
73 | /***
74 | 5 bits: object type
75 | if (object type == 31)
76 | 6 bits + 32: object type
77 | 4 bits: frequency index
78 | if (frequency index == 15)
79 | 24 bits: frequency
80 | 4 bits: channel configuration
81 | var bits: AOT Specific Config
82 | ***/
83 |
84 | // config is a string in hex eg 1490 or 0x1210
85 | // Read each ASCII character and add to a bit array
86 | BitStream bs = new BitStream();
87 | bs.AddHexString(config_string);
88 |
89 | // Read 5 bits
90 | ObjectType = bs.Read(5);
91 |
92 | // Read 4 bits
93 | FrequencyIndex = bs.Read(4);
94 |
95 | // Read 4 bits
96 | ChannelConfiguration = bs.Read(4);
97 | }
98 |
99 | public List Process_AAC_RTP_Packet(byte[] rtp_payload, int rtp_marker) {
100 |
101 | // RTP Payload for MPEG4-GENERIC can consist of multple blocks.
102 | // Each block has 3 parts
103 | // Part 1 - Acesss Unit Header Length + Header
104 | // Part 2 - Access Unit Auxiliary Data Length + Data (not used in AAC High Bitrate)
105 | // Part 3 - Access Unit Audio Data
106 |
107 | // The rest of the RTP packet is the AMR data
108 | List audio_data = new List();
109 |
110 | int ptr = 0;
111 |
112 | while (true) {
113 | if (ptr + 4 > rtp_payload.Length) break; // 2 bytes for AU Header Length, 2 bytes of AU Header payload
114 |
115 | // Get Size of the AU Header
116 | int au_headers_length_bits = (((rtp_payload[ptr] << 8) + (rtp_payload[ptr + 1] << 0))); // 16 bits
117 | int au_headers_length = (int)Math.Ceiling((double)au_headers_length_bits / 8.0);
118 | ptr += 2;
119 |
120 | // Examine the AU Header. Get the size of the AAC data
121 | int aac_frame_size = (((rtp_payload[ptr] << 8) + (rtp_payload[ptr+1] << 0)) >> 3); // 13 bits
122 | int aac_index_delta = rtp_payload[ptr+1] & 0x03; // 3 bits
123 | ptr += au_headers_length;
124 |
125 | // extract the AAC block
126 | if (ptr + aac_frame_size > rtp_payload.Length) break; // not enough data to copy
127 | byte[] aac_data = new byte[aac_frame_size];
128 | System.Array.Copy(rtp_payload, ptr, aac_data, 0, aac_frame_size);
129 | audio_data.Add(aac_data);
130 | ptr += aac_frame_size;
131 | }
132 |
133 | return audio_data;
134 | }
135 |
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPRequest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Diagnostics;
6 |
7 | namespace Unity.Ig.VideoStreaming.Server.Messages
8 | {
9 | ///
10 | /// An Rtsp Request
11 | ///
12 | public class RtspRequest : RtspMessage
13 | {
14 |
15 | ///
16 | /// Request type.
17 | ///
18 | public enum RequestType
19 | {
20 | UNKNOWN,
21 | DESCRIBE,
22 | ANNOUNCE,
23 | GET_PARAMETER,
24 | OPTIONS,
25 | PAUSE,
26 | PLAY,
27 | RECORD,
28 | REDIRECT,
29 | SETUP,
30 | SET_PARAMETER,
31 | TEARDOWN,
32 | }
33 |
34 | ///
35 | /// Parses the request command.
36 | ///
37 | /// A string request command.
38 | /// The typed request.
39 | internal static RequestType ParseRequest(string aStringRequest)
40 | {
41 | RequestType returnValue;
42 | if (!Enum.TryParse(aStringRequest, true, out returnValue))
43 | returnValue = RequestType.UNKNOWN;
44 | return returnValue;
45 | }
46 |
47 | ///
48 | /// Gets the Rtsp request.
49 | ///
50 | /// A request parts.
51 | /// the parsed request
52 | internal static RtspMessage GetRtspRequest(string[] aRequestParts)
53 | {
54 | //
55 | Debug.Assert(aRequestParts != (string[])null, "aRequestParts");
56 | Debug.Assert(aRequestParts.Length != 0, "aRequestParts.Length == 0");
57 | //
58 | // we already know this is a Request
59 | RtspRequest returnValue;
60 | switch (ParseRequest(aRequestParts[0]))
61 | {
62 | case RequestType.OPTIONS:
63 | returnValue = new RtspRequestOptions();
64 | break;
65 | case RequestType.DESCRIBE:
66 | returnValue = new RtspRequestDescribe();
67 | break;
68 | case RequestType.SETUP:
69 | returnValue = new RtspRequestSetup();
70 | break;
71 | case RequestType.PLAY:
72 | returnValue = new RtspRequestPlay();
73 | break;
74 | case RequestType.PAUSE:
75 | returnValue = new RtspRequestPause();
76 | break;
77 | case RequestType.TEARDOWN:
78 | returnValue = new RtspRequestTeardown();
79 | break;
80 | case RequestType.GET_PARAMETER:
81 | returnValue = new RtspRequestGetParameter();
82 | break;
83 | case RequestType.ANNOUNCE:
84 | returnValue = new RtspRequestAnnounce();
85 | break;
86 | case RequestType.RECORD:
87 | returnValue = new RtspRequestRecord();
88 | break;
89 | /*
90 | case RequestType.REDIRECT:
91 | break;
92 |
93 | case RequestType.SET_PARAMETER:
94 | break;
95 | */
96 | case RequestType.UNKNOWN:
97 | default:
98 | returnValue = new RtspRequest();
99 | break;
100 | }
101 |
102 |
103 |
104 | return returnValue;
105 | }
106 |
107 | ///
108 | /// Initializes a new instance of the class.
109 | ///
110 | public RtspRequest()
111 | {
112 | Command = "OPTIONS * RTSP/1.0";
113 | }
114 |
115 | ///
116 | /// Gets the request.
117 | ///
118 | /// The request in string format.
119 | public string Request
120 | {
121 | get
122 | {
123 | return commandArray[0];
124 | }
125 | }
126 |
127 | ///
128 | /// Gets the request.
129 | /// The return value is typed with if the value is not
130 | /// reconise the value is sent. The string value can be get by
131 | ///
132 | /// The request.
133 | public RequestType RequestTyped
134 | {
135 | get
136 | {
137 | return ParseRequest(commandArray[0]);
138 | }
139 | set
140 | {
141 | if (Enum.IsDefined(typeof(RequestType), value))
142 | commandArray[0] = value.ToString();
143 | else
144 | commandArray[0] = RequestType.UNKNOWN.ToString();
145 | }
146 | }
147 |
148 | private Uri _RtspUri;
149 | ///
150 | /// Gets or sets the Rtsp asked URI.
151 | ///
152 | /// The Rtsp asked URI.
153 | /// The request with uri * is return with null URI
154 | public Uri RtspUri
155 | {
156 | get
157 | {
158 | if (commandArray.Length < 2 || commandArray[1]=="*")
159 | return null;
160 | if (_RtspUri == null)
161 | Uri.TryCreate(commandArray[1], UriKind.Absolute, out _RtspUri);
162 | return _RtspUri;
163 | }
164 | set
165 | {
166 | _RtspUri = value;
167 | if (commandArray.Length < 2)
168 | {
169 | Array.Resize(ref commandArray, 3);
170 | }
171 | commandArray[1] = (value != null ? value.ToString().TrimEnd('/') : "*");
172 | }
173 | }
174 |
175 | ///
176 | /// Gets the assiociate OK response with the request.
177 | ///
178 | /// an Rtsp response correcponding to request.
179 | public virtual RtspResponse CreateResponse()
180 | {
181 | RtspResponse returnValue = new RtspResponse();
182 | returnValue.ReturnCode = 200;
183 | returnValue.CSeq = this.CSeq;
184 | if (this.Headers.ContainsKey(RtspHeaderNames.Session))
185 | {
186 | returnValue.Headers[RtspHeaderNames.Session] = this.Headers[RtspHeaderNames.Session];
187 | }
188 |
189 | return returnValue;
190 | }
191 |
192 | public Object ContextData { get; set; }
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/H264Encoder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 | using UnityEngine.Profiling;
4 | using Unity.Collections;
5 | using Unity.Collections.LowLevel.Unsafe;
6 | using Unity.Jobs;
7 |
8 | namespace Unity.Ig.VideoStreaming.Server
9 | {
10 | public struct H264Encoder : IDisposable
11 | {
12 | public void Dispose()
13 | {
14 | m_CreateJob.Complete();
15 | m_EncodeJob.Complete();
16 | if (m_EncoderPtr.IsCreated)
17 | {
18 | H264EncoderPlugin.DestroyEncoder(m_EncoderPtr[0]);
19 | m_EncoderPtr.Dispose();
20 | }
21 | }
22 |
23 | // Start is called before the first frame update
24 | public JobHandle Start(uint width, uint height, uint frameRate, ref NativeList spsNalu, ref NativeList ppsNalu)
25 | {
26 | if (!m_CreateJob.Equals(default(JobHandle)))
27 | {
28 | Debug.LogWarning("Trying to start the H264Encoder more than once.");
29 | return default;
30 | }
31 |
32 | m_EncoderPtr = new NativeArray(1, Allocator.Persistent);
33 |
34 | var createJob = new CreateEncoderJob
35 | {
36 | width = width,
37 | height = height,
38 | frameRateNumerator = frameRate,
39 | encoderPtr = m_EncoderPtr,
40 | spsNalu = spsNalu,
41 | ppsNalu = ppsNalu
42 | };
43 | m_CreateJob = createJob.Schedule();
44 | return m_CreateJob;
45 | }
46 |
47 | public JobHandle Encode(in JobHandle dependsOn, in NativeArray imageData, ulong timeStampNs, ref NativeList spsNalu, ref NativeList ppsNalu, ref NativeList imageNalu)
48 | {
49 | if (!m_CreateJob.IsCompleted)
50 | return default;
51 | m_CreateJob.Complete();
52 |
53 | if (!m_EncoderPtr.IsCreated || m_EncoderPtr[0] == IntPtr.Zero || !m_EncodeJob.IsCompleted)
54 | return default;
55 | m_EncodeJob.Complete();
56 |
57 | var encodeJob = new EncodeJob
58 | {
59 | encoderPtr = m_EncoderPtr[0],
60 | pixelData = imageData,
61 | timeStampNs = timeStampNs,
62 | spsNalu = spsNalu,
63 | ppsNalu = ppsNalu,
64 | imageNalu = imageNalu
65 | };
66 | m_EncodeJob = encodeJob.Schedule(dependsOn);
67 | return m_EncodeJob;
68 | }
69 |
70 | static bool ConsumeBuffer(IntPtr encoderPtr, ref NativeList spsNalu, ref NativeList ppsNalu, ref NativeList imageNalu)
71 | {
72 | Profiler.BeginSample("BeginConsumeEncodedBuffer");
73 | bool success = H264EncoderPlugin.BeginConsumeEncodedBuffer(encoderPtr, out uint bufferSize);
74 | Profiler.EndSample();
75 | if (!success)
76 | {
77 | Debug.LogWarning("No encoded frame ready.");
78 | return false;
79 | }
80 |
81 | imageNalu.ResizeUninitialized((int)bufferSize);
82 |
83 | Profiler.BeginSample("EndConsumeEncodedBuffer");
84 | ulong bufferTimeStampNs;
85 | bool isKeyFrame;
86 | unsafe
87 | {
88 | success = H264EncoderPlugin.EndConsumeEncodedBuffer(encoderPtr, (byte*)imageNalu.GetUnsafePtr(), out bufferTimeStampNs, out isKeyFrame);
89 | }
90 |
91 | Profiler.EndSample();
92 | if (!success)
93 | {
94 | Debug.LogErrorFormat("Could not get {0} bytes encoded buffer", bufferSize);
95 | return false;
96 | }
97 |
98 | if (isKeyFrame)
99 | unsafe
100 | {
101 | //Debug.LogFormat("Produced keyframe at t={0}, size={1}", bufferTimeStampNs, bufferSize);
102 | var sz = H264EncoderPlugin.GetSpsNAL(encoderPtr, (byte*)0);
103 | spsNalu.ResizeUninitialized((int)sz);
104 | H264EncoderPlugin.GetSpsNAL(encoderPtr, (byte*)spsNalu.GetUnsafePtr());
105 |
106 | sz = H264EncoderPlugin.GetPpsNAL(encoderPtr, (byte*)0);
107 | ppsNalu.ResizeUninitialized((int)sz);
108 | H264EncoderPlugin.GetPpsNAL(encoderPtr, (byte*)ppsNalu.GetUnsafePtr());
109 | }
110 |
111 | return true;
112 | }
113 |
114 | private struct CreateEncoderJob : IJob
115 | {
116 | [ReadOnly]
117 | public uint width;
118 |
119 | [ReadOnly]
120 | public uint height;
121 |
122 | [ReadOnly]
123 | public uint frameRateNumerator;
124 |
125 | [WriteOnly]
126 | public NativeArray encoderPtr;
127 |
128 | public NativeList spsNalu;
129 | public NativeList ppsNalu;
130 |
131 | public void Execute()
132 | {
133 | var encoder = H264EncoderPlugin.CreateEncoder(width, height, frameRateNumerator, 1, 1000000);
134 | encoderPtr[0] = encoder;
135 | unsafe
136 | {
137 | uint sz = H264EncoderPlugin.GetSpsNAL(encoder, (byte*)0);
138 | spsNalu.ResizeUninitialized((int)sz);
139 | H264EncoderPlugin.GetSpsNAL(encoder, (byte*)spsNalu.GetUnsafePtr());
140 |
141 | sz = H264EncoderPlugin.GetPpsNAL(encoder, (byte*)0);
142 | ppsNalu.ResizeUninitialized((int)sz);
143 | H264EncoderPlugin.GetPpsNAL(encoder, (byte*)ppsNalu.GetUnsafePtr());
144 | }
145 | }
146 | }
147 |
148 | private struct EncodeJob : IJob
149 | {
150 | [ReadOnly]
151 | [NativeDisableUnsafePtrRestriction]
152 | public IntPtr encoderPtr;
153 |
154 | [ReadOnly]
155 | public NativeArray pixelData;
156 |
157 | [ReadOnly]
158 | public ulong timeStampNs;
159 |
160 | public NativeList spsNalu;
161 | public NativeList ppsNalu;
162 | public NativeList imageNalu;
163 |
164 | public void Execute()
165 | {
166 | unsafe
167 | {
168 | Profiler.BeginSample("EncodeFrame");
169 |
170 | //Debug.Log("Encoding source image with " + pixelData.Length + " bytes");
171 | bool success = H264EncoderPlugin.EncodeFrame(encoderPtr, (byte*)pixelData.GetUnsafeReadOnlyPtr(), timeStampNs);
172 | Profiler.EndSample();
173 |
174 | if (!success)
175 | {
176 | Debug.LogErrorFormat("Error encoding frame at t = {0}", timeStampNs);
177 | return;
178 | }
179 | }
180 |
181 | Profiler.BeginSample("ConsumeBuffer");
182 | ConsumeBuffer(encoderPtr, ref spsNalu, ref ppsNalu, ref imageNalu);
183 | Profiler.EndSample();
184 | }
185 | }
186 |
187 | // Held in native array so we can have it initialized from a job.
188 | NativeArray m_EncoderPtr;
189 | private JobHandle m_CreateJob;
190 | private JobHandle m_EncodeJob;
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Authentication.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Cryptography;
3 | using System.Text;
4 | using UnityEngine;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server
7 | {
8 |
9 | // WWW-Authentication and Authorization Headers
10 | public class Authentication
11 | {
12 | private static ILogger _logger = new Logger(Debug.unityLogger.logHandler);
13 |
14 | static Authentication()
15 | {
16 | _logger.logEnabled = false;
17 | }
18 |
19 | public enum Type {Basic, Digest};
20 |
21 | private String username = null;
22 | private String password = null;
23 | private String realm = null;
24 | private String nonce = null;
25 | private Type authentication_type = Type.Digest;
26 | private readonly MD5 md5 = System.Security.Cryptography.MD5.Create();
27 |
28 |
29 | private const char quote = '\"';
30 |
31 | // Constructor
32 | public Authentication(String username, String password, String realm, Type authentication_type)
33 | {
34 | this.username = username;
35 | this.password = password;
36 | this.realm = realm;
37 | this.authentication_type = authentication_type;
38 |
39 | this.nonce = new System.Random().Next(100000000,999999999).ToString(); // random 9 digit number
40 | }
41 |
42 | public String GetHeader() {
43 | if (authentication_type == Type.Basic) {
44 | return "Basic realm=" + quote + realm + quote;
45 | }
46 | if (authentication_type == Type.Digest) {
47 | return "Digest realm=" + quote + realm + quote + ", nonce=" + quote + nonce + quote;
48 | }
49 | return null;
50 | }
51 |
52 |
53 | public bool IsValid(Messages.RtspMessage received_message) {
54 |
55 | string authorization = received_message.Headers["Authorization"];
56 |
57 |
58 | // Check Username and Password
59 | if (authentication_type == Type.Basic && authorization.StartsWith("Basic ")) {
60 | string base64_str = authorization.Substring(6); // remove 'Basic '
61 | byte[] data = Convert.FromBase64String(base64_str);
62 | string decoded = Encoding.UTF8.GetString(data);
63 | int split_position = decoded.IndexOf(':');
64 | string decoded_username = decoded.Substring(0, split_position);
65 | string decoded_password = decoded.Substring(split_position + 1);
66 |
67 | if ((decoded_username == username) && (decoded_password == password)) {
68 | _logger.Log("Basic Authorization passed");
69 | return true;
70 | } else {
71 | _logger.Log("Basic Authorization failed");
72 | return false;
73 | }
74 | }
75 |
76 | // Check Username, URI, Nonce and the MD5 hashed Response
77 | if (authentication_type == Type.Digest && authorization.StartsWith("Digest ")) {
78 | string value_str = authorization.Substring(7); // remove 'Digest '
79 | string[] values = value_str.Split(',');
80 | string auth_header_username = null;
81 | string auth_header_realm = null;
82 | string auth_header_nonce = null;
83 | string auth_header_uri = null;
84 | string auth_header_response = null;
85 | string message_method = null;
86 | string message_uri = null;
87 | try {
88 | message_method = received_message.Command.Split(' ')[0];
89 | message_uri = received_message.Command.Split(' ')[1];
90 | } catch {}
91 |
92 | foreach (string value in values) {
93 | string[] tuple = value.Trim().Split(new char[] {'='},2); // split on first '='
94 | if (tuple.Length == 2 && tuple[0].Equals("username")) {
95 | auth_header_username = tuple[1].Trim(new char[] {' ','\"'}); // trim space and quotes
96 | }
97 | else if (tuple.Length == 2 && tuple[0].Equals("realm")) {
98 | auth_header_realm = tuple[1].Trim(new char[] {' ','\"'}); // trim space and quotes
99 | }
100 | else if (tuple.Length == 2 && tuple[0].Equals("nonce")) {
101 | auth_header_nonce = tuple[1].Trim(new char[] {' ','\"'}); // trim space and quotes
102 | }
103 | else if (tuple.Length == 2 && tuple[0].Equals("uri")) {
104 | auth_header_uri = tuple[1].Trim(new char[] {' ','\"'}); // trim space and quotes
105 | }
106 | else if (tuple.Length == 2 && tuple[0].Equals("response")) {
107 | auth_header_response = tuple[1].Trim(new char[] {' ','\"'}); // trim space and quotes
108 | }
109 | }
110 |
111 | // Create the MD5 Hash using all parameters passed in the Auth Header with the
112 | // addition of the 'Password'
113 | String hashA1 = CalculateMD5Hash(md5, auth_header_username+":"+auth_header_realm+":"+this.password);
114 | String hashA2 = CalculateMD5Hash(md5, message_method + ":" + auth_header_uri);
115 | String expected_response = CalculateMD5Hash(md5, hashA1 + ":" + auth_header_nonce + ":" + hashA2);
116 |
117 | // Check if everything matches
118 | // ToDo - extract paths from the URIs (ignoring SETUP's trackID)
119 | if ((auth_header_username == this.username)
120 | && (auth_header_realm == this.realm)
121 | && (auth_header_nonce == this.nonce)
122 | && (auth_header_response == expected_response)
123 | ){
124 | _logger.Log("Digest Authorization passed");
125 | return true;
126 | } else {
127 | _logger.Log("Digest Authorization failed");
128 | return false;
129 | }
130 | }
131 | return false;
132 | }
133 |
134 |
135 |
136 | // Generate Basic or Digest Authorization
137 | public string GenerateAuthorization(string username, string password,
138 | string auth_type, string realm, string nonce, string url, string command) {
139 |
140 | if (username == null || username.Length == 0) return null;
141 | if (password == null || password.Length == 0) return null;
142 | if (realm == null || realm.Length == 0) return null;
143 | if (auth_type.Equals("Digest") && (nonce == null || nonce.Length == 0)) return null;
144 |
145 | if (auth_type.Equals("Basic")) {
146 | byte[] credentials = System.Text.Encoding.UTF8.GetBytes(username+":"+password);
147 | String credentials_base64 = Convert.ToBase64String(credentials);
148 | String basic_authorization = "Basic " + credentials_base64;
149 | return basic_authorization;
150 | }
151 | else if (auth_type.Equals("Digest")) {
152 |
153 | MD5 md5 = System.Security.Cryptography.MD5.Create();
154 | String hashA1 = CalculateMD5Hash(md5, username+":"+realm+":"+password);
155 | String hashA2 = CalculateMD5Hash(md5, command + ":" + url);
156 | String response = CalculateMD5Hash(md5, hashA1 + ":" + nonce + ":" + hashA2);
157 |
158 | const String quote = "\"";
159 | String digest_authorization = "Digest username=" + quote + username + quote +", "
160 | + "realm=" + quote + realm + quote + ", "
161 | + "nonce=" + quote + nonce + quote + ", "
162 | + "uri=" + quote + url + quote + ", "
163 | + "response=" + quote + response + quote;
164 |
165 | return digest_authorization;
166 | }
167 | else {
168 | return null;
169 | }
170 |
171 | }
172 |
173 |
174 |
175 | // MD5 (lower case)
176 | private string CalculateMD5Hash(MD5 md5_session, string input)
177 | {
178 | byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
179 | byte[] hash = md5_session.ComputeHash(inputBytes);
180 |
181 | StringBuilder output = new StringBuilder();
182 | for (int i = 0; i < hash.Length; i++) {
183 | output.Append(hash[i].ToString("x2"));
184 | }
185 |
186 | return output.ToString();
187 | }
188 | }
189 | }
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Native~/H264Encoder/H264Encoder.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | 15.0
23 | {45BDF642-D8AA-400A-A088-A66D09B1E247}
24 | Win32Proj
25 | H264Encoder
26 | 10.0.17763.0
27 |
28 |
29 |
30 | DynamicLibrary
31 | true
32 | v141
33 | Unicode
34 |
35 |
36 | DynamicLibrary
37 | false
38 | v141
39 | true
40 | Unicode
41 |
42 |
43 | DynamicLibrary
44 | true
45 | v141
46 | Unicode
47 |
48 |
49 | DynamicLibrary
50 | false
51 | v141
52 | true
53 | Unicode
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | true
75 |
76 |
77 | true
78 |
79 |
80 | false
81 |
82 |
83 | false
84 |
85 |
86 |
87 | Use
88 | Level3
89 | Disabled
90 | true
91 | WIN32;_DEBUG;H264ENCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
92 | true
93 |
94 |
95 | Windows
96 | true
97 |
98 |
99 |
100 |
101 | Use
102 | Level3
103 | Disabled
104 | true
105 | _DEBUG;H264ENCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
106 | true
107 |
108 |
109 | Windows
110 | true
111 |
112 |
113 |
114 |
115 | Use
116 | Level3
117 | MaxSpeed
118 | true
119 | true
120 | true
121 | WIN32;NDEBUG;H264ENCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
122 | true
123 |
124 |
125 | Windows
126 | true
127 | true
128 | true
129 |
130 |
131 |
132 |
133 | Use
134 | Level3
135 | MaxSpeed
136 | true
137 | true
138 | true
139 | NDEBUG;H264ENCODER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
140 | true
141 |
142 |
143 | Windows
144 | true
145 | true
146 | true
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 | Create
158 | Create
159 | Create
160 | Create
161 |
162 |
163 |
164 |
165 |
166 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Sdp/SdpFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.IO;
6 | using System.Globalization;
7 |
8 | namespace Unity.Ig.VideoStreaming.Server.Sdp
9 | {
10 | public class SdpFile
11 | {
12 | private static KeyValuePair GetKeyValue(TextReader sdpStream)
13 | {
14 | string line = sdpStream.ReadLine();
15 |
16 | // end of file ?
17 | if(string.IsNullOrEmpty(line))
18 | return new KeyValuePair(null, null);
19 |
20 |
21 | string[] parts = line.Split(new char[] { '=' }, 2);
22 | if (parts.Length != 2)
23 | throw new InvalidDataException();
24 | if (parts[0].Length != 1)
25 | throw new InvalidDataException();
26 |
27 | KeyValuePair value = new KeyValuePair(parts[0], parts[1]);
28 | return value;
29 | }
30 |
31 | ///
32 | /// Reads the specified SDP stream.
33 | /// As define in RFC 4566
34 | ///
35 | /// The SDP stream.
36 | ///
37 | public static SdpFile Read(TextReader sdpStream)
38 | {
39 | SdpFile returnValue = new SdpFile();
40 | KeyValuePair value = GetKeyValue(sdpStream);
41 |
42 | // Version mandatory
43 | if (value.Key == "v")
44 | {
45 | returnValue.Version = int.Parse(value.Value, CultureInfo.InvariantCulture);
46 | value = GetKeyValue(sdpStream);
47 | }
48 | else {
49 | throw new InvalidDataException();
50 | }
51 |
52 | // Origin mandatory
53 | if (value.Key == "o")
54 | {
55 | returnValue.Origin = Origin.Parse(value.Value);
56 | value = GetKeyValue(sdpStream);
57 | }
58 | else {
59 | throw new InvalidDataException();
60 | }
61 |
62 | // Session mandatory.
63 | // However the MuxLab HDMI Encoder (TX-500762) Firmware 1.0.6
64 | // does not include the 'Session' so supress InvalidDatarException
65 | if (value.Key == "s")
66 | {
67 | returnValue.Session = value.Value;
68 | value = GetKeyValue(sdpStream);
69 | }
70 | else {
71 | // throw new InvalidDataException(); // we should throw, but instead we just ignore the error
72 | }
73 |
74 | // Session Information optional
75 | if (value.Key == "i")
76 | {
77 | returnValue.SessionInformation = value.Value;
78 | value = GetKeyValue(sdpStream);
79 | }
80 |
81 | // Uri optional
82 | if (value.Key == "u")
83 | {
84 | returnValue.Url = new Uri(value.Value);
85 | value = GetKeyValue(sdpStream);
86 | }
87 |
88 | // Email optional
89 | if (value.Key == "e")
90 | {
91 | returnValue.Email = value.Value;
92 | value = GetKeyValue(sdpStream);
93 | }
94 |
95 | // Phone optional
96 | if (value.Key == "p")
97 | {
98 | returnValue.Phone = value.Value;
99 | value = GetKeyValue(sdpStream);
100 | }
101 |
102 | // Connection optional
103 | if (value.Key == "c")
104 | {
105 | returnValue.Connection = Connection.Parse(value.Value);
106 | value = GetKeyValue(sdpStream);
107 | }
108 |
109 | // bandwidth optional
110 | if (value.Key == "b")
111 | {
112 | returnValue.Bandwidth = Bandwidth.Parse(value.Value);
113 | value = GetKeyValue(sdpStream);
114 | }
115 |
116 | // Timing mandatory
117 | while (value.Key == "t")
118 | {
119 | string timing = value.Value;
120 | string repeat = string.Empty;
121 | value = GetKeyValue(sdpStream);
122 | if (value.Key == "r")
123 | {
124 | repeat = value.Value;
125 | value = GetKeyValue(sdpStream);
126 | }
127 | returnValue.Timings.Add(new Timing(timing, repeat));
128 | }
129 |
130 | // timezone optional
131 | if (value.Key == "z")
132 | {
133 |
134 | returnValue.TimeZone = SdpTimeZone.ParseInvariant(value.Value);
135 | value = GetKeyValue(sdpStream);
136 | }
137 |
138 | // encryption key optional
139 | if (value.Key == "k")
140 | {
141 |
142 | returnValue.EncriptionKey = EncriptionKey.ParseInvariant(value.Value);
143 | value = GetKeyValue(sdpStream);
144 | }
145 |
146 | //Attribute optional multiple
147 | while (value.Key == "a")
148 | {
149 | returnValue.Attributs.Add(Attribut.ParseInvariant(value.Value));
150 | value = GetKeyValue(sdpStream);
151 | }
152 |
153 | // Hack for MuxLab HDMI Encoder (TX-500762) Firmware 1.0.6
154 | // Skip over all other Key/Value pairs until the 'm=' key
155 | while (value.Key != "m") {
156 | value = GetKeyValue(sdpStream);
157 | }
158 |
159 | // Media
160 | while (value.Key == "m")
161 | {
162 | Media newMedia = ReadMedia(sdpStream, ref value);
163 | returnValue.Medias.Add(newMedia);
164 | }
165 |
166 |
167 | return returnValue;
168 | }
169 |
170 | private static Media ReadMedia(TextReader sdpStream, ref KeyValuePair value)
171 | {
172 | Media returnValue = new Media(value.Value);
173 | value = GetKeyValue(sdpStream);
174 |
175 | // Media title
176 | if (value.Key == "i")
177 | {
178 | value = GetKeyValue(sdpStream);
179 | }
180 |
181 | // Connexion optional
182 | if (value.Key == "c")
183 | {
184 | returnValue.Connection = Connection.Parse(value.Value);
185 | value = GetKeyValue(sdpStream);
186 | }
187 |
188 | // bandwidth optional
189 | if (value.Key == "b")
190 | {
191 | returnValue.Bandwidth = Bandwidth.Parse(value.Value);
192 | value = GetKeyValue(sdpStream);
193 | }
194 |
195 | // enkription key optional
196 | if (value.Key == "k")
197 | {
198 |
199 | returnValue.EncriptionKey = EncriptionKey.ParseInvariant(value.Value);
200 | value = GetKeyValue(sdpStream);
201 | }
202 |
203 | //Attribut optional multiple
204 | while (value.Key == "a")
205 | {
206 | returnValue.Attributs.Add(Attribut.ParseInvariant(value.Value));
207 | value = GetKeyValue(sdpStream);
208 | }
209 |
210 | return returnValue;
211 | }
212 |
213 |
214 | public int Version { get; set; }
215 |
216 |
217 | public Origin Origin { get; set; }
218 |
219 | public string Session { get; set; }
220 |
221 | public string SessionInformation { get; set; }
222 |
223 | public Uri Url { get; set; }
224 |
225 | public string Email { get; set; }
226 |
227 | public string Phone { get; set; }
228 |
229 | public Connection Connection { get; set; }
230 |
231 | public Bandwidth Bandwidth { get; set; }
232 |
233 | private readonly List timingList = new List();
234 |
235 | public IList Timings
236 | {
237 | get
238 | {
239 | return timingList;
240 | }
241 | }
242 |
243 | public SdpTimeZone TimeZone { get; set; }
244 |
245 | public EncriptionKey EncriptionKey { get; set; }
246 |
247 | private readonly List attributs = new List();
248 |
249 | public IList Attributs
250 | {
251 | get
252 | {
253 | return attributs;
254 | }
255 | }
256 |
257 | private readonly List medias = new List();
258 |
259 | public IList Medias
260 | {
261 | get
262 | {
263 | return medias;
264 | }
265 |
266 | }
267 |
268 | }
269 | }
270 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/UdpSocket.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Sockets;
4 | using System.Threading;
5 | using UnityEngine.Profiling;
6 |
7 | namespace Unity.Ig.VideoStreaming.Server
8 | {
9 | public class UDPSocket
10 | {
11 |
12 | private UdpClient data_socket = null;
13 | private UdpClient control_socket = null;
14 |
15 | private Thread data_read_thread = null;
16 | private Thread control_read_thread = null;
17 |
18 | public int data_port = 50000;
19 | public int control_port = 50001;
20 |
21 | bool is_multicast = false;
22 | IPAddress data_mcast_addr;
23 | IPAddress control_mcast_addr;
24 |
25 | ///
26 | /// Initializes a new instance of the class.
27 | /// Creates two new UDP sockets using the start and end Port range
28 | ///
29 | public UDPSocket(int start_port, int end_port)
30 | {
31 |
32 | is_multicast = false;
33 |
34 | // open a pair of UDP sockets - one for data (video or audio) and one for the status channel (RTCP messages)
35 | data_port = start_port;
36 | control_port = start_port + 1;
37 |
38 | bool ok = false;
39 | while (ok == false && (control_port < end_port))
40 | {
41 | // Video/Audio port must be odd and command even (next one)
42 | try
43 | {
44 | data_socket = new UdpClient(data_port);
45 | control_socket = new UdpClient(control_port);
46 | ok = true;
47 | }
48 | catch (SocketException)
49 | {
50 | // Fail to allocate port, try again
51 | if (data_socket != null)
52 | data_socket.Close();
53 | if (control_socket != null)
54 | control_socket.Close();
55 |
56 | // try next data or control port
57 | data_port += 2;
58 | control_port += 2;
59 | }
60 |
61 | if (ok)
62 | {
63 | data_socket.Client.ReceiveBufferSize = 100 * 1024;
64 | data_socket.Client.SendBufferSize = 65535; // default is 8192. Make it as large as possible for large RTP packets which are not fragmented
65 |
66 | control_socket.Client.DontFragment = false;
67 |
68 | }
69 | }
70 | }
71 |
72 |
73 | ///
74 | /// Initializes a new instance of the class.
75 | /// Used with Multicast mode with the Multicast Address and Port
76 | ///
77 | public UDPSocket(String data_multicast_address, int data_multicast_port, String control_multicast_address, int control_multicast_port)
78 | {
79 |
80 | is_multicast = true;
81 |
82 | // open a pair of UDP sockets - one for data (video or audio) and one for the status channel (RTCP messages)
83 | this.data_port = data_multicast_port;
84 | this.control_port = control_multicast_port;
85 |
86 | try
87 | {
88 | IPEndPoint data_ep = new IPEndPoint(IPAddress.Any, data_port);
89 | IPEndPoint control_ep = new IPEndPoint(IPAddress.Any, control_port);
90 |
91 | data_mcast_addr = IPAddress.Parse(data_multicast_address);
92 | control_mcast_addr = IPAddress.Parse(control_multicast_address);
93 |
94 | data_socket = new UdpClient();
95 | data_socket.Client.Bind(data_ep);
96 | data_socket.JoinMulticastGroup(data_mcast_addr);
97 |
98 | control_socket = new UdpClient();
99 | control_socket.Client.Bind(control_ep);
100 | control_socket.JoinMulticastGroup(control_mcast_addr);
101 |
102 |
103 | data_socket.Client.ReceiveBufferSize = 100 * 1024;
104 | data_socket.Client.SendBufferSize = 65535; // default is 8192. Make it as large as possible for large RTP packets which are not fragmented
105 |
106 |
107 | control_socket.Client.DontFragment = false;
108 |
109 | }
110 | catch (SocketException)
111 | {
112 | // Fail to allocate port, try again
113 | if (data_socket != null)
114 | data_socket.Close();
115 | if (control_socket != null)
116 | control_socket.Close();
117 |
118 | return;
119 | }
120 | }
121 |
122 | ///
123 | /// Starts this instance.
124 | ///
125 | public void Start()
126 | {
127 | if (data_socket == null || control_socket == null)
128 | {
129 | throw new InvalidOperationException("UDP Forwader host was not initialized, can't continue");
130 | }
131 |
132 | if (data_read_thread != null)
133 | {
134 | throw new InvalidOperationException("Forwarder was stopped, can't restart it");
135 | }
136 |
137 | data_read_thread = new Thread(() => DoWorkerJob(data_socket, data_port));
138 | data_read_thread.Name = "DataPort " + data_port;
139 | data_read_thread.Start();
140 |
141 | control_read_thread = new Thread(() => DoWorkerJob(control_socket, control_port));
142 | control_read_thread.Name = "ControlPort " + control_port;
143 | control_read_thread.Start();
144 | }
145 |
146 | ///
147 | /// Stops this instance.
148 | ///
149 | public void Stop()
150 | {
151 | if (is_multicast)
152 | {
153 | // leave the multicast groups
154 | data_socket.DropMulticastGroup(data_mcast_addr);
155 | control_socket.DropMulticastGroup(control_mcast_addr);
156 | }
157 | data_socket.Close();
158 | control_socket.Close();
159 | }
160 |
161 | ///
162 | /// Occurs when message is received.
163 | ///
164 | public event EventHandler DataReceived;
165 |
166 | ///
167 | /// Raises the event.
168 | ///
169 | /// The instance containing the event data.
170 | protected void OnDataReceived(RtspChunkEventArgs rtspChunkEventArgs)
171 | {
172 | EventHandler handler = DataReceived;
173 |
174 | if (handler != null)
175 | handler(this, rtspChunkEventArgs);
176 | }
177 |
178 |
179 | ///
180 | /// Does the video job.
181 | ///
182 | private void DoWorkerJob(System.Net.Sockets.UdpClient socket, int data_port)
183 | {
184 | Profiler.BeginThreadProfiling("RTSP", $"UDPSocket.DoWorkerJob port {data_port}");
185 | IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, data_port);
186 | try
187 | {
188 | // loop until we get an exception eg the socket closed
189 | while (true)
190 | {
191 | byte[] frame = socket.Receive(ref ipEndPoint);
192 |
193 | Profiler.BeginSample("OnDataReceived");
194 | // We have an RTP frame.
195 | // Fire the DataReceived event with 'frame'
196 | Console.WriteLine("Received RTP data on port " + data_port);
197 |
198 | Messages.RtspChunk currentMessage = new Messages.RtspData();
199 | // aMessage.SourcePort = ??
200 | currentMessage.Data = frame;
201 | ((Messages.RtspData)currentMessage).Channel = data_port;
202 |
203 |
204 | OnDataReceived(new RtspChunkEventArgs(currentMessage));
205 |
206 | Profiler.EndSample();
207 | }
208 | }
209 | catch (ObjectDisposedException)
210 | {
211 | }
212 | catch (SocketException)
213 | {
214 | }
215 | }
216 |
217 | ///
218 | /// Write to the RTP Data Port
219 | ///
220 | public void Write_To_Data_Port(byte[] data, String hostname, int port) {
221 | data_socket.Send(data,data.Length, hostname, port);
222 | }
223 |
224 | ///
225 | /// Write to the RTP Control Port
226 | ///
227 | public void Write_To_Control_Port(byte[] data, String hostname, int port)
228 | {
229 | data_socket.Send(data, data.Length, hostname, port);
230 | }
231 |
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Globalization;
6 |
7 | namespace Unity.Ig.VideoStreaming.Server.Messages
8 | {
9 | public class RtspResponse : RtspMessage
10 | {
11 | public const int DEFAULT_TIMEOUT = 60;
12 |
13 | ///
14 | /// Gets the default error message for an error code.
15 | ///
16 | /// An error code.
17 | /// The default error message associate
18 | private static string GetDefaultError(int aErrorCode)
19 | {
20 | switch (aErrorCode)
21 | {
22 |
23 | case 100: return "Continue";
24 |
25 | case 200: return "OK";
26 | case 201: return "Created";
27 | case 250: return "Low on Storage Space";
28 |
29 | case 300: return "Multiple Choices";
30 | case 301: return "Moved Permanently";
31 | case 302: return "Moved Temporarily";
32 | case 303: return "See Other";
33 | case 305: return "Use Proxy";
34 |
35 | case 400: return "Bad Request";
36 | case 401: return "Unauthorized";
37 | case 402: return "Payment Required";
38 | case 403: return "Forbidden";
39 | case 404: return "Not Found";
40 | case 405: return "Method Not Allowed";
41 | case 406: return "Not Acceptable";
42 | case 407: return "Proxy Authentication Required";
43 | case 408: return "Request Timeout";
44 | case 410: return "Gone";
45 | case 411: return "Length Required";
46 | case 412: return "Precondition Failed";
47 | case 413: return "Request Entity Too Large";
48 | case 414: return "Request-URI Too Long";
49 | case 415: return "Unsupported Media Type";
50 | case 451: return "Invalid parameter";
51 | case 452: return "Illegal Conference Identifier";
52 | case 453: return "Not Enough Bandwidth";
53 | case 454: return "Session Not Found";
54 | case 455: return "Method Not Valid In This State";
55 | case 456: return "Header Field Not Valid";
56 | case 457: return "Invalid Range";
57 | case 458: return "Parameter Is Read-Only";
58 | case 459: return "Aggregate Operation Not Allowed";
59 | case 460: return "Only Aggregate Operation Allowed";
60 | case 461: return "Unsupported Transport";
61 | case 462: return "Destination Unreachable";
62 |
63 | case 500: return "Internal Server Error";
64 | case 501: return "Not Implemented";
65 | case 502: return "Bad Gateway";
66 | case 503: return "Service Unavailable";
67 | case 504: return "Gateway Timeout";
68 | case 505: return "RTSP Version Not Supported";
69 | case 551: return "Option not support";
70 | default:
71 | return "Return: " + aErrorCode.ToString(CultureInfo.InvariantCulture);
72 | }
73 | }
74 |
75 | ///
76 | /// Initializes a new instance of the class.
77 | ///
78 | public RtspResponse()
79 | : base()
80 | {
81 | // Initialise with a default result code.
82 | Command = "RTSP/1.0 200 OK";
83 | }
84 |
85 | private int _returnCode;
86 | ///
87 | /// Gets or sets the return code of the response.
88 | ///
89 | /// The return code.
90 | /// On change the error message is set to the default one associate with the code
91 | public int ReturnCode
92 | {
93 | get
94 | {
95 | if (_returnCode == 0 && commandArray.Length >= 2)
96 | {
97 | int.TryParse(commandArray[1], out _returnCode);
98 | }
99 |
100 | return _returnCode;
101 | }
102 | set
103 | {
104 | if (ReturnCode != value)
105 | {
106 | _returnCode = value;
107 | // make sure we have the room
108 | if (commandArray.Length < 3)
109 | {
110 | Array.Resize(ref commandArray, 3);
111 | }
112 | commandArray[1] = value.ToString(CultureInfo.InvariantCulture);
113 | commandArray[2] = GetDefaultError(value);
114 | }
115 | }
116 | }
117 |
118 | ///
119 | /// Gets or sets the error/return message.
120 | ///
121 | /// The return message.
122 | public string ReturnMessage
123 | {
124 | get
125 | {
126 | if (commandArray.Length < 3)
127 | return String.Empty;
128 | return commandArray[2];
129 | }
130 | set
131 | {
132 | // Make sure we have the room
133 | if (commandArray.Length < 3)
134 | {
135 | Array.Resize(ref commandArray, 3);
136 | }
137 | commandArray[2] = value;
138 |
139 | }
140 | }
141 |
142 | ///
143 | /// Gets a value indicating whether this instance correspond to an OK response.
144 | ///
145 | /// true if this instance is OK; otherwise, false.
146 | public bool IsOk
147 | {
148 | get
149 | {
150 | if (ReturnCode > 0 && ReturnCode < 400)
151 | return true;
152 | return false;
153 | }
154 | }
155 |
156 | ///
157 | /// Gets the timeout in second.
158 | /// The default timeout is 60.
159 | ///
160 | /// The timeout.
161 | public int Timeout
162 | {
163 | get
164 | {
165 | int returnValue = DEFAULT_TIMEOUT;
166 | if (Headers.ContainsKey(RtspHeaderNames.Session))
167 | {
168 | string[] parts = Headers[RtspHeaderNames.Session].Split(';');
169 | if (parts.Length > 1)
170 | {
171 | string[] subParts = parts[1].Split('=');
172 | if (subParts.Length > 1 &&
173 | subParts[0].ToUpperInvariant() == "TIMEOUT")
174 | if (!int.TryParse(subParts[1], out returnValue))
175 | returnValue = DEFAULT_TIMEOUT;
176 | }
177 | }
178 | return returnValue;
179 | }
180 | set
181 | {
182 | if(Headers.ContainsKey(RtspHeaderNames.Session))
183 | if (value != DEFAULT_TIMEOUT)
184 | {
185 |
186 | Headers[RtspHeaderNames.Session] = Headers[RtspHeaderNames.Session].Split(';').First()
187 | + ";timeout=" + value.ToString(CultureInfo.InvariantCulture);
188 | }
189 | else
190 | {
191 | //remove timeout part
192 | Headers[RtspHeaderNames.Session] = Headers[RtspHeaderNames.Session].Split(';').First();
193 | }
194 | }
195 | }
196 |
197 | ///
198 | /// Gets the session ID.
199 | ///
200 | /// The session ID.
201 | public override string Session
202 | {
203 | get
204 | {
205 | if (!Headers.ContainsKey(RtspHeaderNames.Session))
206 | return null;
207 |
208 | return Headers[RtspHeaderNames.Session].Split(';')[0];
209 | }
210 | set
211 | {
212 | if(Timeout != DEFAULT_TIMEOUT)
213 | {
214 | Headers[RtspHeaderNames.Session] = value + ";timeout=" + Timeout.ToString(CultureInfo.InvariantCulture);
215 | }
216 | else
217 | {
218 | Headers[RtspHeaderNames.Session] = value;
219 | }
220 | }
221 | }
222 |
223 | ///
224 | /// Gets or sets the original request associate with the response.
225 | ///
226 | /// The original request.
227 | public RtspRequest OriginalRequest
228 | { get; set; }
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/H264Payload.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using UnityEngine;
5 |
6 | namespace Unity.Ig.VideoStreaming.Server
7 | {
8 | // This class handles the H264 Payload
9 | // It has methods to parse parameters in the SDP
10 | // It has methods to process the RTP Payload
11 |
12 | public class H264Payload
13 | {
14 | private static ILogger _logger = new Logger(Debug.unityLogger.logHandler);
15 | static H264Payload()
16 | {
17 | _logger.logEnabled = false;
18 | }
19 |
20 | int norm, fu_a, fu_b, stap_a, stap_b, mtap16, mtap24 = 0; // used for diagnostics stats
21 |
22 | List temporary_rtp_payloads = new List(); // used to assemble the RTP packets that form one RTP Frame
23 | // Eg all the RTP Packets from M=0 through to M=1
24 |
25 | MemoryStream fragmented_nal = new MemoryStream(); // used to concatenate fragmented H264 NALs where NALs are split over RTP packets
26 |
27 |
28 | // Constructor
29 | public H264Payload()
30 | {
31 | }
32 |
33 | public List Process_H264_RTP_Packet(byte[] rtp_payload, int rtp_marker) {
34 |
35 | // Add to the list of payloads for the current Frame of video
36 | temporary_rtp_payloads.Add(rtp_payload); // Todo Could optimise this and go direct to Process Frame if just 1 packet in frame
37 |
38 | if (rtp_marker == 1)
39 | {
40 | // End Marker is set. Process the list of RTP Packets (forming 1 RTP frame) and save the NALs to a file
41 | List nal_units = Process_H264_RTP_Frame(temporary_rtp_payloads);
42 | temporary_rtp_payloads.Clear();
43 |
44 | return nal_units;
45 | }
46 |
47 | return null; // we don't have a frame yet. Keep accumulating RTP packets
48 | }
49 |
50 |
51 | // Process a RTP Frame. A RTP Frame can consist of several RTP Packets which have the same Timestamp
52 | // Returns a list of NAL Units (with no 00 00 00 01 header and with no Size header)
53 | private List Process_H264_RTP_Frame(List rtp_payloads)
54 | {
55 | _logger.Log("RTP Data comprised of " + rtp_payloads.Count + " rtp packets");
56 |
57 | List nal_units = new List(); // Stores the NAL units for a Video Frame. May be more than one NAL unit in a video frame.
58 |
59 | for (int payload_index = 0; payload_index < rtp_payloads.Count; payload_index++)
60 | {
61 | // Examine the first rtp_payload and the first byte (the NAL header)
62 | int nal_header_f_bit = (rtp_payloads[payload_index][0] >> 7) & 0x01;
63 | int nal_header_nri = (rtp_payloads[payload_index][0] >> 5) & 0x03;
64 | int nal_header_type = (rtp_payloads[payload_index][0] >> 0) & 0x1F;
65 |
66 | // If the Nal Header Type is in the range 1..23 this is a normal NAL (not fragmented)
67 | // So write the NAL to the file
68 | if (nal_header_type >= 1 && nal_header_type <= 23)
69 | {
70 | _logger.Log("Normal NAL");
71 | norm++;
72 | nal_units.Add(rtp_payloads[payload_index]);
73 | }
74 | // There are 4 types of Aggregation Packet (split over RTP payloads)
75 | else if (nal_header_type == 24)
76 | {
77 | _logger.Log("Agg STAP-A");
78 | stap_a++;
79 |
80 | // RTP packet contains multiple NALs, each with a 16 bit header
81 | // Read 16 byte size
82 | // Read NAL
83 | try
84 | {
85 | int ptr = 1; // start after the nal_header_type which was '24'
86 | // if we have at least 2 more bytes (the 16 bit size) then consume more data
87 | while (ptr + 2 < (rtp_payloads[payload_index].Length - 1))
88 | {
89 | int size = (rtp_payloads[payload_index][ptr] << 8) + (rtp_payloads[payload_index][ptr + 1] << 0);
90 | ptr = ptr + 2;
91 | byte[] nal = new byte[size];
92 | System.Array.Copy(rtp_payloads[payload_index], ptr, nal, 0, size); // copy the NAL
93 | nal_units.Add(nal); // Add to list of NALs for this RTP frame. Start Codes like 00 00 00 01 get added later
94 | ptr = ptr + size;
95 | }
96 | }
97 | catch
98 | {
99 | _logger.Log("H264 Aggregate Packet processing error");
100 | }
101 | }
102 | else if (nal_header_type == 25)
103 | {
104 | _logger.Log("Agg STAP-B not supported");
105 | stap_b++;
106 | }
107 | else if (nal_header_type == 26)
108 | {
109 | _logger.Log("Agg MTAP16 not supported");
110 | mtap16++;
111 | }
112 | else if (nal_header_type == 27)
113 | {
114 | _logger.Log("Agg MTAP24 not supported");
115 | mtap24++;
116 | }
117 | else if (nal_header_type == 28)
118 | {
119 | _logger.Log("Frag FU-A");
120 | fu_a++;
121 |
122 | // Parse Fragmentation Unit Header
123 | int fu_header_s = (rtp_payloads[payload_index][1] >> 7) & 0x01; // start marker
124 | int fu_header_e = (rtp_payloads[payload_index][1] >> 6) & 0x01; // end marker
125 | int fu_header_r = (rtp_payloads[payload_index][1] >> 5) & 0x01; // reserved. should be 0
126 | int fu_header_type = (rtp_payloads[payload_index][1] >> 0) & 0x1F; // Original NAL unit header
127 |
128 | _logger.Log("Frag FU-A s=" + fu_header_s + "e=" + fu_header_e);
129 |
130 | // Check Start and End flags
131 | if (fu_header_s == 1 && fu_header_e == 0)
132 | {
133 | // Start of Fragment.
134 | // Initiise the fragmented_nal byte array
135 | // Build the NAL header with the original F and NRI flags but use the the Type field from the fu_header_type
136 | byte reconstructed_nal_type = (byte)((nal_header_f_bit << 7) + (nal_header_nri << 5) + fu_header_type);
137 |
138 | // Empty the stream
139 | fragmented_nal.SetLength(0);
140 |
141 | // Add reconstructed_nal_type byte to the memory stream
142 | fragmented_nal.WriteByte(reconstructed_nal_type);
143 |
144 | // copy the rest of the RTP payload to the memory stream
145 | fragmented_nal.Write(rtp_payloads[payload_index], 2, rtp_payloads[payload_index].Length - 2);
146 | }
147 |
148 | if (fu_header_s == 0 && fu_header_e == 0)
149 | {
150 | // Middle part of Fragment
151 | // Append this payload to the fragmented_nal
152 | // Data starts after the NAL Unit Type byte and the FU Header byte
153 | fragmented_nal.Write(rtp_payloads[payload_index], 2, rtp_payloads[payload_index].Length - 2);
154 | }
155 |
156 | if (fu_header_s == 0 && fu_header_e == 1)
157 | {
158 | // End part of Fragment
159 | // Append this payload to the fragmented_nal
160 | // Data starts after the NAL Unit Type byte and the FU Header byte
161 | fragmented_nal.Write(rtp_payloads[payload_index], 2, rtp_payloads[payload_index].Length - 2);
162 |
163 | // Add the NAL to the array of NAL units
164 | nal_units.Add(fragmented_nal.ToArray());
165 | }
166 | }
167 |
168 | else if (nal_header_type == 29)
169 | {
170 | _logger.Log("Frag FU-B not supported");
171 | fu_b++;
172 | }
173 | else
174 | {
175 | _logger.Log("Unknown NAL header " + nal_header_type + " not supported");
176 | }
177 |
178 | }
179 |
180 | // Output some statistics
181 | _logger.Log("Norm=" + norm + " ST-A=" + stap_a + " ST-B=" + stap_b + " M16=" + mtap16 + " M24=" + mtap24 + " FU-A=" + fu_a + " FU-B=" + fu_b);
182 |
183 | // Output all the NALs that form one RTP Frame (one frame of video)
184 | return nal_units;
185 |
186 | }
187 |
188 |
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/VideoStreamingServer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using System.Net.Sockets;
6 | using Unity.Collections;
7 | using Unity.Jobs;
8 | using UnityEngine;
9 | using UnityEngine.Profiling;
10 | using UnityEngine.Rendering;
11 | using Unity.Ig.VideoStreaming.Server;
12 |
13 | namespace Unity.Ig.VideoStreaming.Server
14 | {
15 | public class VideoStreamingServer : MonoBehaviour
16 | {
17 | [SerializeField]
18 | int m_Port = 8554;
19 | public int Port { set { m_Port = value; } }
20 |
21 | [SerializeField]
22 | string m_Username = "user"; // or use NUL if there is no username
23 | [SerializeField]
24 | string m_Password = "password"; // or use NUL if there is no password
25 |
26 | public void SetCredentials(string username, string password)
27 | {
28 | m_Username = username;
29 | m_Password = password;
30 | }
31 |
32 | [SerializeField]
33 | RenderTexture m_SourceTexture;
34 | public RenderTexture SourceTexture { set { m_SourceTexture = value; } }
35 |
36 | [SerializeField]
37 | int m_Framerate = 30;
38 | public int Framerate { set { m_Framerate = value; } }
39 |
40 | RtspServer m_Server;
41 | H264Encoder m_Encoder;
42 | const float m_StartTime = 3.0F;
43 | int m_LastFrameIndex = -1;
44 | bool m_RtspServerActive = false;
45 | RenderTexture m_NV12Texture;
46 | Material m_RGBToNV12Material;
47 |
48 | struct H264EncoderForStartup : IH264Encoder
49 | {
50 | public byte[] spsNalu;
51 | public byte[] ppsNalu;
52 |
53 | public void CompressFrame(NativeArray data, ref ArraySegment compressedData, out bool isKeyFrame)
54 | {
55 | throw new NotImplementedException();
56 | }
57 |
58 | public bool ExpectsYUV()
59 | {
60 | return false;
61 | }
62 |
63 | public byte[] GetRawPPS()
64 | {
65 | return ppsNalu;
66 | }
67 |
68 | public byte[] GetRawSPS()
69 | {
70 | return spsNalu;
71 | }
72 | }
73 |
74 | // Start is called before the first frame update
75 | void Start()
76 | {
77 | if (m_SourceTexture == null)
78 | return;
79 |
80 | try
81 | {
82 | m_Server = new RtspServer(m_Port, m_Username, m_Password);
83 | m_Encoder = new H264Encoder();
84 | NativeList spsNalu = new NativeList(Allocator.TempJob);
85 | NativeList ppsNalu = new NativeList(Allocator.TempJob);
86 | var startJob = m_Encoder.Start((uint)m_SourceTexture.width, (uint)m_SourceTexture.height,
87 | (uint)m_Framerate, ref spsNalu, ref ppsNalu);
88 | m_JobQueue.Enqueue(new JobItem { job = startJob, spsNalu = spsNalu, ppsNalu = ppsNalu });
89 |
90 | if (String.IsNullOrEmpty(m_Username) != String.IsNullOrEmpty(m_Password))
91 | Debug.LogError("Both username and password should be specified, not just one of them.");
92 | else if (!String.IsNullOrEmpty(m_Username) && !String.IsNullOrEmpty(m_Password))
93 | Debug.Log($"Watch the output on rtsp://{m_Username}:{m_Password}@{GetMyIPAddress()}:{m_Port}");
94 | else
95 | Debug.Log($"Watch the output on rtsp://{GetMyIPAddress()}:{m_Port}");
96 |
97 | var shaderName = "Video/RGBToNV12";
98 | var rgbToNV12Shader = Shader.Find(shaderName);
99 | if (rgbToNV12Shader == null)
100 | Debug.LogError("Could not find shader " + shaderName);
101 | m_RGBToNV12Material = new Material(rgbToNV12Shader);
102 | m_NV12Texture = new RenderTexture(m_SourceTexture.width, m_SourceTexture.height * 3 / 2, 0, RenderTextureFormat.R8, RenderTextureReadWrite.Linear);
103 | m_NV12Texture.Create();
104 | }
105 | catch (Exception e)
106 | {
107 | Debug.LogError("Error: Could not start server: " + e);
108 | }
109 | }
110 |
111 | void OnDestroy()
112 | {
113 | m_Server?.StopListen();
114 | m_Server?.Dispose();
115 | while (m_JobQueue.Count > 0)
116 | CompleteJobs();
117 | m_Server = null;
118 | m_Encoder.Dispose();
119 | if (m_NV12Texture != null)
120 | {
121 | Destroy(m_NV12Texture);
122 | m_NV12Texture = null;
123 | }
124 |
125 | if (m_RGBToNV12Material != null)
126 | {
127 | Destroy(m_RGBToNV12Material);
128 | m_RGBToNV12Material = null;
129 | }
130 | }
131 |
132 | struct JobItem
133 | {
134 | public AsyncGPUReadbackRequest req;
135 | public NativeList spsNalu;
136 | public NativeList ppsNalu;
137 | public NativeList imageNalu;
138 | public JobHandle job;
139 | public ulong timeStampNs;
140 |
141 | public bool CompleteIfPossible()
142 | {
143 | if (!job.IsCompleted)
144 | return false;
145 | job.Complete();
146 | return true;
147 | }
148 |
149 | public void DisposeLists()
150 | {
151 | if (spsNalu.IsCreated)
152 | spsNalu.Dispose();
153 | if (ppsNalu.IsCreated)
154 | ppsNalu.Dispose();
155 | if (imageNalu.IsCreated)
156 | imageNalu.Dispose();
157 | }
158 | };
159 |
160 | Queue m_JobQueue = new Queue();
161 |
162 | void Update()
163 | {
164 | CompleteJobs();
165 | if (m_Framerate == 0 || m_SourceTexture == null)
166 | return;
167 |
168 | var curTime = Time.timeSinceLevelLoad;
169 | if (curTime < m_StartTime)
170 | return;
171 |
172 | var elapsedTime = curTime - m_StartTime;
173 | var frameIndex = (int)(elapsedTime * m_Framerate);
174 | if (frameIndex == m_LastFrameIndex)
175 | return;
176 |
177 | m_RtspServerActive = m_Server.RefreshConnectionList();
178 | if (!m_RtspServerActive)
179 | return;
180 |
181 | ulong timeStampNs = (ulong)(elapsedTime * 1000000000);
182 | m_LastFrameIndex = frameIndex;
183 | var readbackStart = Time.realtimeSinceStartup;
184 |
185 | Graphics.Blit(m_SourceTexture, m_NV12Texture, m_RGBToNV12Material);
186 |
187 | Profiler.BeginSample("Schedule AsyncGPUReadback");
188 | AsyncGPUReadback.Request(m_NV12Texture, 0, (request) =>
189 | {
190 | //Debug.Log($"Readback took {Time.realtimeSinceStartup - readbackStart} s");
191 | EncodeImage(request, timeStampNs);
192 | });
193 | Profiler.EndSample();
194 | }
195 |
196 | void EncodeImage(AsyncGPUReadbackRequest req, ulong timeStampNs)
197 | {
198 | if (m_Server == null)
199 | return;
200 | var spsNalu = new NativeList(Allocator.TempJob);
201 | var ppsNalu = new NativeList(Allocator.TempJob);
202 | var imageNalu = new NativeList(Allocator.TempJob);
203 | JobHandle prevJob = m_JobQueue.Count > 0 ? m_JobQueue.Peek().job : default(JobHandle);
204 | var encodeJob = m_Encoder.Encode(prevJob, req.GetData(), timeStampNs, ref spsNalu, ref ppsNalu, ref imageNalu);
205 |
206 | //Debug.Log("Encode image at t=" + timeStampNs);
207 | if (encodeJob.Equals(default(JobHandle)))
208 | {
209 | spsNalu.Dispose();
210 | ppsNalu.Dispose();
211 | imageNalu.Dispose();
212 | return;
213 | }
214 |
215 | m_JobQueue.Enqueue(new JobItem { job = encodeJob, req = req, spsNalu = spsNalu, ppsNalu = ppsNalu, imageNalu = imageNalu, timeStampNs = timeStampNs });
216 | }
217 |
218 | private void CompleteJobs()
219 | {
220 | while (m_JobQueue.Count > 0)
221 | {
222 | if (!m_JobQueue.Peek().CompleteIfPossible())
223 | break;
224 | var jobItem = m_JobQueue.Dequeue();
225 | if (!jobItem.imageNalu.IsCreated)
226 |
227 | // Nalu without image denotes the initial job, which just returns the sps/pps. Getting this info makes us ready to open
228 | // the RTSP TCP end point.
229 | m_Server.StartListen(new H264EncoderForStartup()
230 | {
231 | spsNalu = jobItem.spsNalu.ToArray(),
232 | ppsNalu = jobItem.ppsNalu.ToArray()
233 | });
234 | else
235 | {
236 | //Debug.LogFormat("Sending image NALU with {0} bytes", jobItem.imageNalu.Length);
237 | m_Server.SendNALUs(jobItem.timeStampNs, in jobItem.spsNalu, in jobItem.ppsNalu, in jobItem.imageNalu);
238 | }
239 |
240 | jobItem.DisposeLists();
241 | }
242 | }
243 |
244 | string GetMyIPAddress()
245 | {
246 | var host = Dns.GetHostEntry(Dns.GetHostName());
247 | var ipAddress = host.AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
248 | return ipAddress.ToString();
249 | }
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/H265Payload.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace Unity.Ig.VideoStreaming.Server
6 | {
7 | // This class handles the H265 Payload
8 | // It has methods to parse parameters in the SDP
9 | // It has methods to process the RTP Payload
10 |
11 | // By Roger Hardiman, RJH Technical Consultancy Ltd
12 |
13 | public class H265Payload
14 | {
15 | // H265 / HEVC structure.
16 | // An 'Access Unit' is the set of NAL Units that form one Picture
17 | // NAL Units have a 2 byte header comprising of
18 | // F Bit, Type, Layer ID and TID
19 |
20 |
21 | int single, agg, frag = 0; // used for diagnostics stats
22 | bool has_donl = false;
23 |
24 | List temporary_rtp_payloads = new List(); // used to assemble the RTP packets that form one RTP Frame
25 | // Eg all the RTP Packets from M=0 through to M=1
26 |
27 | MemoryStream fragmented_nal = new MemoryStream(); // used to concatenate fragmented H264 NALs where NALs are split over RTP packets
28 |
29 |
30 | // Constructor
31 | public H265Payload(bool has_donl)
32 | {
33 | this.has_donl = has_donl;
34 | }
35 |
36 | public List Process_H265_RTP_Packet(byte[] rtp_payload, int rtp_marker) {
37 |
38 | // Add payload to the List of payloads for the current Frame of Video
39 | // ie all the payloads with M=0 up to the final payload where M=1
40 | temporary_rtp_payloads.Add(rtp_payload); // Todo Could optimise this and go direct to Process Frame if just 1 packet in frame
41 |
42 | if (rtp_marker == 1)
43 | {
44 | // End Marker is set. Process the list of RTP Packets (forming 1 RTP frame) and save the NALs to a file
45 | List nal_units = Process_H265_RTP_Frame(temporary_rtp_payloads);
46 | temporary_rtp_payloads.Clear();
47 |
48 | return nal_units;
49 | }
50 |
51 | return null; // we don't have a frame yet. Keep accumulating RTP packets
52 | }
53 |
54 |
55 | // Process a RTP Frame. A RTP Frame can consist of several RTP Packets which have the same Timestamp
56 | // Returns a list of NAL Units (with no 00 00 00 01 header and with no Size header)
57 | private List Process_H265_RTP_Frame(List rtp_payloads)
58 | {
59 | Console.WriteLine("RTP Data comprised of " + rtp_payloads.Count + " rtp packets");
60 |
61 | List nal_units = new List(); // Stores the NAL units for a Video Frame. May be more than one NAL unit in a video frame.
62 |
63 | for (int payload_index = 0; payload_index < rtp_payloads.Count; payload_index++)
64 | {
65 | // Examine the first two bytes of the RTP data, the Payload Header
66 | // F (Forbidden Bit),
67 | // Type of NAL Unit (or VCL NAL Unit if Type is < 32),
68 | // LayerId
69 | // TID (TemporalID = TID - 1)
70 | /*+---------------+---------------+
71 | *|0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
72 | *+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
73 | *|F| Type | LayerId | TID |
74 | *+-------------+-----------------+
75 | */
76 |
77 | int payload_header = (rtp_payloads[payload_index][0] << 8) | (rtp_payloads[payload_index][1]);
78 | int payload_header_f_bit = (payload_header >> 15) & 0x01;
79 | int payload_header_type = (payload_header >> 9) & 0x3F;
80 | int payload_header_layer_id = (payload_header >> 3) & 0x3F;
81 | int payload_header_tid = payload_header & 0x7;
82 |
83 |
84 | // There are three ways to Packetize NAL units into RTP Packets
85 | // Single NAL Unit Packet
86 | // Aggregation Packet (payload_header_type = 48)
87 | // Fragmentation Unit (payload_header_type = 49)
88 |
89 |
90 | // Single NAL Unit Packet
91 | // 32=VPS
92 | // 33=SPS
93 | // 34=PPS
94 | if (payload_header_type != 48 && payload_header_type != 49)
95 | {
96 | Console.WriteLine("Single NAL");
97 | single++;
98 |
99 | //TODO - Handle DONL
100 |
101 | nal_units.Add(rtp_payloads[payload_index]);
102 | }
103 |
104 | // Aggregation Packet
105 | else if (payload_header_type == 48)
106 | {
107 | Console.WriteLine("Aggregation Packet");
108 | agg++;
109 |
110 | // RTP packet contains multiple NALs, each with a 16 bit header
111 | // Read 16 byte size
112 | // Read NAL
113 | // Use a Try/Catch to protect from bad RTP data where block sizes exceed the
114 | // available data
115 | try
116 | {
117 | int ptr = 2; // start after 16 bit Payload Header
118 |
119 | // loop until the ptr has moved beyond the length of the data
120 | while (ptr < (rtp_payloads[payload_index].Length - 1))
121 | {
122 | if (has_donl) ptr = ptr + 2; // step over the DONL data
123 | int size = (rtp_payloads[payload_index][ptr] << 8) + (rtp_payloads[payload_index][ptr + 1] << 0);
124 | ptr = ptr + 2;
125 | byte[] nal = new byte[size];
126 | System.Array.Copy(rtp_payloads[payload_index], ptr, nal, 0, size); // copy the NAL
127 | nal_units.Add(nal); // Add to list of NALs for this RTP frame. Start Codes like 00 00 00 01 get added later
128 | ptr = ptr + size;
129 | }
130 | }
131 | catch
132 | {
133 | Console.WriteLine("H265 Aggregate Packet processing error");
134 | }
135 | }
136 |
137 | // Fragmentation Unit
138 | else if (payload_header_type == 49)
139 | {
140 | Console.WriteLine("Fragmentation Unit");
141 | frag++;
142 |
143 | // Parse Fragmentation Unit Header
144 | int fu_header_s = (rtp_payloads[payload_index][2] >> 7) & 0x01; // start marker
145 | int fu_header_e = (rtp_payloads[payload_index][2] >> 6) & 0x01; // end marker
146 | int fu_header_type = (rtp_payloads[payload_index][2] >> 0) & 0x3F; // fu type
147 |
148 | Console.WriteLine("Frag FU-A s=" + fu_header_s + "e=" + fu_header_e);
149 |
150 | // Check Start and End flags
151 | if (fu_header_s == 1 && fu_header_e == 0)
152 | {
153 | // Start of Fragment.
154 | // Initiise the fragmented_nal byte array
155 |
156 | // Empty the stream
157 | fragmented_nal.SetLength(0);
158 |
159 | // Reconstrut the NAL header from the rtp_payload_header, replacing the Type with FU Type
160 | int nal_header = (payload_header & 0x81FF); // strip out existing 'type'
161 | nal_header = nal_header | (fu_header_type << 9);
162 |
163 | fragmented_nal.WriteByte((byte)((nal_header >> 8) & 0xFF));
164 | fragmented_nal.WriteByte((byte)((nal_header >> 0) & 0xFF));
165 |
166 | if (has_donl)
167 | {
168 | // start copying after the DONL data
169 | fragmented_nal.Write(rtp_payloads[payload_index], 5, rtp_payloads[payload_index].Length - 5);
170 | }
171 | else
172 | {
173 | // there is no DONL data
174 | fragmented_nal.Write(rtp_payloads[payload_index], 3, rtp_payloads[payload_index].Length - 3);
175 | }
176 | }
177 |
178 | if (fu_header_s == 0 && fu_header_e == 0)
179 | {
180 | // Middle part of Fragment
181 | // Append this payload to the fragmented_nal
182 |
183 | if (has_donl) {
184 | // start copying after the DONL data
185 | fragmented_nal.Write(rtp_payloads[payload_index], 5, rtp_payloads[payload_index].Length - 5);
186 | } else {
187 | // there is no DONL data
188 | fragmented_nal.Write(rtp_payloads[payload_index], 3, rtp_payloads[payload_index].Length - 3);
189 | }
190 | }
191 |
192 | if (fu_header_s == 0 && fu_header_e == 1)
193 | {
194 | // End part of Fragment
195 | // Append this payload to the fragmented_nal
196 | if (has_donl)
197 | {
198 | // start copying after the DONL data
199 | fragmented_nal.Write(rtp_payloads[payload_index], 5, rtp_payloads[payload_index].Length - 5);
200 | }
201 | else
202 | {
203 | // there is no DONL data
204 | fragmented_nal.Write(rtp_payloads[payload_index], 3, rtp_payloads[payload_index].Length - 3);
205 | }
206 |
207 | // Add the NAL to the array of NAL units
208 | nal_units.Add(fragmented_nal.ToArray());
209 | }
210 | }
211 | else {
212 | Console.WriteLine("Unknown Payload Header Type = " + payload_header_type);
213 | }
214 | }
215 |
216 | // Output some statistics
217 | Console.WriteLine("Single=" + single + " Agg=" + agg + " Frag=" + frag);
218 |
219 | // Output all the NALs that form one RTP Frame (one frame of video)
220 | return nal_units;
221 |
222 | }
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPMessage.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace Unity.Ig.VideoStreaming.Server.Messages
4 | {
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Diagnostics.Contracts;
8 | using System.Globalization;
9 | using System.IO;
10 | using System.Text;
11 | using System.Text.RegularExpressions;
12 |
13 | public class RtspMessage : RtspChunk
14 | {
15 | private static ILogger _logger = new Logger(Debug.unityLogger.logHandler);
16 |
17 | static RtspMessage()
18 | {
19 | _logger.logEnabled = false;
20 | }
21 |
22 | ///
23 | /// The regex to validate the Rtsp message.
24 | ///
25 | private static readonly Regex _rtspVersionTest = new Regex(@"^RTSP/\d\.\d", RegexOptions.Compiled);
26 | ///
27 | /// Create the good type of Rtsp Message from the header.
28 | ///
29 | /// A request line.
30 | /// An Rtsp message
31 | public static RtspMessage GetRtspMessage(string aRequestLine)
32 | {
33 | // We can't determine the message
34 | if (string.IsNullOrEmpty(aRequestLine))
35 | return new RtspMessage();
36 | string[] requestParts = aRequestLine.Split(new char[] { ' ' }, 3);
37 | RtspMessage returnValue;
38 | if (requestParts.Length == 3)
39 | {
40 | // A request is : Method SP Request-URI SP RTSP-Version
41 | // A response is : RTSP-Version SP Status-Code SP Reason-Phrase
42 | // RTSP-Version = "RTSP" "/" 1*DIGIT "." 1*DIGIT
43 | if (_rtspVersionTest.IsMatch(requestParts[2]))
44 | returnValue = RtspRequest.GetRtspRequest(requestParts);
45 | else if (_rtspVersionTest.IsMatch(requestParts[0]))
46 | returnValue = new RtspResponse();
47 | else
48 | {
49 | _logger.LogFormat(LogType.Warning, "Got a strange message {0}", aRequestLine);
50 | returnValue = new RtspMessage();
51 | }
52 | }
53 | else
54 | {
55 | _logger.LogFormat(LogType.Warning, "Got a strange message {0}", aRequestLine);
56 | returnValue = new RtspMessage();
57 | }
58 | returnValue.Command = aRequestLine;
59 | return returnValue;
60 | }
61 |
62 | ///
63 | /// Initializes a new instance of the class.
64 | ///
65 | public RtspMessage()
66 | {
67 | Data = new byte[0];
68 | Creation = DateTime.Now;
69 | }
70 |
71 | private Dictionary _headers = new Dictionary(StringComparer.OrdinalIgnoreCase);
72 |
73 | internal protected string[] commandArray;
74 |
75 | ///
76 | /// Gets or sets the creation time.
77 | ///
78 | /// The creation time.
79 | public DateTime Creation { get; private set; }
80 |
81 | ///
82 | /// Gets or sets the command of the message (first line).
83 | ///
84 | /// The command.
85 | public string Command
86 | {
87 | get
88 | {
89 | if (commandArray == null)
90 | return string.Empty;
91 | return string.Join(" ", commandArray);
92 | }
93 | set
94 | {
95 | if (value == null)
96 | commandArray = new string[] { String.Empty };
97 | else
98 | commandArray = value.Split(new char[] {' '}, 3);
99 | }
100 | }
101 |
102 |
103 | ///
104 | /// Gets the Method of the message (eg OPTIONS, DESCRIBE, SETUP, PLAY).
105 | ///
106 | /// The Method
107 | public string Method
108 | {
109 | get
110 | {
111 | if (commandArray == null)
112 | return string.Empty;
113 | return commandArray[0];
114 | }
115 | }
116 |
117 |
118 | ///
119 | /// Gets the headers of the message.
120 | ///
121 | /// The headers.
122 | public Dictionary Headers
123 | {
124 | get
125 | {
126 | return _headers;
127 | }
128 | }
129 |
130 | ///
131 | /// Adds one header from a string.
132 | ///
133 | /// The string containing header of format Header: Value.
134 | /// is null
135 | public void AddHeader(string line)
136 | {
137 | if (line == (string)null)
138 | throw new ArgumentNullException("line");
139 |
140 | //spliter
141 | string[] elements = line.Split(new char[] { ':' }, 2);
142 | if (elements.Length == 2)
143 | {
144 | _headers[elements[0].Trim()] = elements[1].TrimStart();
145 | }
146 | else
147 | {
148 | _logger.LogFormat(LogType.Warning, "Invalid Header received : -{0}-", line);
149 | }
150 | }
151 |
152 | ///
153 | /// Gets or sets the Ccommande Seqquence number.
154 | /// If the header is not define or not a valid number it return 0
155 | ///
156 | /// The sequence number.
157 | public int CSeq
158 | {
159 | get
160 | {
161 | string returnStringValue;
162 | int returnValue;
163 | if (!(_headers.TryGetValue("CSeq", out returnStringValue) &&
164 | int.TryParse(returnStringValue, out returnValue)))
165 | returnValue = 0;
166 |
167 | return returnValue;
168 | }
169 | set
170 | {
171 | _headers["CSeq"] = value.ToString(CultureInfo.InvariantCulture);
172 | }
173 | }
174 |
175 | ///
176 | /// Gets the session ID.
177 | ///
178 | /// The session ID.
179 | public virtual string Session
180 | {
181 | get
182 | {
183 | if (!_headers.ContainsKey("Session"))
184 | return null;
185 |
186 | return _headers["Session"];
187 | }
188 | set
189 | {
190 | _headers["Session"] = value;
191 | }
192 | }
193 |
194 | ///
195 | /// Initialises the length of the data byte array from content lenth header.
196 | ///
197 | public void InitialiseDataFromContentLength()
198 | {
199 | int dataLength;
200 | if (!(_headers.ContainsKey("Content-Length")
201 | && int.TryParse(_headers["Content-Length"], out dataLength)))
202 | {
203 | dataLength = 0;
204 | }
205 | this.Data = new byte[dataLength];
206 | }
207 |
208 | ///
209 | /// Adjusts the content length header.
210 | ///
211 | public void AdjustContentLength()
212 | {
213 | if (Data.Length > 0)
214 | {
215 | _headers["Content-Length"] = Data.Length.ToString(CultureInfo.InvariantCulture);
216 | }
217 | else
218 | {
219 | _headers.Remove("Content-Length");
220 | }
221 | }
222 |
223 | ///
224 | /// Sends to the message to a stream.
225 | ///
226 | /// The stream.
227 | /// is empty
228 | /// can't be written.
229 | public void SendTo(Stream stream)
230 | {
231 | //
232 | if (stream == null)
233 | throw new ArgumentNullException("stream");
234 | if (!stream.CanWrite)
235 | throw
236 | new ArgumentException("Stream CanWrite == false, can't send message to it", "stream");
237 | //
238 | Contract.EndContractBlock();
239 |
240 | Encoding encoder = ASCIIEncoding.UTF8;
241 | StringBuilder outputString = new StringBuilder();
242 |
243 | AdjustContentLength();
244 |
245 | // output header
246 | outputString.Append(Command);
247 | outputString.Append("\r\n");
248 | foreach (KeyValuePair item in _headers)
249 | {
250 | outputString.AppendFormat("{0}: {1}\r\n", item.Key, item.Value);
251 | }
252 | outputString.Append("\r\n");
253 | byte[] buffer = encoder.GetBytes(outputString.ToString());
254 | lock(stream) {
255 | stream.Write(buffer, 0, buffer.Length);
256 |
257 | // Output data
258 | if (Data.Length > 0)
259 | stream.Write(Data, 0, Data.Length);
260 |
261 | }
262 | stream.Flush();
263 | }
264 |
265 |
266 |
267 | ///
268 | /// Logs the message.
269 | ///
270 | /// A log level.
271 | public override void LogMessage(LogType aLevel)
272 | {
273 | // if the level is not logged directly return
274 | if (!_logger.IsLogTypeAllowed(aLevel))
275 | return;
276 |
277 | _logger.Log(aLevel, "Commande : {0}", Command);
278 | foreach (KeyValuePair item in _headers)
279 | {
280 | _logger.LogFormat(aLevel, "Header : {0}: {1}", item.Key, item.Value);
281 | }
282 |
283 | if (Data.Length > 0)
284 | {
285 | _logger.Log(aLevel, "Data :-{0}-", ASCIIEncoding.ASCII.GetString(Data));
286 | }
287 | }
288 |
289 | ///
290 | /// Crée un nouvel objet qui est une copie de l'instance en cours.
291 | ///
292 | ///
293 | /// Nouvel objet qui est une copie de cette instance.
294 | ///
295 | public override object Clone()
296 | {
297 | RtspMessage returnValue = GetRtspMessage(this.Command);
298 |
299 | foreach (var item in this.Headers)
300 | {
301 | if (item.Value == null)
302 | returnValue.Headers.Add(item.Key.Clone() as string, null);
303 | else
304 | returnValue.Headers.Add(item.Key.Clone() as string, item.Value.Clone() as string);
305 | }
306 | returnValue.Data = this.Data.Clone() as byte[];
307 | returnValue.SourcePort = this.SourcePort;
308 |
309 | return returnValue;
310 | }
311 |
312 | }
313 | }
314 |
--------------------------------------------------------------------------------
/VideoStreamingServerPackages/Runtime/Messages/RTSPTransport.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Diagnostics.Contracts;
6 | using System.Globalization;
7 |
8 | namespace Unity.Ig.VideoStreaming.Server.Messages
9 | {
10 | public class RtspTransport
11 | {
12 | public RtspTransport()
13 | {
14 | // Default value is true in RFC
15 | IsMulticast = true;
16 | LowerTransport = LowerTransportType.UDP;
17 | Mode = "PLAY";
18 | }
19 | /*
20 | RFC
21 | Transport = "Transport" ":"
22 | 1\#transport-spec
23 | transport-spec = transport-protocol/profile[/lower-transport]
24 | *parameter
25 | transport-protocol = "RTP"
26 | profile = "AVP"
27 | lower-transport = "TCP" | "UDP"
28 | parameter = ( "unicast" | "multicast" )
29 | | ";" "destination" [ "=" address ]
30 | | ";" "interleaved" "=" channel [ "-" channel ]
31 | | ";" "append"
32 | | ";" "ttl" "=" ttl
33 | | ";" "layers" "=" 1*DIGIT
34 | | ";" "port" "=" port [ "-" port ]
35 | | ";" "client_port" "=" port [ "-" port ]
36 | | ";" "server_port" "=" port [ "-" port ]
37 | | ";" "ssrc" "=" ssrc
38 | | ";" "mode" = <"> 1\#mode <">
39 | ttl = 1*3(DIGIT)
40 | port = 1*5(DIGIT)
41 | ssrc = 8*8(HEX)
42 | channel = 1*3(DIGIT)
43 | address = host
44 | mode = <"> *Method <"> | Method
45 |
46 | */
47 | ///
48 | /// List of transport
49 | ///
50 | [Serializable]
51 | public enum TransportType
52 | {
53 | ///
54 | /// RTP for now
55 | ///
56 | RTP,
57 | }
58 |
59 | ///
60 | /// Profile type
61 | ///
62 | [Serializable]
63 | public enum ProfileType
64 | {
65 | ///
66 | /// RTP/AVP of now
67 | ///
68 | AVP,
69 | }
70 |
71 | ///
72 | /// Transport type.
73 | ///
74 | [Serializable]
75 | public enum LowerTransportType
76 | {
77 | ///
78 | /// UDP transport.
79 | ///
80 | UDP,
81 | ///
82 | /// TCP transport.
83 | ///
84 | TCP,
85 | }
86 |
87 |
88 | ///
89 | /// Gets or sets the transport.
90 | ///
91 | /// The transport.
92 | public TransportType Transport { get; set; }
93 | ///
94 | /// Gets or sets the profile.
95 | ///
96 | /// The profile.
97 | public ProfileType Profile { get; set; }
98 | ///
99 | /// Gets or sets the lower transport.
100 | ///
101 | /// The lower transport.
102 | public LowerTransportType LowerTransport { get; set; }
103 | ///
104 | /// Gets or sets a value indicating whether this instance is multicast.
105 | ///
106 | ///
107 | /// true if this instance is multicast; otherwise, false.
108 | ///
109 | public bool IsMulticast { get; set; }
110 | ///
111 | /// Gets or sets the destination.
112 | ///
113 | /// The destination.
114 | public string Destination { get; set; }
115 | ///
116 | /// Gets or sets the source.
117 | ///
118 | /// The source.
119 | public string Source { get; set; }
120 | ///
121 | /// Gets or sets the interleaved.
122 | ///
123 | /// The interleaved.
124 | public PortCouple Interleaved { get; set; }
125 | ///
126 | /// Gets or sets a value indicating whether this instance is append.
127 | ///
128 | /// true if this instance is append; otherwise, false.
129 | public bool IsAppend { get; set; }
130 | ///
131 | /// Gets or sets the TTL.
132 | ///
133 | /// The TTL.
134 | public int TTL { get; set; }
135 | ///
136 | /// Gets or sets the layers.
137 | ///
138 | /// The layers.
139 | public int Layers { get; set; }
140 | ///
141 | /// Gets or sets the port.
142 | ///
143 | /// The port.
144 | public PortCouple Port { get; set; }
145 | ///
146 | /// Gets or sets the client port.
147 | ///
148 | /// The client port.
149 | public PortCouple ClientPort { get; set; }
150 | ///
151 | /// Gets or sets the server port.
152 | ///
153 | /// The server port.
154 | public PortCouple ServerPort { get; set; }
155 | ///
156 | /// Gets or sets the S SRC.
157 | ///
158 | /// The S SRC.
159 | public string SSrc { get; set; }
160 | ///
161 | /// Gets or sets the mode.
162 | ///
163 | /// The mode.
164 | public string Mode { get; set; }
165 |
166 | ///
167 | /// Parses the specified transport string.
168 | ///
169 | /// A transport string.
170 | /// The transport class.
171 | /// is null.
172 | public static RtspTransport Parse(string aTransportString)
173 | {
174 | if (aTransportString == null)
175 | throw new ArgumentNullException("aTransportString");
176 | Contract.EndContractBlock();
177 |
178 | RtspTransport returnValue = new RtspTransport();
179 |
180 | string[] transportPart = aTransportString.Split(';');
181 | string[] transportProtocolPart = transportPart[0].Split('/');
182 |
183 | ReadTransport(returnValue, transportProtocolPart);
184 | ReadProfile(returnValue, transportProtocolPart);
185 | ReadLowerTransport(returnValue, transportProtocolPart);
186 |
187 | foreach (string part in transportPart)
188 | {
189 | string[] subPart = part.Split('=');
190 |
191 | switch (subPart[0].ToUpperInvariant())
192 | {
193 | case "UNICAST":
194 | returnValue.IsMulticast = false;
195 | break;
196 | case "MULTICAST":
197 | returnValue.IsMulticast = true;
198 | break;
199 | case "DESTINATION":
200 | if (subPart.Length == 2)
201 | returnValue.Destination = subPart[1];
202 | break;
203 | case "SOURCE":
204 | if (subPart.Length == 2)
205 | returnValue.Source = subPart[1];
206 | break;
207 | case "INTERLEAVED":
208 | returnValue.IsMulticast = false;
209 | if (subPart.Length < 2)
210 | throw new ArgumentException("interleaved value invalid", "aTransportString");
211 |
212 | returnValue.Interleaved = PortCouple.Parse(subPart[1]);
213 | break;
214 | case "APPEND":
215 | returnValue.IsAppend = true;
216 | break;
217 | case "TTL":
218 | int ttl = 0;
219 | if (subPart.Length < 2 || !int.TryParse(subPart[1], out ttl))
220 | throw new ArgumentException("TTL value invalid", "aTransportString");
221 | returnValue.TTL = ttl;
222 | break;
223 | case "LAYERS":
224 | int layers = 0;
225 | if (subPart.Length < 2 || !int.TryParse(subPart[1], out layers))
226 | throw new ArgumentException("Layers value invalid", "aTransportString");
227 | returnValue.TTL = layers;
228 | break;
229 | case "PORT":
230 | if (subPart.Length < 2)
231 | throw new ArgumentException("Port value invalid", "aTransportString");
232 | returnValue.Port = PortCouple.Parse(subPart[1]);
233 | break;
234 | case "CLIENT_PORT":
235 | if (subPart.Length < 2)
236 | throw new ArgumentException("client_port value invalid", "aTransportString");
237 | returnValue.ClientPort = PortCouple.Parse(subPart[1]);
238 | break;
239 | case "SERVER_PORT":
240 | if (subPart.Length < 2)
241 | throw new ArgumentException("server_port value invalid", "aTransportString");
242 | returnValue.ServerPort = PortCouple.Parse(subPart[1]);
243 | break;
244 | case "SSRC":
245 | if (subPart.Length < 2)
246 | throw new ArgumentException("ssrc value invalid", "aTransportString");
247 | returnValue.SSrc = subPart[1];
248 | break;
249 | case "MODE":
250 | if (subPart.Length < 2)
251 | throw new ArgumentException("mode value invalid", "aTransportString");
252 | returnValue.Mode = subPart[1];
253 | break;
254 | default:
255 | // TODO log invalid part
256 | break;
257 | }
258 | }
259 | return returnValue;
260 | }
261 |
262 | private static void ReadLowerTransport(RtspTransport returnValue, string[] transportProtocolPart)
263 | {
264 | if (transportProtocolPart.Length == 3)
265 | {
266 | LowerTransportType lowerTransport;
267 | if (!Enum.TryParse(transportProtocolPart[2], out lowerTransport))
268 | throw new ArgumentException("Lower transport type invalid", "aTransportString");
269 | returnValue.LowerTransport = lowerTransport;
270 | }
271 | }
272 |
273 | private static void ReadProfile(RtspTransport returnValue, string[] transportProtocolPart)
274 | {
275 | ProfileType profile;
276 | if (transportProtocolPart.Length < 2 || !Enum.TryParse(transportProtocolPart[1], out profile))
277 | throw new ArgumentException("Transport profile type invalid", "aTransportString");
278 | returnValue.Profile = profile;
279 | }
280 |
281 | private static void ReadTransport(RtspTransport returnValue, string[] transportProtocolPart)
282 | {
283 | TransportType transport;
284 | if (!Enum.TryParse(transportProtocolPart[0], out transport))
285 | throw new ArgumentException("Transport type invalid", "aTransportString");
286 | returnValue.Transport = transport;
287 | }
288 |
289 | ///
290 | /// Returns a that represents this instance.
291 | ///
292 | ///
293 | /// A that represents this instance.
294 | ///
295 | public override string ToString()
296 | {
297 | StringBuilder transportString = new StringBuilder();
298 | transportString.Append(Transport.ToString());
299 | transportString.Append('/');
300 | transportString.Append(Profile.ToString());
301 | transportString.Append('/');
302 | transportString.Append(LowerTransport.ToString());
303 | if (LowerTransport == LowerTransportType.TCP)
304 | {
305 | transportString.Append(";unicast");
306 | }
307 | if (LowerTransport == LowerTransportType.UDP)
308 | {
309 | transportString.Append(';');
310 | transportString.Append(IsMulticast ? "multicast" : "unicast");
311 | }
312 | if (Destination != null)
313 | {
314 | transportString.Append(";destination=");
315 | transportString.Append(Destination);
316 | }
317 | if (Source != null)
318 | {
319 | transportString.Append(";source=");
320 | transportString.Append(Source);
321 | }
322 | if (Interleaved != null)
323 | {
324 | transportString.Append(";interleaved=");
325 | transportString.Append(Interleaved.ToString());
326 | }
327 | if (IsAppend)
328 | {
329 | transportString.Append(";append");
330 | }
331 | if (TTL > 0)
332 | {
333 | transportString.Append(";ttl=");
334 | transportString.Append(TTL);
335 | }
336 | if (Layers > 0)
337 | {
338 | transportString.Append(";layers=");
339 | transportString.Append(Layers);
340 | }
341 | if (Port != null)
342 | {
343 | transportString.Append(";port=");
344 | transportString.Append(Port.ToString());
345 | }
346 | if (ClientPort != null)
347 | {
348 | transportString.Append(";client_port=");
349 | transportString.Append(ClientPort.ToString());
350 | }
351 | if (ServerPort != null)
352 | {
353 | transportString.Append(";server_port=");
354 | transportString.Append(ServerPort.ToString());
355 | }
356 | if (SSrc != null)
357 | {
358 | transportString.Append(";ssrc=");
359 | transportString.Append(SSrc);
360 | }
361 | if (Mode != null && Mode != "PLAY")
362 | {
363 | transportString.Append(";mode=");
364 | transportString.Append(Mode);
365 | }
366 | return transportString.ToString();
367 | }
368 |
369 | }
370 | }
371 |
--------------------------------------------------------------------------------