Skip to content

Commit 84377c1

Browse files
authored
Add example RtspToWebRTCAudioAndVideo (sipsorcery-org#922)
1 parent 777303d commit 84377c1

12 files changed

+862
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
bin/
2+
obj/
3+
**/.vs/
4+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace RtspToWebRtcRestreamer
8+
{
9+
internal class DemuxerConfig
10+
{
11+
private Dictionary<StreamsEnum, string> commandTemplateDic = new Dictionary<StreamsEnum, string>()
12+
{
13+
{StreamsEnum.video, "-re -i {0} -an -vcodec {1} -ssrc {2} -f rtp rtp://{3}:{4} -sdp_file {5}" },
14+
{StreamsEnum.audio, "-re -i {0} -vn -acodec {1} -ssrc {2} -f rtp rtp://{3}:{4} -sdp_file {5}" },
15+
{StreamsEnum.videoAndAudio, "-use_wallclock_as_timestamps 1 -i {0} -map 0:v -c:v {1} -ssrc {2} -f rtp rtp://{3}:{4} -map 0:a -c:a {5} -ssrc {6} -f rtp rtp://{3}:{7} -sdp_file {8} -y"},
16+
{StreamsEnum.videoAndAudioUdp, "-use_wallclock_as_timestamps 1 -rtsp_transport udp -i {0} -map 0:v -c:v {1} -ssrc {2} -f rtp rtp://{3}:{4} -map 0:a -c:a {5} -ssrc {6} -f rtp rtp://{3}:{7} -sdp_file {8} -y" }
17+
};
18+
// exe path
19+
public string SdpFolder { get; private set; } = "A:\\temp\\sdp";
20+
public string SdpFileName { get; private set; } = "stream.sdp";
21+
public string FfmpegBinaryFolder { get; private set; } = "C:\\Program Files\\ffmpeg\\bin";
22+
23+
24+
25+
// ffmpeg command settings
26+
27+
public string rtspUrl = "rtsp://admin:[email protected]:554/ISAPI/Streaming/Channels/101";
28+
public string vcodec = "h264";
29+
public string acodec = "pcm_alaw";
30+
public int audioPort = 5204;
31+
public int videoPort = 5202;
32+
public uint audioSsrc = 50;
33+
public uint videoSsrc = 60;
34+
public string serverIP = "127.0.0.1";
35+
36+
public StreamsEnum outputStream = StreamsEnum.videoAndAudio;
37+
38+
public string sdpPath
39+
{
40+
get { return Path.Combine(SdpFolder, SdpFileName); }
41+
}
42+
43+
public string Args
44+
{
45+
get
46+
{
47+
switch (outputStream)
48+
{
49+
case StreamsEnum.videoAndAudio:
50+
return String.Format(commandTemplateDic[StreamsEnum.videoAndAudio],
51+
rtspUrl,
52+
vcodec,
53+
videoSsrc,
54+
serverIP,
55+
videoPort,
56+
acodec,
57+
audioSsrc,
58+
audioPort,
59+
sdpPath
60+
);
61+
case StreamsEnum.videoAndAudioUdp:
62+
return String.Format(commandTemplateDic[StreamsEnum.videoAndAudioUdp],
63+
rtspUrl,
64+
vcodec,
65+
videoSsrc,
66+
serverIP,
67+
videoPort,
68+
acodec,
69+
audioSsrc,
70+
audioPort,
71+
sdpPath
72+
);
73+
case StreamsEnum.audio:
74+
return String.Format(commandTemplateDic[StreamsEnum.audio],
75+
rtspUrl,
76+
acodec,
77+
audioSsrc,
78+
serverIP,
79+
audioPort,
80+
sdpPath
81+
);
82+
case StreamsEnum.video:
83+
return String.Format(commandTemplateDic[StreamsEnum.video],
84+
rtspUrl,
85+
vcodec,
86+
videoSsrc,
87+
serverIP,
88+
videoPort,
89+
sdpPath
90+
);
91+
default:
92+
return "";
93+
}
94+
95+
96+
}
97+
}
98+
}
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
using SIPSorcery.Net;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Diagnostics;
5+
using System.Net;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace RtspToWebRtcRestreamer
11+
{
12+
/// <summary>
13+
/// Create FFmpeg process that split rtsp stream into two RTP stream (audio+video)
14+
/// </summary>
15+
internal class FFmpegDemuxer
16+
{
17+
private Process _ffmpegProcess;
18+
private DemuxerConfig _dc;
19+
20+
public DemuxerConfig GetConfig()
21+
{
22+
return _dc;
23+
}
24+
25+
public FFmpegDemuxer(DemuxerConfig config)
26+
{
27+
_dc = config;
28+
}
29+
30+
public void Run()
31+
{
32+
// delete old sdp file if exist
33+
if (File.Exists(_dc.sdpPath))
34+
{
35+
File.Delete(_dc.sdpPath);
36+
}
37+
// configure and run ffmpeg process
38+
39+
SetupAndRunProcess(ref _ffmpegProcess, _dc.Args);
40+
41+
// Verification
42+
// wait until sdp file created and satisfy condition
43+
// in my case sdp file need have two track audio and video
44+
// if we do not check desire condition it may lead to errors due to not fully writed sdp file
45+
var ready = false;
46+
while (!ready)
47+
{
48+
if(IsOk(_dc.sdpPath) == true)
49+
{
50+
ready = true;
51+
break;
52+
}
53+
Task.Delay(77);
54+
}
55+
}
56+
57+
private bool IsOk(string sdpFilePath)
58+
{
59+
try
60+
{
61+
if (File.Exists(sdpFilePath))
62+
{
63+
var sdp = SDP.ParseSDPDescription(File.ReadAllText(sdpFilePath));
64+
switch (_dc.outputStream)
65+
{
66+
case StreamsEnum.videoAndAudio:
67+
{
68+
var videoAnn = sdp.Media.First(x => x.Media == SDPMediaTypesEnum.video);
69+
var audioAnn = sdp.Media.First(x => x.Media == SDPMediaTypesEnum.audio);
70+
if (videoAnn != null && audioAnn != null)
71+
return true;
72+
return false;
73+
}
74+
case StreamsEnum.video:
75+
{
76+
var videoAnn = sdp.Media.First(x => x.Media == SDPMediaTypesEnum.video);
77+
if (videoAnn != null)
78+
return true;
79+
return false;
80+
}
81+
case StreamsEnum.audio:
82+
{
83+
var audioAnn = sdp.Media.First(x => x.Media == SDPMediaTypesEnum.audio);
84+
if (audioAnn != null)
85+
return true;
86+
return false;
87+
}
88+
default:
89+
return false;
90+
}
91+
}
92+
else
93+
return false;
94+
}
95+
96+
catch(Exception ex) { return false; }
97+
}
98+
99+
void SetupAndRunProcess(ref Process proc, string arguments)
100+
{
101+
proc = new Process();
102+
proc.StartInfo.FileName = Path.Combine(_dc.FfmpegBinaryFolder, "ffmpeg");
103+
proc.StartInfo.CreateNoWindow = true;
104+
proc.StartInfo.UseShellExecute = false;
105+
proc.StartInfo.RedirectStandardError = true;
106+
proc.StartInfo.RedirectStandardInput = true;
107+
proc.StartInfo.RedirectStandardOutput = true;
108+
proc.OutputDataReceived += FFMpegOutputLog;
109+
proc.ErrorDataReceived += FFMpegOutputError;
110+
proc.StartInfo.Arguments = arguments;
111+
proc.StartInfo.WorkingDirectory = _dc.FfmpegBinaryFolder;
112+
113+
if (!Directory.Exists(_dc.SdpFolder)) Directory.CreateDirectory(_dc.SdpFolder);
114+
proc.Start();
115+
proc.BeginErrorReadLine();
116+
proc.BeginOutputReadLine();
117+
}
118+
119+
private void FFMpegOutputError(object sender, DataReceivedEventArgs e)
120+
{
121+
Console.WriteLine(e.Data);
122+
}
123+
124+
private void FFMpegOutputLog(object sender, DataReceivedEventArgs e)
125+
{
126+
Console.WriteLine(e.Data);
127+
}
128+
}
129+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
using Org.BouncyCastle.Security;
2+
using SIPSorcery.Net;
3+
using SIPSorcery.SIP;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Net;
8+
using System.Runtime.Intrinsics.Arm;
9+
using System.Text;
10+
using System.Threading.Tasks;
11+
12+
namespace RtspToWebRtcRestreamer
13+
{
14+
/// <summary>
15+
/// Listne RTP streams created by ffmpeg process
16+
/// </summary>
17+
internal class FFmpegListener
18+
{
19+
private Thread _videoThread;
20+
private Thread _audioThread;
21+
private RTPSession _videoRTP;
22+
private RTPSession _audioRTP;
23+
private DemuxerConfig _dc;
24+
public bool ready = false;
25+
public MediaStreamTrack? videoTrack { get; private set; }
26+
public MediaStreamTrack? audioTrack { get; private set; }
27+
public SDPAudioVideoMediaFormat videoFormatRTP { get; private set; }
28+
public SDPAudioVideoMediaFormat audioFormatRTP { get; private set; }
29+
30+
31+
public event Action<IPEndPoint, SDPMediaTypesEnum, RTPPacket> OnAudioRtpPacketReceived;
32+
public event Action<IPEndPoint, SDPMediaTypesEnum, RTPPacket> OnVideoRtpPacketReceived;
33+
34+
public FFmpegListener( DemuxerConfig demuxConfig)
35+
{
36+
_dc = demuxConfig;
37+
}
38+
39+
public async void Run(CancellationToken token)
40+
{
41+
switch (_dc.outputStream)
42+
{
43+
case StreamsEnum.videoAndAudio:
44+
case StreamsEnum.none:
45+
{
46+
ListenAudio();
47+
ListenVideo();
48+
break;
49+
}
50+
case StreamsEnum.audio:
51+
{
52+
ListenAudio();
53+
break;
54+
}
55+
case StreamsEnum.video:
56+
{
57+
ListenVideo();
58+
break;
59+
}
60+
}
61+
ready = true;
62+
}
63+
64+
private void ListenAudio()
65+
{
66+
var sdpAudio = SDP.ParseSDPDescription(File.ReadAllText(_dc.sdpPath));
67+
var videoAnn = sdpAudio.Media.Find(x => x.Media == SDPMediaTypesEnum.video);
68+
var audioAnn = sdpAudio.Media.Find(x => x.Media == SDPMediaTypesEnum.audio);
69+
sdpAudio.Media.Remove(videoAnn);
70+
// configure audio listener
71+
audioFormatRTP = audioAnn.MediaFormats.Values.First();
72+
audioTrack = new MediaStreamTrack(
73+
SDPMediaTypesEnum.audio,
74+
false,
75+
new List<SDPAudioVideoMediaFormat> { audioFormatRTP },
76+
MediaStreamStatusEnum.SendRecv);
77+
audioTrack.Ssrc = _dc.audioSsrc;
78+
_audioRTP = new RTPSession(false, false, false, IPAddress.Loopback, _dc.audioPort);
79+
_audioRTP.AcceptRtpFromAny = true;
80+
_audioRTP.SetRemoteDescription(SIPSorcery.SIP.App.SdpType.answer, sdpAudio);
81+
_audioRTP.addTrack(audioTrack);
82+
83+
_audioRTP.OnRtpPacketReceived += HndlAudioPacketReceived;
84+
85+
_audioThread = new Thread(() => _audioRTP.Start());
86+
_audioThread.Start();
87+
}
88+
89+
private void ListenVideo()
90+
{
91+
// create sdpVideo
92+
var sdpVideo = SDP.ParseSDPDescription(File.ReadAllText(_dc.sdpPath));
93+
var videoAnn = sdpVideo.Media.Find(x => x.Media == SDPMediaTypesEnum.video);
94+
// !- its necessary to delete audio announcment from whole sdp file otherwise RTP session will not catch frames
95+
var audioAnn = sdpVideo.Media.Find(x => x.Media == SDPMediaTypesEnum.audio);
96+
sdpVideo.Media.Remove(audioAnn);
97+
98+
// configure video listener
99+
videoFormatRTP = videoAnn.MediaFormats.Values.First();
100+
videoTrack = new MediaStreamTrack(
101+
SDPMediaTypesEnum.video,
102+
false,
103+
new List<SDPAudioVideoMediaFormat> { videoFormatRTP },
104+
MediaStreamStatusEnum.RecvOnly);
105+
videoTrack.Ssrc = _dc.videoSsrc;
106+
_videoRTP = new RTPSession(false, false, false, IPAddress.Loopback, _dc.videoPort);
107+
_videoRTP.AcceptRtpFromAny = true;
108+
_videoRTP.SetRemoteDescription(SIPSorcery.SIP.App.SdpType.answer, sdpVideo);
109+
_videoRTP.addTrack(videoTrack);
110+
111+
_videoRTP.OnRtpPacketReceived += HndlVideoPacketReceived;
112+
_videoThread = new Thread(() => _videoRTP.Start());
113+
_videoThread.Start();
114+
}
115+
116+
private void HndlVideoPacketReceived(IPEndPoint arg1, SDPMediaTypesEnum arg2, RTPPacket arg3)
117+
{
118+
if (OnVideoRtpPacketReceived == null) return;
119+
OnVideoRtpPacketReceived.Invoke(arg1, arg2, arg3);
120+
}
121+
private void HndlAudioPacketReceived(IPEndPoint arg1, SDPMediaTypesEnum arg2, RTPPacket arg3)
122+
{
123+
if (OnAudioRtpPacketReceived == null) return;
124+
OnAudioRtpPacketReceived.Invoke(arg1, arg2, arg3);
125+
}
126+
}
127+
}
128+

0 commit comments

Comments
 (0)