コード例 #1
0
 /// <summary>
 /// 创建Transcode
 /// </summary>
 /// <param name="deviceId"></param>
 /// <param name="vhostDomain"></param>
 /// <param name="transcode"></param>
 /// <param name="rs"></param>
 /// <returns></returns>
 public static bool CreateVhostTranscode(string deviceId, string vhostDomain, Transcode transcode,
                                         out ResponseStruct rs)
 {
     rs = new ResponseStruct()
     {
         Code    = ErrorNumber.None,
         Message = ErrorMessage.ErrorDic ![ErrorNumber.None],
コード例 #2
0
        private async void Transcode(Movie movie, Transcode transcodeProfile, Server server, string callbackUrl)
        {
            var client = new SshClient(server.Ip, "root", server.RootPassword);

            try
            {
                string successCallbackUrl = callbackUrl.Replace("STATE", StreamState.Ready.ToString());
                var    options            = new Dictionary <string, string>
                {
                    { "hls_time", "4" },
                    { "hls_playlist_type", "vod" }
                };
                string transcoder     = FFMPEGCommand.MakeCommand(transcodeProfile, movie.Source, $"/var/hls/{movie.StreamKey}", options);
                string prepareCommand = $"mkdir /var/hls/{movie.StreamKey}";
                prepareCommand += $" && cd /var/hls/{movie.StreamKey}";
                prepareCommand += $" && mkdir 1080p 720p 480p 360p";

                client.Connect();
                client.RunCommand(prepareCommand);
                var cmd    = client.CreateCommand($"nohup sh -c '{transcoder} && curl -i -X GET {successCallbackUrl}' >/dev/null 2>&1 & echo $!");
                var result = cmd.Execute();
                int pid    = int.Parse(result);
                client.RunCommand($"disown -h {pid}");
                client.Disconnect();
                client.Dispose();
            }
            catch (Exception)
            {
                string failCallbackUrl = callbackUrl.Replace("STATE", StreamState.Error.ToString());
                var    httpClient      = new HttpClient();
                await httpClient.GetAsync(failCallbackUrl);
            }
        }
コード例 #3
0
        public void CanTranscode()
        {
            using (var stream1 = new MemoryStream())
                using (var stream2 = new MemoryStream())
                    using (var writerStream1 = new WriterStream(stream1))
                        using (var writerStream2 = new WriterStream(stream2))
                        {
                            // write into compact binary protocol first
                            var writer = writerStream1.CreateCompactBinaryWriter();
                            writer.Write(this.testObject);
                            stream1.Position = 0;
                            using (var readerStream1 = ReaderStream.FromMemoryStreamBuffer(stream1, null))
                            {
                                var reader = readerStream1.CreateCompactBinaryReader();
                                // re-write as simple protocol
                                var writer2 = writerStream2.CreateSimpleBinaryWriter();
                                Transcode.FromTo(reader, writer2);
                                stream2.Position = 0;

                                using (var readerStream2 = new ReaderStream(stream2))
                                {
                                    var reader2       = readerStream2.CreateSimpleBinaryReader();
                                    var anotherObject = reader2.Read <AnotherDerivedThing>();

                                    Assert.IsTrue(Comparer.Equal(anotherObject, this.testObject));
                                }
                            }
                        }
        }
コード例 #4
0
        public async Task <IActionResult> Edit(Transcode model)
        {
            if (ModelState.IsValid)
            {
                await transcodeRepository.Edit(model);

                return(RedirectToAction(nameof(Manage)));
            }
            return(View());
        }
コード例 #5
0
        public static void TranscodeCBFB(Stream from, Stream to)
        {
            var input  = new InputStream(from, 11);
            var reader = new CompactBinaryReader <InputStream>(input);

            var output = new OutputStream(to, 19);
            var writer = new FastBinaryWriter <OutputStream>(output);

            Transcode.FromTo(reader, writer);
            output.Flush();
        }
コード例 #6
0
        public static void TranscodeCBJson <From>(Stream from, Stream to)
        {
            var input  = new InputStream(from, 11);
            var reader = new CompactBinaryReader <InputStream>(input);

            var writer = new SimpleJsonWriter(to);

            Transcode <From> .FromTo(reader, writer);

            writer.Flush();
        }
コード例 #7
0
 internal Transcoder(MediaSource source)
 {
     TranscodingProcess         = new Transcode(source);
     TranscodingProcess.Exited += TranscodingProcess_Exited;
     Timer           = new System.Timers.Timer(1000);
     Timer.AutoReset = true;
     Timer.Elapsed  += Timer_Elapsed;
     Timer.Start();
     lock (this)
         Status = TranscodingProcess.BeginTranscodeJob() != 0 ? TranscodingStatus.Error : TranscodingStatus.Initializing;
     TranscodingService.NotifyAll(source.Key, Status);
 }
コード例 #8
0
        public static void TranscodeSPSP <From>(Stream from, Stream to)
        {
            var input  = new InputStream(from, 11);
            var reader = new SimpleBinaryReader <InputStream>(input);

            var output = new OutputStream(to, 19);
            var writer = new SimpleBinaryWriter <OutputStream>(output);

            Transcode <From> .FromTo(reader, writer);

            output.Flush();
        }
コード例 #9
0
        public static void TranscodeCBXml <From>(Stream from, Stream to)
        {
            var input  = new InputStream(from, 11);
            var reader = new CompactBinaryReader <InputStream>(input);

            var hasBase = Schema <From> .RuntimeSchema.HasBase;
            var writer  = new SimpleXmlWriter(to, new SimpleXmlWriter.Settings {
                UseNamespaces = hasBase
            });

            Transcode <From> .FromTo(reader, writer);

            writer.Flush();
        }
コード例 #10
0
        public JsonResult SetVhostTranscode(string deviceId, string vhostDomain, string transcodeInstanceName,
                                            Transcode transcode)
        {
            ResponseStruct rss = CommonFunctions.CheckParams(new object[]
                                                             { deviceId, vhostDomain, transcodeInstanceName, transcode });

            if (rss.Code != ErrorNumber.None)
            {
                return(Program.CommonFunctions.DelApisResult(null !, rss));
            }

            var rt = VhostTranscodeApis.SetVhostTranscode(deviceId, vhostDomain, transcodeInstanceName, transcode,
                                                          out ResponseStruct rs);

            return(Program.CommonFunctions.DelApisResult(rt, rs));
        }
コード例 #11
0
        static void Main()
        {
            var src = new Example
            {
                Widgets =
                {
                    new Widget {
                        Name = "Konfabulator", Number = 3.14
                    }
                }
            };

            var output = new OutputBuffer();
            var writer = new CompactBinaryWriter <OutputBuffer>(output);

            Serialize.To(writer, src);

            var input  = new InputBuffer(output.Data);
            var reader = new CompactBinaryReader <InputBuffer>(input);

            var xmlString = new StringBuilder();
            var xmlWriter = new SimpleXmlWriter(XmlWriter.Create(xmlString, new XmlWriterSettings
            {
                OmitXmlDeclaration = true,
                Indent             = true
            }));

            Transcode <Example> .FromTo(reader, xmlWriter);

            xmlWriter.Flush();

            string[] expectedLines =
                @"<Example>
  <Widgets>
    <Item>
      <Widget>
        <Name>Konfabulator</Name>
        <Number>3.14</Number>
      </Widget>
    </Item>
  </Widgets>
</Example>".Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.None);

            string[] actualLines = xmlString.ToString().Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.None);

            ThrowIfFalse(expectedLines.SequenceEqual(actualLines));
        }
コード例 #12
0
        public FormTranscode(Transcode trans)
        {
            InitializeComponent();
            _trans = trans;

            textName.Text         = trans.Name;
            textAudioBitrate.Text = trans.AudioBitrate.ToString();
            textChannels.Text     = trans.AudioChannels.ToString();
            textFps.Text          = trans.VideoFps.ToString(new NumberFormatInfo {
                CurrencyDecimalSeparator = "."
            });
            textHeight.Text              = trans.VideoHeight.ToString();
            textVideoBitrate.Text        = trans.VideoBitrate.ToString();
            textWidth.Text               = trans.VideoWidth.ToString();
            comboAudioCodec.SelectedItem = string.IsNullOrEmpty(trans.AudioCodec) ? "" : trans.AudioCodec;
            comboMux.SelectedItem        = string.IsNullOrEmpty(trans.Incapsulate) ? "ts" : trans.Incapsulate;
            comboRate.SelectedText       = trans.AudioRate > 0 ? trans.AudioRate.ToString() : "";
            comboVideoCodec.SelectedItem = string.IsNullOrEmpty(trans.VideoCodec) ? "" : trans.VideoCodec;
        }
コード例 #13
0
        static void Main()
        {
            var src = new Example
            {
                Widgets =
                {
                    new Widget {
                        Name = "Konfabulator", Number = 3.14
                    }
                }
            };

            var output = new OutputBuffer();
            var writer = new CompactBinaryWriter <OutputBuffer>(output);

            Serialize.To(writer, src);

            var input  = new InputBuffer(output.Data);
            var reader = new CompactBinaryReader <InputBuffer>(input);

            var xmlString = new StringBuilder();
            var xmlWriter = new SimpleXmlWriter(XmlWriter.Create(xmlString, new XmlWriterSettings
            {
                OmitXmlDeclaration = true,
                Indent             = true
            }));

            Transcode <Example> .FromTo(reader, xmlWriter);

            xmlWriter.Flush();

            Debug.Assert(xmlString.ToString() ==
                         @"<Example>
  <Widgets>
    <Item>
      <Widget>
        <Name>Konfabulator</Name>
        <Number>3.14</Number>
      </Widget>
    </Item>
  </Widgets>
</Example>");
        }
コード例 #14
0
ファイル: FormOption.cs プロジェクト: northspb/p2pproxy
        private void btnTransAdd_Click(object sender, EventArgs e)
        {
            var broadcaster = _app.Broadcaster as VlcBroadcaster;

            if (broadcaster == null)
            {
                return;
            }
            Transcode trans = new Transcode("Новый профиль");
            var       form  = new FormTranscode(trans);

            form.Closed += (o, args) =>
            {
                if (form.DialogResult == DialogResult.OK)
                {
                    broadcaster.AddTranscode(trans);
                    broadcaster.SaveTranscodes();
                    comboTranscode.Items.Add(trans.Name);
                    comboTranscode.SelectedItem = trans.Name;
                }
            };
            form.ShowDialog();
        }
コード例 #15
0
        private void StartTranscoding_Click(object sender, EventArgs e)
        {
            if (originalFileData == null || string.IsNullOrEmpty(FilePathAndName.Text) || string.IsNullOrEmpty(SaveFilePathName.Text))
            {
                MessageBox.Show("Nie wybrano pliku wideo lub nie podano ścieżki zapisu (lub jest nie poprawna), albo parametry są nieprawidłowe.", "Błąd", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            var param = new TranscodeParams()
            {
                Path         = SaveFilePathName.Text,
                Format       = ".avi",
                Name         = (string.IsNullOrEmpty(originalFileData?.Name) ? "NoweWideo" : originalFileData.Name.Split('.')[0]),
                AudioCodec   = (AudioCodec)Enum.Parse(typeof(AudioCodec), AudioCodecList.SelectedItem.ToString()),
                VideoCodec   = (VideoCodec)Enum.Parse(typeof(VideoCodec), VideoCodecList.SelectedItem.ToString()),
                FPS          = (int)FPS.Value,
                Width        = (int)VideoWidth.Value,
                Height       = (int)VideoHeight.Value,
                SetSize      = SetSizeRadio.Checked,
                VideoBitrate = (int)VideoBitrate.Value,
                AudioBitrate = (int)AudioBitrate.Value,
                Container    = ContainerList.SelectedItem.ToString(),
                Scale        = (float)Scale.Value,
            };

            var tran = new Transcode(param, originalFileData, vlcPath);

            tran.Execute();

            MessageBox.Show(
                $"Użyte argumenty: {tran.Argumets}\r\n" +
                $"Czas transkodowania: {tran.TargetParams.Time / 1000} sekund\r\n" +
                $"Oryginalny rozmiar: {tran.TargetParams.OriginalSize} kB\r\n" +
                $"Nowy rozmiar: {tran.TargetParams.TranscodedSize} kB\r\n"
                , "Wynik transkodowania", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
コード例 #16
0
        public static string MakeCommand(Transcode transcodeProfile, string input, string output, IDictionary <string, string> options)
        {
            string inputOption = options.ContainsKey("-f") ? $"-f {options["-f"]}" : string.Empty;

            inputOption += options.ContainsKey("-safe") ? $" -safe {options["-safe"]}" : string.Empty;
            StringBuilder command = new StringBuilder($"ffmpeg -y -nostdin -hide_banner {inputOption} -i {input}");

            // 1920x1080
            command.Append(" -vf scale=w=1920:h=1080:force_original_aspect_ratio=decrease");
            command.Append($" -c:a {transcodeProfile.AudioCodec ?? "copy"}");
            command.Append(transcodeProfile.AudioSampleRate_1080 > 0 ? $" -ar {transcodeProfile.AudioSampleRate_720}" : string.Empty);
            command.Append($" -c:v {transcodeProfile.VideoCodec ?? "copy"}");
            command.Append(!transcodeProfile.Preset.Equals("default") ? $" -preset {transcodeProfile.Preset}" : string.Empty);
            command.Append(!transcodeProfile.VideoProfile.Equals("none") ? $" -profile:v  {transcodeProfile.VideoProfile}" : string.Empty);
            command.Append(transcodeProfile.CRF > 0 ? $" -crf {transcodeProfile.CRF}" : string.Empty);
            command.Append(transcodeProfile.VideoBitrate_1080 > 0 ? $" -b:v {transcodeProfile.VideoBitrate_1080}k" : string.Empty);
            command.Append(transcodeProfile.MaxBitrate_1080 > 0 ? $" -maxrate {transcodeProfile.MaxBitrate_1080}k" : string.Empty);
            command.Append(transcodeProfile.BufferSize_1080 > 0 ? $" -bufsize {transcodeProfile.BufferSize_1080}k" : string.Empty);
            command.Append(transcodeProfile.AudioBitrate_1080 > 0 ? $" -b:a {transcodeProfile.AudioBitrate_1080}k" : string.Empty);

            if (options.ContainsKey("custom_output"))
            {
                command.Append($" -f mpegts {options["custom_output_1080p"]}");
            }
            else
            {
                command.Append(options.ContainsKey("hls_time") ? $" -hls_time {options["hls_time"]}" : string.Empty);
                command.Append(options.ContainsKey("hls_playlist_type") ? $" -hls_playlist_type {options["hls_playlist_type"]}" : string.Empty);
                command.Append(options.ContainsKey("hls_flags") ? $" -hls_flags {options["hls_flags"]}" : string.Empty);
                command.Append($" -hls_segment_filename {output}/1080p/1080p_%d.ts {output}/1080p/1080p.m3u8");
            }

            // 1280x720
            command.Append(" -vf scale=w=1280:h=720:force_original_aspect_ratio=decrease");
            command.Append($" -c:a {transcodeProfile.AudioCodec ?? "copy"}");
            command.Append(transcodeProfile.AudioSampleRate_720 > 0 ? $" -ar {transcodeProfile.AudioSampleRate_720}" : string.Empty);
            command.Append($" -c:v {transcodeProfile.VideoCodec ?? "copy"}");
            command.Append(!transcodeProfile.Preset.Equals("default") ? $" -preset {transcodeProfile.Preset}" : string.Empty);
            command.Append(!transcodeProfile.VideoProfile.Equals("none") ? $" -profile:v  {transcodeProfile.VideoProfile}" : string.Empty);
            command.Append(transcodeProfile.CRF > 0 ? $" -crf {transcodeProfile.CRF}" : string.Empty);
            command.Append(transcodeProfile.VideoBitrate_720 > 0 ? $" -b:v {transcodeProfile.VideoBitrate_720}k" : string.Empty);
            command.Append(transcodeProfile.MaxBitrate_720 > 0 ? $" -maxrate {transcodeProfile.MaxBitrate_720}k" : string.Empty);
            command.Append(transcodeProfile.BufferSize_720 > 0 ? $" -bufsize {transcodeProfile.BufferSize_720}k" : string.Empty);
            command.Append(transcodeProfile.AudioBitrate_720 > 0 ? $" -b:a {transcodeProfile.AudioBitrate_720}k" : string.Empty);

            if (options.ContainsKey("custom_output"))
            {
                command.Append($" -f mpegts {options["custom_output_720p"]}");
            }
            else
            {
                command.Append(options.ContainsKey("hls_time") ? $" -hls_time {options["hls_time"]}" : string.Empty);
                command.Append(options.ContainsKey("hls_playlist_type") ? $" -hls_playlist_type {options["hls_playlist_type"]}" : string.Empty);
                command.Append(options.ContainsKey("hls_flags") ? $" -hls_flags {options["hls_flags"]}" : string.Empty);
                command.Append($" -hls_segment_filename {output}/720p/720p_%d.ts {output}/720p/720p.m3u8");
            }

            // 842x480
            command.Append(" -vf scale=w=842:h=480:force_original_aspect_ratio=decrease");
            command.Append($" -c:a {transcodeProfile.AudioCodec ?? "copy"}");
            command.Append(transcodeProfile.AudioSampleRate_480 > 0 ? $" -ar {transcodeProfile.AudioSampleRate_480}" : string.Empty);
            command.Append($" -c:v {transcodeProfile.VideoCodec ?? "copy"}");
            command.Append(!transcodeProfile.Preset.Equals("default") ? $" -preset {transcodeProfile.Preset}" : string.Empty);
            command.Append(!transcodeProfile.VideoProfile.Equals("none") ? $" -profile:v  {transcodeProfile.VideoProfile}" : string.Empty);
            command.Append(transcodeProfile.CRF > 0 ? $" -crf {transcodeProfile.CRF}" : string.Empty);
            command.Append(transcodeProfile.VideoBitrate_480 > 0 ? $" -b:v {transcodeProfile.VideoBitrate_480}k" : string.Empty);
            command.Append(transcodeProfile.MaxBitrate_480 > 0 ? $" -maxrate {transcodeProfile.MaxBitrate_480}k" : string.Empty);
            command.Append(transcodeProfile.BufferSize_480 > 0 ? $" -bufsize {transcodeProfile.BufferSize_480}k" : string.Empty);
            command.Append(transcodeProfile.AudioBitrate_480 > 0 ? $" -b:a {transcodeProfile.AudioBitrate_480}k" : string.Empty);

            if (options.ContainsKey("custom_output"))
            {
                command.Append($" -f mpegts {options["custom_output_480p"]}");
            }
            else
            {
                command.Append(options.ContainsKey("hls_time") ? $" -hls_time {options["hls_time"]}" : string.Empty);
                command.Append(options.ContainsKey("hls_playlist_type") ? $" -hls_playlist_type {options["hls_playlist_type"]}" : string.Empty);
                command.Append(options.ContainsKey("hls_flags") ? $" -hls_flags {options["hls_flags"]}" : string.Empty);
                command.Append($" -hls_segment_filename {output}/480p/480p_%d.ts {output}/480p/480p.m3u8");
            }

            // 640x360
            command.Append(" -vf scale=w=640:h=360:force_original_aspect_ratio=decrease");
            command.Append($" -c:a {transcodeProfile.AudioCodec ?? "copy"}");
            command.Append(transcodeProfile.AudioSampleRate_360 > 0 ? $" -ar {transcodeProfile.AudioSampleRate_480}" : string.Empty);
            command.Append($" -c:v {transcodeProfile.VideoCodec ?? "copy"}");
            command.Append(!transcodeProfile.Preset.Equals("default") ? $" -preset {transcodeProfile.Preset}" : string.Empty);
            command.Append(!transcodeProfile.VideoProfile.Equals("none") ? $" -profile:v  {transcodeProfile.VideoProfile}" : string.Empty);
            command.Append(transcodeProfile.CRF > 0 ? $" -crf {transcodeProfile.CRF}" : string.Empty);
            command.Append(transcodeProfile.VideoBitrate_360 > 0 ? $" -b:v {transcodeProfile.VideoBitrate_360}k" : string.Empty);
            command.Append(transcodeProfile.MaxBitrate_360 > 0 ? $" -maxrate {transcodeProfile.MaxBitrate_360}k" : string.Empty);
            command.Append(transcodeProfile.BufferSize_360 > 0 ? $" -bufsize {transcodeProfile.BufferSize_360}k" : string.Empty);
            command.Append(transcodeProfile.AudioBitrate_360 > 0 ? $" -b:a {transcodeProfile.AudioBitrate_360}k" : string.Empty);

            if (options.ContainsKey("custom_output"))
            {
                command.Append($" -f mpegts {options["custom_output_360p"]}");
            }
            else
            {
                command.Append(options.ContainsKey("hls_time") ? $" -hls_time {options["hls_time"]}" : string.Empty);
                command.Append(options.ContainsKey("hls_playlist_type") ? $" -hls_playlist_type {options["hls_playlist_type"]}" : string.Empty);
                command.Append(options.ContainsKey("hls_flags") ? $" -hls_flags {options["hls_flags"]}" : string.Empty);
                command.Append($" -hls_segment_filename {output}/360p/360p_%d.ts {output}/360p/360p.m3u8");
            }

            return(command.ToString());
        }
コード例 #17
0
        private static void write_Vhost_Transcode(Transcode o, out string output, int segmentLevel,
                                                  List <Type> types = null !)
        {
            output = "";
            string segmentSpace_head = paddingSegment(segmentLevel).Key;
            string segmentSpace      = paddingSegment(segmentLevel).Value;

            output += segmentSpace_head + o.SectionsName?.ToLower().Trim() + " " + o.InstanceName + " { \r\n";
            foreach (PropertyInfo p in o.GetType().GetProperties())
            {
                object?obj = p.GetValue(o);
                if (obj == null)
                {
                    continue;
                }
                if (p.Name.ToLower().Trim() == "sectionsname" || p.Name.ToLower().Trim() == "instancename" ||
                    p.Name.ToLower().Trim() == "ingestname")
                {
                    continue;
                }

                if ((p.PropertyType == typeof(string) || p.PropertyType == typeof(int?) ||
                     p.PropertyType == typeof(ushort?) ||
                     p.PropertyType == typeof(byte?) ||
                     p.PropertyType == typeof(float?) || p.PropertyType == typeof(bool?)) ||
                    ((types != null) && types.Contains(p.PropertyType)))
                {
                    if (obj != null)
                    {
                        if (p.PropertyType == typeof(bool?))
                        {
                            string s = "";
                            s = Common.GetBoolStr(p, o);
                            string sTmp = segmentSpace + p.Name.Trim().ToLower() + "\t" + s + ";";
                            output += (sTmp + "\r\n");
                        }
                        else
                        {
                            string sTmp = segmentSpace + p.Name.Trim().ToLower() + "\t" + obj + ";";
                            output += (sTmp + "\r\n");
                        }
                    }
                }
            }

            foreach (PropertyInfo p in o.GetType().GetProperties()) //循环非基础类型数据,为了保证基础类型数据在顶上
            {
                object?obj = p.GetValue(o);
                if (obj == null)
                {
                    continue;
                }
                if (p.Name.ToLower().Trim() == "sectionsname" || p.Name.ToLower().Trim() == "instancename")
                {
                    continue;
                }

                if (p.PropertyType == typeof(List <IngestTranscodeEngine>))
                {
                    List <Type> types1 = new List <Type>();
                    types1.Add(typeof(IngestEngineIoformat?));
                    types1.Add(typeof(IngestEngineVprofile?));
                    types1.Add(typeof(IngestEngineVpreset?));
                    if (o.Engines != null)
                    {
                        foreach (IngestTranscodeEngine i in o.Engines)
                        {
                            string s = "";
                            write_Vhost_Ingest_Engine(i, out s, 3, types1);
                            output += s;
                        }
                    }
                }
            }

            output += segmentSpace_head + "}\r\n";
        }
コード例 #18
0
        private async void Transcode(Channel channel, Transcode transcodeProfile, Server server, string callbackUrl)
        {
            var client = new SshClient(server.Ip, "root", server.RootPassword);

            try
            {
                string   successCallbackUrl = callbackUrl.Replace("/STATE", string.Empty);
                string   streamDirectory    = $"/var/hls/{channel.StreamKey}";
                string   prepareCommand     = $"mkdir {streamDirectory}";
                string[] resolutions        = new string[]
                {
                    "1080p",
                    "720p",
                    "480p",
                    "360p"
                };

                prepareCommand += $" && cd {streamDirectory}";
                prepareCommand += " && mkdir 1080p 720p 480p 360p sources";
                prepareCommand += " && cd sources";
                prepareCommand += " && mkdir 1080p 720p 480p 360p";

                foreach (string resolution in resolutions)
                {
                    StringBuilder sourceList = new StringBuilder();

                    for (int i = 0; i < channel.SourcePath.Length; i++)
                    {
                        sourceList.Append($"file '{streamDirectory}/sources/{resolution}/{i}.ts'\n");
                    }

                    prepareCommand += $" && printf \"{sourceList}\" > {streamDirectory}/sources/{resolution}/source_list.txt";
                }

                client.Connect();
                client.RunCommand(prepareCommand);

                for (int i = 0; i < channel.SourcePath.Length; i++)
                {
                    var options = new Dictionary <string, string>
                    {
                        { "custom_output", string.Empty },
                        { "custom_output_1080p", $"{streamDirectory}/sources/1080p/{i}.ts" },
                        { "custom_output_720p", $"{streamDirectory}/sources/720p/{i}.ts" },
                        { "custom_output_480p", $"{streamDirectory}/sources/480p/{i}.ts" },
                        { "custom_output_360p", $"{streamDirectory}/sources/360p/{i}.ts" }
                    };
                    string transcoder = FFMPEGCommand.MakeCommand(transcodeProfile, channel.SourcePath[i], string.Empty, options);
                    var    cmd        = client.CreateCommand($"nohup sh -c '{transcoder} && curl -i -X GET {successCallbackUrl}' >/dev/null 2>&1 & echo $!");
                    var    result     = cmd.Execute();
                    int    pid        = int.Parse(result);
                    client.RunCommand($"disown -h {pid}");
                }

                client.Disconnect();
                client.Dispose();
            }
            catch (Exception)
            {
                string failCallbackUrl = callbackUrl.Replace("STATE", StreamState.Error.ToString());
                var    httpClient      = new HttpClient();
                await httpClient.GetAsync(failCallbackUrl);
            }
        }