protected override Task Ready() { var ready = base.Ready(); var args = new List <string> { "-y", Options.InputArgs }; if (AudioSource != null) { var config = AudioSource.Config; args.Add($"-map 0:a:0"); if (Options.AudioMode == FFCaptureMode.LSEncode) { var source = AudioSource as PcmNamedPipeAudioSource; args.AddRange(new[] { $"-f s16le", $"-ar {config.ClockRate}", $"-ac {config.ChannelCount}", NamedPipe.GetOSPipeName(source.PipeName) }); } else { var source = AudioSource as RtpAudioSource; args.Add($"-f rtp"); if (Options.AudioMode == FFCaptureMode.NoEncode) { args.Add($"-c copy"); } else { if (RtpAudioFormat.IsOpus) { args.AddRange(new[] { $"-ar {config.ClockRate}", $"-ac {config.ChannelCount}", $"-c libopus", $"-b:a {Options.AudioBitrate}k", }); } else if (RtpAudioFormat.IsG722) { args.AddRange(new[] { $"-ar 16000", $"-ac 1", $"-c g722", }); } else if (RtpAudioFormat.IsPcmu) { args.AddRange(new[] { $"-ar 8000", $"-ac 1", $"-c pcm_mulaw", }); } else if (RtpAudioFormat.IsPcma) { args.AddRange(new[] { $"-ar 8000", $"-ac 1", $"-c pcm_alaw", }); } else { throw new InvalidOperationException($"Unexpected audio format '{RtpAudioFormat.Name}'."); } } if (RtpAudioFormat.IsOpus) { args.Add($"rtp://127.0.0.1:{source.Port}"); } else if (RtpAudioFormat.IsG722) { args.Add($"rtp://127.0.0.1:{source.Port}?pkt_size={G722PacketSize}"); } else if (RtpAudioFormat.IsPcmu) { args.Add($"rtp://127.0.0.1:{source.Port}?pkt_size={PcmuPacketSize}"); } else if (RtpAudioFormat.IsPcma) { args.Add($"rtp://127.0.0.1:{source.Port}?pkt_size={PcmaPacketSize}"); } else { throw new InvalidOperationException($"Unexpected audio format '{RtpAudioFormat.Name}'."); } } } var processParameterSets = false; if (VideoSource != null) { args.Add($"-map 0:v:0"); if (Options.VideoMode == FFCaptureMode.LSEncode) { var source = VideoSource as Yuv4MpegNamedPipeVideoSource; args.AddRange(new[] { $"-f yuv4mpegpipe", $"-pix_fmt yuv420p", NamedPipe.GetOSPipeName(source.PipeName) }); } else { var source = VideoSource as RtpVideoSource; args.Add($"-f rtp"); if (Options.VideoMode == FFCaptureMode.NoEncode) { args.Add($"-c copy"); if (RtpVideoFormat.IsH264) { processParameterSets = true; source.NeedsParameterSets = true; H264SdpFileName = $"h264_{Utility.GenerateId()}.sdp"; args.AddRange(new[] { $"-sdp_file {H264SdpFileName}", }); } } else { if (RtpVideoFormat.IsVp8) { args.AddRange(new[] { $"-c libvpx -auto-alt-ref 0", $"-pix_fmt yuv420p", $"-quality realtime", $"-speed 16", $"-crf 10", $"-b:v {Options.VideoBitrate}k", $"-g {Options.KeyFrameInterval}", }); } else if (RtpVideoFormat.IsVp9) { args.AddRange(new[] { $"-c libvpx-vp9 -strict experimental", $"-level 0", $"-pix_fmt yuv420p", $"-lag-in-frames 0", $"-deadline realtime", $"-quality realtime", $"-speed 16", $"-b:v {Options.VideoBitrate}k -maxrate {Options.VideoBitrate}k", $"-g {Options.KeyFrameInterval}", }); } else if (RtpVideoFormat.IsH264) { args.AddRange(new[] { $"-c libx264", $"-profile:v baseline", $"-level:v 1.3", $"-pix_fmt yuv420p", $"-tune zerolatency", $"-b:v {Options.VideoBitrate}k", $"-g {Options.KeyFrameInterval} -keyint_min {Options.KeyFrameInterval}", }); } else { throw new InvalidOperationException($"Unexpected video format '{RtpVideoFormat.Name}'."); } } if (RtpVideoFormat.IsVp8) { args.Add($"rtp://127.0.0.1:{source.Port}?pkt_size={Vp8PacketSize}"); } else if (RtpVideoFormat.IsVp9) { args.Add($"rtp://127.0.0.1:{source.Port}?pkt_size={Vp9PacketSize}"); } else if (RtpVideoFormat.IsH264) { args.Add($"rtp://127.0.0.1:{source.Port}?pkt_size={H264PacketSize}"); } else { throw new InvalidOperationException($"Unexpected video format '{RtpVideoFormat.Name}'."); } } } FFmpeg = FFUtility.FFmpeg(string.Join(" ", args)); var monitor = new Thread(() => { while (!_Done) { FFmpeg.WaitForExit(); if (!_Done) { Console.Error.WriteLine("FFmpeg exited unexpectedly."); FFmpeg = FFUtility.FFmpeg(string.Join(" ", args)); } } }) { IsBackground = true }; monitor.Start(); if (processParameterSets) { ProcessParameterSets(); } return(ready); }
protected override async Task Ready() { await base.Ready(); var args = new List <string> { "-y" }; if (AudioSink != null) { var config = AudioSink.Config; if (Options.AudioMode == FFRenderMode.LSDecode) { var sink = AudioSink as PcmNamedPipeAudioSink; args.AddRange(new[] { $"-guess_layout_max 0", $"-f s16le", $"-ar {config.ClockRate}", $"-ac {config.ChannelCount}", $"-i {NamedPipe.GetOSPipeName(sink.PipeName)}", }); } else { var sink = AudioSink as RtpAudioSink; sink.IPAddress = "127.0.0.1"; sink.Port = LockedRandomizer.Next(49162, 65536); sink.PayloadType = 96; var sdpMediaDescription = new Sdp.MediaDescription(new Sdp.Media(Sdp.MediaType.Audio, sink.Port, Sdp.Rtp.Media.RtpAvpTransportProtocol, sink.PayloadType.ToString())); sdpMediaDescription.AddMediaAttribute(new Sdp.SendReceiveAttribute()); if (RtpAudioFormat.IsOpus) { sdpMediaDescription.AddMediaAttribute(new Sdp.Rtp.MapAttribute(sink.PayloadType, AudioFormat.OpusName, Opus.Format.DefaultClockRate, Opus.Format.DefaultChannelCount.ToString())); sdpMediaDescription.AddMediaAttribute(new Sdp.FormatParametersAttribute(sink.PayloadType, "useinbandfec=1")); } else if (RtpAudioFormat.IsG722) { sdpMediaDescription.AddMediaAttribute(new Sdp.Rtp.MapAttribute(sink.PayloadType, AudioFormat.G722Name, G722.Format.DefaultClockRate, G722.Format.DefaultChannelCount.ToString())); } else if (RtpAudioFormat.IsPcmu) { sdpMediaDescription.AddMediaAttribute(new Sdp.Rtp.MapAttribute(sink.PayloadType, AudioFormat.PcmuName, G711.Format.DefaultClockRate, G711.Format.DefaultChannelCount.ToString())); } else if (RtpAudioFormat.IsPcma) { sdpMediaDescription.AddMediaAttribute(new Sdp.Rtp.MapAttribute(sink.PayloadType, AudioFormat.PcmaName, G711.Format.DefaultClockRate, G711.Format.DefaultChannelCount.ToString())); } else { throw new InvalidOperationException($"Unexpected audio format '{RtpAudioFormat.Name}'."); } if (Options.AudioBitrate.HasValue && !RtpAudioFormat.IsFixedBitrate) { sdpMediaDescription.AddBandwidth(new Sdp.Bandwidth(Sdp.BandwidthType.ApplicationSpecific, Options.AudioBitrate.Value)); } var sdpMessage = new Sdp.Message(new Sdp.Origin("127.0.0.1"), "liveswitch-audio") { ConnectionData = new Sdp.ConnectionData("127.0.0.1") }; if (sdpMediaDescription != null) { sdpMessage.AddMediaDescription(sdpMediaDescription); } var sdp = sdpMessage.ToString(); AudioSdpFileName = $"audio_{Utility.GenerateId()}.sdp"; File.WriteAllText(AudioSdpFileName, sdp); Console.Error.WriteLine($"Audio SDP:{Environment.NewLine}{sdp}"); args.AddRange(new[] { $"-protocol_whitelist file,crypto,udp,rtp", $"-i {AudioSdpFileName}" }); } } if (VideoSink != null) { if (Options.VideoMode == FFRenderMode.LSDecode) { var sink = VideoSink as Yuv4MpegNamedPipeVideoSink; args.AddRange(new[] { $"-f yuv4mpegpipe", $"-i {NamedPipe.GetOSPipeName(sink.PipeName)}", }); } else { var sink = VideoSink as RtpVideoSink; sink.IPAddress = "127.0.0.1"; sink.Port = LockedRandomizer.Next(49162, 65536); sink.PayloadType = 97; var sdpMediaDescription = new Sdp.MediaDescription(new Sdp.Media(Sdp.MediaType.Video, sink.Port, Sdp.Rtp.Media.RtpAvpTransportProtocol, sink.PayloadType.ToString())); sdpMediaDescription.AddMediaAttribute(new Sdp.SendReceiveAttribute()); if (Options.VideoFrameRate.HasValue) { sdpMediaDescription.AddMediaAttribute(new Sdp.FrameRateAttribute(Options.VideoFrameRate.ToString())); } if (RtpVideoFormat.IsVp8) { sdpMediaDescription.AddMediaAttribute(new Sdp.Rtp.MapAttribute(sink.PayloadType, VideoFormat.Vp8Name, VideoFormat.DefaultClockRate)); } else if (RtpVideoFormat.IsVp9) { sdpMediaDescription.AddMediaAttribute(new Sdp.Rtp.MapAttribute(sink.PayloadType, VideoFormat.Vp9Name, VideoFormat.DefaultClockRate)); } else if (RtpVideoFormat.IsH264) { sdpMediaDescription.AddMediaAttribute(new Sdp.Rtp.MapAttribute(sink.PayloadType, VideoFormat.H264Name, VideoFormat.DefaultClockRate)); sdpMediaDescription.AddMediaAttribute(new Sdp.FormatParametersAttribute(sink.PayloadType, "profile-level-id=42001f;level-asymmetry-allowed=1;packetization-mode=1")); } else { throw new InvalidOperationException($"Unexpected video format '{RtpVideoFormat.Name}'."); } if (Options.VideoBitrate.HasValue && !RtpVideoFormat.IsFixedBitrate) { sdpMediaDescription.AddBandwidth(new Sdp.Bandwidth(Sdp.BandwidthType.ApplicationSpecific, Options.VideoBitrate.Value)); } var sdpMessage = new Sdp.Message(new Sdp.Origin("127.0.0.1"), "liveswitch-video") { ConnectionData = new Sdp.ConnectionData("127.0.0.1") }; if (sdpMediaDescription != null) { sdpMessage.AddMediaDescription(sdpMediaDescription); } var sdp = sdpMessage.ToString(); VideoSdpFileName = $"video_{Utility.GenerateId()}.sdp"; File.WriteAllText(VideoSdpFileName, sdp); Console.Error.WriteLine($"Video SDP:{Environment.NewLine}{sdp}"); if (Options.VideoFrameRate.HasValue) { args.Add($"-r {Options.VideoFrameRate}"); } args.AddRange(new[] { $"-protocol_whitelist file,crypto,udp,rtp", $"-i {VideoSdpFileName}", }); } } args.Add(Options.OutputArgs); FFmpeg = FFUtility.FFmpeg(string.Join(" ", args), ProcessFFmpegOutput); var monitor = new Thread(() => { while (!_Done) { FFmpeg.WaitForExit(); Deactivate(); if (!_Done) { Console.Error.WriteLine("FFmpeg exited unexpectedly."); FFmpeg = FFUtility.FFmpeg(string.Join(" ", args), ProcessFFmpegOutput); } } }) { IsBackground = true }; monitor.Start(); }