private async Task <Stream> ExecuteTranscodingProcessAsync(FFMpegTranscodeData data, FFMpegTranscodeContext context, bool waitForBuffer)
        {
            if (context.Partial == true || await IsTranscodeRunningAsync(data.ClientId, data.TranscodeId).ConfigureAwait(false) == false)
            {
                try
                {
                    context.Start();
                    await AddTranscodeContextAsync(data.ClientId, data.TranscodeId, context).ConfigureAwait(false);

                    //context.TargetFile = Path.Combine(data.WorkPath, data.SegmentPlaylist != null ? data.SegmentPlaylist : data.OutputFilePath);
                    //if(context.TargetFile.EndsWith("pipe:"))
                    //{
                    //  context.TargetFile = "";
                    //}
                    context.Live       = data.IsLive;
                    context.SegmentDir = null;
                    if (data.SegmentPlaylist != null)
                    {
                        context.SegmentDir = data.WorkPath;
                    }
                    string name = "MP Transcode - " + data.TranscodeId;
                    if (context.Partial)
                    {
                        name += " - Partial: " + Thread.CurrentThread.ManagedThreadId;
                    }

                    Thread transcodeThread = new Thread(TranscodeProcessor)
                    {
                        //IsBackground = true, //Can cause invalid cache files
                        Name     = name,
                        Priority = ThreadPriority.Normal
                    };
                    FFMpegTranscodeThreadData threadData = new FFMpegTranscodeThreadData()
                    {
                        TranscodeData = data,
                        Context       = context
                    };
                    transcodeThread.Start(threadData);
                }
                catch
                {
                    _ffMpegEncoderHandler.EndEncoding(data.Encoder, data.TranscodeId);
                    context.Fail();
                    await RemoveTranscodeContextAsync(data.ClientId, data.TranscodeId, context).ConfigureAwait(false);

                    throw;
                }
            }

            if (waitForBuffer == false)
            {
                return(null);
            }

            return(await GetTranscodedFileBufferAsync(data, context).ConfigureAwait(false));
        }
        internal static async Task CreatePlaylistFilesAsync(FFMpegTranscodeData data)
        {
            if (Directory.Exists(data.WorkPath) == false)
            {
                Directory.CreateDirectory(data.WorkPath);
            }

            try
            {
                if (data.SegmentPlaylistData != null)
                {
                    string playlist     = Path.Combine(data.WorkPath, BaseMediaConverter.PLAYLIST_FILE_NAME);
                    string tempPlaylist = playlist + ".tmp";
                    using (FileStream fileStream = File.Open(tempPlaylist, FileMode.Create, FileAccess.Write, FileShare.None))
                        await data.SegmentPlaylistData.CopyToAsync(fileStream);
                    MoveFile(tempPlaylist, playlist);

                    if (data.SegmentSubsPlaylistData != null)
                    {
                        playlist     = Path.Combine(data.WorkPath, BaseMediaConverter.PLAYLIST_SUBTITLE_FILE_NAME);
                        tempPlaylist = playlist + ".tmp";
                        using (FileStream fileStream = File.Open(tempPlaylist, FileMode.Create, FileAccess.Write, FileShare.None))
                            await data.SegmentSubsPlaylistData.CopyToAsync(fileStream);
                        MoveFile(tempPlaylist, playlist);
                    }
                }

                if (data.SegmentPlaylist != null && data.SegmentManifestData != null)
                {
                    string tempManifest = data.SegmentPlaylist + ".tmp";
                    using (FileStream fileStream = File.Open(tempManifest, FileMode.Create, FileAccess.Write, FileShare.None))
                        await data.SegmentManifestData.CopyToAsync(fileStream);
                    MoveFile(tempManifest, data.SegmentPlaylist);
                }
            }
            finally
            {
                //No need to keep data so free used memory
                data.SegmentManifestData?.Dispose();
                data.SegmentPlaylistData?.Dispose();
                data.SegmentSubsPlaylistData?.Dispose();
            }
        }
        private async Task <Stream> GetTranscodedFileBufferAsync(FFMpegTranscodeData data, FFMpegTranscodeContext context)
        {
            try
            {
                if (data.IsLive == true && data.SegmentPlaylist == null)
                {
                    int iTry = 60;
                    while (iTry > 0 && context.Failed == false && context.Aborted == false)
                    {
                        bool streamReady = false;
                        try
                        {
                            if (data.LiveStream != null)
                            {
                                streamReady = data.LiveStream.CanRead;
                            }
                        }
                        catch { }
                        if (streamReady)
                        {
                            _logger.Debug(string.Format("FFMpegMediaConverter: Serving transcoded stream '{0}'", data.TranscodeId));
                            return(new BufferedStream(data.LiveStream));
                        }
                        iTry--;
                        await Task.Delay(500).ConfigureAwait(false);
                    }
                    _logger.Error("FFMpegMediaConverter: Timed out waiting for transcoded stream '{0}'", data.TranscodeId);
                }
                else
                {
                    string fileReturnPath = "";
                    if (data.SegmentPlaylist != null)
                    {
                        fileReturnPath = Path.Combine(data.WorkPath, data.SegmentPlaylist);
                    }
                    else
                    {
                        fileReturnPath = Path.Combine(data.WorkPath, data.OutputFilePath);
                    }

                    int iTry = 60;
                    while (iTry > 0 && context.Failed == false && context.Aborted == false)
                    {
                        if (File.Exists(fileReturnPath))
                        {
                            long length = 0;
                            try
                            {
                                length = new FileInfo(fileReturnPath).Length;
                            }
                            catch { }
                            if (length > 0)
                            {
                                _logger.Debug(string.Format("FFMpegMediaConverter: Serving transcoded file '{0}'", fileReturnPath));
                                if (data.SegmentPlaylist != null)
                                {
                                    return(await PlaylistManifest.CorrectPlaylistUrlsAsync(data.SegmentBaseUrl, fileReturnPath).ConfigureAwait(false));
                                }
                                else
                                {
                                    Stream stream = new FileStream(fileReturnPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                                    return(stream);
                                }
                            }
                        }
                        iTry--;
                        await Task.Delay(500).ConfigureAwait(false);
                    }
                    _logger.Error("FFMpegMediaConverter: Timed out waiting for transcoded file '{0}'", fileReturnPath);
                }
            }
            catch (Exception ex)
            {
                _logger.Error("FFMpegMediaConverter: Error waiting for transcoding '{0}'", ex, data.TranscodeId);
            }
            return(null);
        }
        protected override async Task <TranscodeContext> TranscodeImageAsync(string clientId, ImageTranscoding image, bool waitForBuffer)
        {
            FFMpegTranscodeContext context = new FFMpegTranscodeContext(_cacheEnabled, _cachePath);

            context.Partial = false;
            string transcodingFile = Path.Combine(_cachePath, GetTranscodingImageFileName(image));

            if (File.Exists(transcodingFile) == true)
            {
                //Use existing context if possible
                TranscodeContext existingContext = await GetExistingTranscodeContextAsync(clientId, image.TranscodeId).ConfigureAwait(false);

                if (existingContext != null)
                {
                    existingContext.TargetFile = transcodingFile;
                    if (existingContext.Stream == null)
                    {
                        existingContext.AssignStream(await GetFileStreamAsync(transcodingFile).ConfigureAwait(false));
                    }
                    return(existingContext);
                }
                else
                {
                    //Presume that it is a cached file
                    TouchFile(transcodingFile);
                    context.Partial    = false;
                    context.TargetFile = transcodingFile;
                    context.AssignStream(await GetFileStreamAsync(transcodingFile).ConfigureAwait(false));
                    return(context);
                }
            }

            FFMpegTranscodeData data = new FFMpegTranscodeData(_cachePath)
            {
                TranscodeId = image.TranscodeId, ClientId = clientId
            };

            if (string.IsNullOrEmpty(image.TranscoderBinPath) == false)
            {
                data.TranscoderBinPath = image.TranscoderBinPath;
            }
            if (string.IsNullOrEmpty(image.TranscoderArguments) == false)
            {
                data.ConcatedFileInput   = image.ConcatSourceMediaPaths;
                data.TranscoderArguments = image.TranscoderArguments;
                data.InputMediaFilePaths = image.SourceMediaPaths;
            }
            else
            {
                _ffMpegCommandline.InitTranscodingParameters(image.ConcatSourceMediaPaths, image.SourceMediaPaths, ref data);
                _ffMpegCommandline.AddTranscodingThreadsParameters(true, ref data);

                _ffMpegCommandline.AddImageParameters(image, data);

                data.InputArguments[data.FirstResourceIndex].Add("-f image2pipe"); //pipe works with network drives
                data.OutputArguments.Add("-f image2");
            }
            data.OutputFilePath = transcodingFile;
            context.TargetFile  = transcodingFile;

            _logger.Debug("FFMpegMediaConverter: Invoking transcoder to transcode image file '{0}' for transcode '{1}'", image.SourceMediaPaths, image.TranscodeId);
            context.Start();
            context.AssignStream(await ExecuteTranscodingProcessAsync(data, context, waitForBuffer).ConfigureAwait(false));
            return(context);
        }
        protected override async Task <TranscodeContext> TranscodeAudioAsync(string clientId, AudioTranscoding audio, double timeStart, double timeDuration, bool waitForBuffer)
        {
            FFMpegTranscodeContext context = new FFMpegTranscodeContext(_cacheEnabled, _cachePath);

            context.TargetDuration = audio.SourceDuration.Value;
            if (timeStart == 0 && audio.TargetIsLive == false && _cacheEnabled)
            {
                timeDuration    = 0;
                context.Partial = false;
            }
            else
            {
                audio.TargetIsLive = true;
                context.Partial    = true;
            }
            if (audio.TargetAudioContainer == AudioContainer.Unknown)
            {
                audio.TargetAudioContainer = audio.SourceAudioContainer;
            }

            string transcodingFile = GetTranscodingAudioFileName(audio, timeStart);

            transcodingFile = Path.Combine(_cachePath, transcodingFile);

            if (File.Exists(transcodingFile))
            {
                //Use non-partial context if possible
                TranscodeContext existingContext = await GetExistingTranscodeContextAsync(clientId, audio.TranscodeId).ConfigureAwait(false);

                if (existingContext != null)
                {
                    existingContext.TargetFile = transcodingFile;
                    if (existingContext.Stream == null)
                    {
                        existingContext.AssignStream(await GetFileStreamAsync(transcodingFile).ConfigureAwait(false));
                    }

                    if (existingContext.CurrentDuration.TotalSeconds == 0)
                    {
                        double bitrate = 0;
                        if (audio.TargetAudioBitrate.HasValue)
                        {
                            bitrate = audio.TargetAudioBitrate.Value;
                        }
                        else if (audio.SourceAudioBitrate.HasValue)
                        {
                            bitrate = audio.SourceAudioBitrate.Value;
                        }
                        bitrate *= 1024; //Bitrate in bits/s
                        if (bitrate > 0)
                        {
                            long startByte = Convert.ToInt64((bitrate * timeStart) / 8.0);
                            if (existingContext.Stream.Length > startByte)
                            {
                                return(existingContext);
                            }
                        }
                    }
                    else
                    {
                        if (existingContext.CurrentDuration.TotalSeconds > timeStart)
                        {
                            return(existingContext);
                        }
                    }
                }
                else
                {
                    //Presume that it is a cached file
                    TouchFile(transcodingFile);
                    context.Partial    = false;
                    context.TargetFile = transcodingFile;
                    context.AssignStream(await GetFileStreamAsync(transcodingFile).ConfigureAwait(false));
                    return(context);
                }
            }

            FFMpegTranscodeData data = new FFMpegTranscodeData(_cachePath)
            {
                TranscodeId = audio.TranscodeId, ClientId = clientId
            };

            if (string.IsNullOrEmpty(audio.TranscoderBinPath) == false)
            {
                data.TranscoderBinPath = audio.TranscoderBinPath;
            }

            if (string.IsNullOrEmpty(audio.TranscoderArguments) == false)
            {
                data.ConcatedFileInput   = audio.ConcatSourceMediaPaths;
                data.TranscoderArguments = audio.TranscoderArguments;
                data.InputMediaFilePaths = audio.SourceMediaPaths;
                data.OutputFilePath      = transcodingFile;
                context.TargetFile       = transcodingFile;
            }
            else
            {
                _ffMpegCommandline.InitTranscodingParameters(audio.ConcatSourceMediaPaths, audio.SourceMediaPaths, ref data);
                _ffMpegCommandline.AddTranscodingThreadsParameters(true, ref data);

                _ffMpegCommandline.AddTimeParameters(audio, timeStart, timeDuration, data);

                _ffMpegCommandline.AddAudioParameters(audio, data);

                context.TargetFile = await _ffMpegCommandline.AddTargetAudioFormatAndOutputFileParametersAsync(audio, transcodingFile, data).ConfigureAwait(false);

                data.OutputArguments.Add("-vn");
            }

            _logger.Debug("FFMpegMediaConverter: Invoking transcoder to transcode audio file '{0}' for transcode '{1}'", audio.SourceMediaPaths, audio.TranscodeId);
            context.Start();
            context.AssignStream(await ExecuteTranscodingProcessAsync(data, context, waitForBuffer).ConfigureAwait(false));
            return(context);
        }
        protected override async Task <TranscodeContext> TranscodeVideoAsync(string clientId, VideoTranscoding video, double timeStart, double timeDuration, bool waitForBuffer)
        {
            FFMpegTranscodeContext context = new FFMpegTranscodeContext(_cacheEnabled, _cachePath);

            context.TargetDuration = video.SourceMediaDuration;
            if (timeStart == 0 && video.TargetIsLive == false && _cacheEnabled)
            {
                timeDuration    = 0;
                context.Partial = false;
            }
            else if (video.TargetVideoContainer == VideoContainer.Hls)
            {
                context.Partial = true;
            }
            else
            {
                video.TargetIsLive = true;
                context.Partial    = true;
            }
            if (video.TargetVideoContainer == VideoContainer.Unknown)
            {
                video.TargetVideoContainer = video.SourceVideoContainer;
            }

            bool          embeddedSupported = false;
            SubtitleCodec embeddedSubCodec  = SubtitleCodec.Unknown;

            if (video.TargetSubtitleSupport == SubtitleSupport.Embedded)
            {
                if (video.TargetVideoContainer == VideoContainer.Matroska)
                {
                    embeddedSupported         = true;
                    embeddedSubCodec          = SubtitleCodec.Ass;
                    video.TargetSubtitleCodec = SubtitleCodec.Ass;
                }
                else if (video.TargetVideoContainer == VideoContainer.Mp4)
                {
                    embeddedSupported         = true;
                    embeddedSubCodec          = SubtitleCodec.MovTxt;
                    video.TargetSubtitleCodec = SubtitleCodec.MovTxt;
                }
                else if (video.TargetVideoContainer == VideoContainer.Hls)
                {
                    embeddedSupported         = true;
                    embeddedSubCodec          = SubtitleCodec.WebVtt;
                    video.TargetSubtitleCodec = SubtitleCodec.WebVtt;
                }
                else if (video.TargetVideoContainer == VideoContainer.Avi)
                {
                    embeddedSupported         = true;
                    embeddedSubCodec          = SubtitleCodec.Srt;
                    video.TargetSubtitleCodec = SubtitleCodec.Srt;
                }
                //else if (video.TargetVideoContainer == VideoContainer.Mpeg2Ts)
                //{
                //  embeddedSupported = true;
                //  embeddedSubCodec = SubtitleCodec.DvbSub;
                //  video.TargetSubtitleCodec = SubtitleCodec.VobSub;
                //}
                else
                {
                    _logger.Debug("FFMpegMediaConverter: Container {0} does not support embedded subtitles", video.TargetVideoContainer);
                }
            }
            video.TargetSubtitleMime       = SubtitleHelper.GetSubtitleMime(video.TargetSubtitleCodec);
            video.PreferredSourceSubtitles = await GetSubtitlesAsync(clientId, video, timeStart).ConfigureAwait(false);

            string transcodingFile = GetTranscodingVideoFileName(video, timeStart, embeddedSupported);

            transcodingFile = Path.Combine(_cachePath, transcodingFile);

            if (File.Exists(transcodingFile))
            {
                //Use non-partial transcode if possible
                TranscodeContext existingContext = await GetExistingTranscodeContextAsync(clientId, video.TranscodeId).ConfigureAwait(false);

                if (existingContext != null)
                {
                    existingContext.TargetFile = transcodingFile;
                    if (existingContext.Stream == null)
                    {
                        existingContext.AssignStream(await GetFileStreamAsync(transcodingFile).ConfigureAwait(false));
                    }
                    if (existingContext.CurrentDuration.TotalSeconds == 0)
                    {
                        double bitrate = 0;
                        if (video.TargetVideoBitrate.HasValue && video.TargetAudioBitrate.HasValue)
                        {
                            bitrate = video.TargetVideoBitrate.Value + video.TargetAudioBitrate.Value;
                        }
                        else if (video.SourceVideoStream.Bitrate.HasValue && video.SourceAudioStreams.Any(a => a.Bitrate > 0))
                        {
                            bitrate = video.SourceVideoStream.Bitrate.Value + video.SourceAudioStreams.Max(a => a.Bitrate ?? 0);
                        }
                        bitrate *= 1024; //Bitrate in bits/s
                        if (bitrate > 0)
                        {
                            long startByte = Convert.ToInt64((bitrate * timeStart) / 8.0);
                            if (existingContext.Stream.Length > startByte)
                            {
                                return(existingContext);
                            }
                        }
                    }
                    else
                    {
                        if (existingContext.CurrentDuration.TotalSeconds > timeStart)
                        {
                            return(existingContext);
                        }
                    }
                }
                else
                {
                    //Presume that it is a cached file
                    TouchFile(transcodingFile);
                    context.Partial    = false;
                    context.TargetFile = transcodingFile;
                    context.AssignStream(await GetFileStreamAsync(transcodingFile).ConfigureAwait(false));
                    return(context);
                }
            }

            if (video.TargetVideoContainer == VideoContainer.Hls)
            {
                long requestedSegmentSequence = requestedSegmentSequence = Convert.ToInt64(timeStart / HLSSegmentTimeInSeconds);
                if (requestedSegmentSequence > 0)
                {
                    requestedSegmentSequence--; //1 segment file margin
                }
                string pathName    = FFMpegPlaylistManifest.GetPlaylistFolderFromTranscodeFile(_cachePath, transcodingFile);
                string playlist    = Path.Combine(pathName, PlaylistManifest.PLAYLIST_MANIFEST_FILE_NAME);
                string segmentFile = Path.Combine(pathName, requestedSegmentSequence.ToString("00000") + ".ts");
                if (File.Exists(playlist) == true && File.Exists(segmentFile) == true)
                {
                    //Use exisitng context if possible
                    TranscodeContext existingContext = await GetExistingTranscodeContextAsync(clientId, video.TranscodeId).ConfigureAwait(false);

                    if (existingContext != null)
                    {
                        if (existingContext.LastSegment > requestedSegmentSequence)
                        {
                            existingContext.TargetFile = playlist;
                            existingContext.SegmentDir = pathName;
                            if (existingContext.Stream == null)
                            {
                                existingContext.AssignStream(await GetFileStreamAsync(playlist).ConfigureAwait(false));
                            }
                            existingContext.HlsBaseUrl = video.HlsBaseUrl;
                            return(existingContext);
                        }
                    }
                    else
                    {
                        //Presume that it is a cached file
                        TouchDirectory(pathName);
                        context.Partial    = false;
                        context.TargetFile = playlist;
                        context.SegmentDir = pathName;
                        context.HlsBaseUrl = video.HlsBaseUrl;
                        context.AssignStream(await GetFileStreamAsync(playlist).ConfigureAwait(false));
                        return(context);
                    }
                }
            }

            FFMpegTranscodeData data = new FFMpegTranscodeData(_cachePath)
            {
                TranscodeId = video.TranscodeId, ClientId = clientId
            };

            if (string.IsNullOrEmpty(video.TranscoderBinPath) == false)
            {
                data.TranscoderBinPath = video.TranscoderBinPath;
            }
            if (string.IsNullOrEmpty(video.TranscoderArguments) == false)
            {
                data.ConcatedFileInput   = video.ConcatSourceMediaPaths;
                data.TranscoderArguments = video.TranscoderArguments;
                data.InputMediaFilePaths = video.SourceMediaPaths;
                if (video.PreferredSourceSubtitles != null)
                {
                    foreach (var mediaSourceIndex in video.PreferredSourceSubtitles.Keys)
                    {
                        foreach (var sub in video.PreferredSourceSubtitles[mediaSourceIndex])
                        {
                            if (string.IsNullOrEmpty(sub.SourcePath) == false)
                            {
                                data.AddSubtitle(mediaSourceIndex, sub.SourcePath);
                                context.TargetSubtitles.Add(sub.SourcePath);
                            }
                        }
                    }
                }
                data.OutputFilePath = transcodingFile;
                context.TargetFile  = transcodingFile;
            }
            else
            {
                data.Encoder = _ffMpegEncoderHandler.StartEncoding(video.TranscodeId, video.TargetVideoCodec);
                _ffMpegCommandline.InitTranscodingParameters(video.ConcatSourceMediaPaths, video.SourceMediaPaths, ref data);

                bool useX26XLib = video.TargetVideoCodec == VideoCodec.H264 || video.TargetVideoCodec == VideoCodec.H265;
                _ffMpegCommandline.AddTranscodingThreadsParameters(!useX26XLib, ref data);

                int subCopyStream = -1;
                if (video.PreferredSourceSubtitles.Any())
                {
                    if (video.FirstPreferredSourceSubtitle.IsEmbedded)
                    {
                        subCopyStream = video.FirstPreferredSourceSubtitle.StreamIndex;
                        _ffMpegCommandline.AddSubtitleCopyParameters(video.FirstPreferredSourceSubtitle, data);
                    }
                    else if (embeddedSupported)
                    {
                        foreach (int mediaSourceIndex in video.PreferredSourceSubtitles.Keys)
                        {
                            _ffMpegCommandline.AddSubtitleEmbeddingParameters(mediaSourceIndex, video.PreferredSourceSubtitles[mediaSourceIndex], embeddedSubCodec, timeStart, data);
                        }
                    }
                    else if (video.TargetSubtitleSupport != SubtitleSupport.SoftCoded)
                    {
                        video.TargetSubtitleSupport = SubtitleSupport.HardCoded; //Fallback to hardcoded subtitles
                        _logger.Debug("FFMpegMediaConverter: Soft subs not supported. Fallback to hardcoded subtitles");
                    }
                }
                else
                {
                    embeddedSupported = false;
                    data.OutputArguments.Add("-sn");
                }

                _ffMpegCommandline.AddTimeParameters(video, timeStart, timeDuration, data);
                _ffMpegCommandline.AddStreamMapParameters(video, data);

                FFMpegEncoderConfig encoderConfig = _ffMpegEncoderHandler.GetEncoderConfig(data.Encoder);
                _ffMpegCommandline.AddVideoParameters(video, data.TranscodeId, encoderConfig, data);
                _ffMpegCommandline.AddVideoAudioParameters(video, data);

                var result = await _ffMpegCommandline.AddTargetVideoFormatAndOutputFileParametersAsync(video, transcodingFile, timeStart, data).ConfigureAwait(false);

                context.TargetFile     = result.TranscodingFile;
                context.CurrentSegment = result.StartSegment;
                if (video.PreferredSourceSubtitles.Any())
                {
                    foreach (var sub in video.PreferredSourceSubtitles.SelectMany(s => s.Value))
                    {
                        if (string.IsNullOrEmpty(sub.SourcePath) == false)
                        {
                            context.TargetSubtitles.Add(sub.SourcePath);
                        }
                    }
                }
            }

            _logger.Info("FFMpegMediaConverter: Invoking transcoder to transcode video file '{0}' for transcode '{1}' with arguments '{2}'", video.SourceMediaPaths.First().Value, video.TranscodeId, String.Join(", ", data.OutputArguments.ToArray()));
            context.Start();
            context.AssignStream(await ExecuteTranscodingProcessAsync(data, context, waitForBuffer).ConfigureAwait(false));
            return(context);
        }
        protected override async Task <bool> ConvertSubtitleFileAsync(string clientId, VideoTranscoding video, double timeStart, string transcodingFile, SubtitleStream sourceSubtitle, SubtitleStream res)
        {
            SubtitleCodec targetCodec = video.TargetSubtitleCodec;

            if (targetCodec == SubtitleCodec.Unknown)
            {
                targetCodec = sourceSubtitle.Codec;
            }

            string tempFile          = null;
            FFMpegTranscodeData data = new FFMpegTranscodeData(_cachePath)
            {
                TranscodeId = video.TranscodeId + "_sub", ClientId = clientId
            };

            if (string.IsNullOrEmpty(video.TranscoderBinPath) == false)
            {
                data.TranscoderBinPath = video.TranscoderBinPath;
            }
            if (string.IsNullOrEmpty(video.TranscoderArguments) == false)
            {
                // TODO: not sure if this is working
                data.TranscoderArguments = video.TranscoderArguments;
                data.InputMediaFilePaths.Add(0, res.SourcePath);
                data.InputArguments.Add(0, new List <string>());
            }
            else
            {
                tempFile = transcodingFile + ".tmp";
                res      = await ConvertSubtitleEncodingAsync(res, tempFile, video.TargetSubtitleCharacterEncoding).ConfigureAwait(false);

                // TODO: not sure if this is working
                _ffMpegCommandline.InitTranscodingParameters(false, new Dictionary <int, string> {
                    { 0, res.SourcePath }
                }, ref data);
                data.InputArguments[0].Add(string.Format("-f {0}", FFMpegGetSubtitleContainer.GetSubtitleContainer(sourceSubtitle.Codec)));
                if (timeStart > 0)
                {
                    data.OutputArguments.Add(string.Format(CultureInfo.InvariantCulture, "-ss {0:0.0}", timeStart));
                }

                res.Codec = targetCodec;
                string subtitleEncoder = "copy";
                if (res.Codec == SubtitleCodec.Unknown)
                {
                    res.Codec = SubtitleCodec.Ass;
                }

                if (sourceSubtitle.Codec != res.Codec)
                {
                    subtitleEncoder = FFMpegGetSubtitleContainer.GetSubtitleContainer(res.Codec);
                }

                string subtitleFormat = FFMpegGetSubtitleContainer.GetSubtitleContainer(res.Codec);
                data.OutputArguments.Add("-vn");
                data.OutputArguments.Add("-an");
                data.OutputArguments.Add(string.Format("-c:s {0}", subtitleEncoder));
                data.OutputArguments.Add(string.Format("-f {0}", subtitleFormat));
            }
            data.OutputFilePath = transcodingFile;

            _logger.Debug("FFMpegMediaConverter: Invoking transcoder to transcode subtitle file '{0}' for transcode '{1}'", res.SourcePath, data.TranscodeId);
            bool success = false;
            var  path    = ResourcePath.Deserialize(res.SourcePath);

            if (path.TryCreateLocalResourceAccessor(out var subRes))
            {
                using (var rah = new LocalFsResourceAccessorHelper(subRes))
                    using (var access = rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess())
                    {
                        var result = await FFMpegBinary.FFMpegExecuteWithResourceAccessAsync(rah.LocalFsResourceAccessor, data.TranscoderArguments, ProcessPriorityClass.Normal, _transcoderTimeout).ConfigureAwait(false);

                        success = result.Success;
                    }
            }
            if (success && File.Exists(transcodingFile) == true)
            {
                if (tempFile != null && File.Exists(tempFile))
                {
                    File.Delete(tempFile);
                }
                res.SourcePath = LocalFsResourceProviderBase.ToProviderPath(transcodingFile);
                return(true);
            }
            return(false);
        }