Exemplo n.º 1
0
        private FfmpegRenderedCommand EncodeVideo(DashConfig config, MediaMetadata inputStats, int inputBitrate, bool enableStreamCopying, Action <string> progressCallback, CancellationToken cancel)
        {
            FfmpegRenderedCommand ffmpegCommand = FFmpegCommandBuilder
                                                  .Initilize(
                inPath: config.InputFilePath,
                outDirectory: config.OutputDirectory,
                outBaseFilename: config.OutputFileName,
                options: config.Options,
                enableStreamCopying: enableStreamCopying
                )
                                                  .WithVideoCommands(inputStats.VideoStreams, config.Qualities, config.Framerate, config.KeyframeInterval, inputBitrate)
                                                  .WithAudioCommands(inputStats.AudioStreams)
                                                  .WithSubtitleCommands(inputStats.SubtitleStreams)
                                                  .Build();

            // Generate intermediates
            try
            {
                ExecutionResult ffResult;
                stderrLog.Invoke($"Running ffmpeg with arguments: {ffmpegCommand.RenderedCommand}");
                ffResult = ManagedExecution.Start(FFmpegPath, ffmpegCommand.RenderedCommand, stdoutLog, progressCallback, cancel); //TODO: Use a better log/error callback mechanism? Also use a better progress mechanism

                // Detect error in ffmpeg process and cleanup, then return null.
                if (ffResult.ExitCode != 0)
                {
                    stderrLog.Invoke($"ERROR: ffmpeg returned code {ffResult.ExitCode}. File: {config.InputFilePath}");
                    CleanOutputFiles(ffmpegCommand.AllPieces.Select(x => x.Path));
                    return(null);
                }
            }
            catch (Exception ex)
            {
                CleanOutputFiles(ffmpegCommand.AllPieces.Select(x => x.Path));

                if (ex is OperationCanceledException)
                {
                    throw new OperationCanceledException($"Exception running ffmpeg on {config.InputFilePath}", ex);
                }
                else
                {
                    throw new Exception($"Exception running ffmpeg on {config.InputFilePath}", ex);
                }
            }

            return(ffmpegCommand);
        }
Exemplo n.º 2
0
        /// <summary>
        /// oOnverts the input file into an MPEG DASH representations.
        /// This includes multiple bitrates, subtitle tracks, audio tracks, and an MPD manifest.
        /// </summary>
        /// <param name="config"></param>
        /// <param name="progress"></param>
        /// <param name="cancel"></param>
        /// <returns></returns>
        public DashEncodeResult GenerateDash(DashConfig config, IProgress <Dictionary <EncodingStage, double> > progress = null, CancellationToken cancel = default(CancellationToken))
        {
            cancel.ThrowIfCancellationRequested();
            if (!Directory.Exists(WorkingDirectory))
            {
                throw new DirectoryNotFoundException("The given path for the working directory doesn't exist.");
            }

            //Field declarations
            MediaMetadata inputStats;
            IQuality      compareQuality;
            int           inputBitrate;
            bool          enableStreamCopy = false;

            inputStats = ProbeFile(config.InputFilePath);
            if (inputStats == null)
            {
                throw new NullReferenceException("ffprobe query returned a null result.");
            }

            inputBitrate = (int)(inputStats.Bitrate / 1024);
            if (!DisableQualityCrushing)
            {
                config.Qualities = QualityCrusher.CrushQualities(config.Qualities, inputBitrate);
            }
            compareQuality = config.Qualities.First();


            if (EnableStreamCopying && compareQuality.Bitrate == 0)
            {
                enableStreamCopy = Copyable264Infer.DetermineCopyCanBeDone(compareQuality.PixelFormat, compareQuality.Level, compareQuality.Profile.ToString(), inputStats.VideoStreams);
            }

            // Set the framerate interval to match input if user has not already set
            if (config.Framerate <= 0)
            {
                config.Framerate = (int)Math.Round(inputStats.Framerate);
            }

            // Set the keyframe interval to match input if user has not already set
            if (config.KeyframeInterval <= 0)
            {
                config.KeyframeInterval = config.Framerate * 3;
            }

            //This is not really the proper place to have this
            // Logging shim for ffmpeg to get progress info
            var ffmpegLogShim = new Action <string>(x =>
            {
                if (x != null)
                {
                    var match = Encode.Regexes.ParseProgress.Match(x);
                    if (match.Success && TimeSpan.TryParse(match.Value, out TimeSpan p))
                    {
                        stdoutLog(x);
                        float progressFloat = Math.Min(1, (float)(p.TotalMilliseconds / 1000) / inputStats.Duration);
                        if (progress != null)
                        {
                            ReportProgress(progress, EncodingStage.Encode, progressFloat);
                        }
                    }
                    else
                    {
                        stderrLog(x);
                    }
                }
                else
                {
                    stderrLog(x);
                }
            });

            cancel.ThrowIfCancellationRequested();
            FfmpegRenderedCommand ffmpgCommand = EncodeVideo(config, inputStats, inputBitrate, enableStreamCopy, ffmpegLogShim, cancel);

            if (ffmpgCommand is null)
            {
                return(null);
            }

            Mp4BoxRenderedCommand mp4BoxCommand = GenerateDashManifest(config, ffmpgCommand.VideoPieces, ffmpgCommand.AudioPieces, cancel);

            if (mp4BoxCommand is null)
            {
                return(null);
            }

            ReportProgress(progress, EncodingStage.DASHify, 1);
            ReportProgress(progress, EncodingStage.PostProcess, 0.3);

            int maxFileIndex = ffmpgCommand.AllPieces.Max(x => x.Index);
            List <StreamSubtitleFile> allSubtitles = ProcessSubtitles(config, ffmpgCommand.SubtitlePieces, maxFileIndex + 1);

            ReportProgress(progress, EncodingStage.PostProcess, 0.66);

            try
            {
                string mpdFilepath = mp4BoxCommand.MpdPath;
                if (File.Exists(mpdFilepath))
                {
                    MPD mpd = PostProcessMpdFile(mpdFilepath, allSubtitles);

                    var result = new DashEncodeResult(mpd, inputStats.Metadata, TimeSpan.FromMilliseconds((inputStats.VideoStreams.FirstOrDefault()?.duration ?? 0) * 1000), mpdFilepath);

                    // Success.
                    return(result);
                }

                stderrLog.Invoke($"ERROR: MP4Box did not produce the expected mpd file at path {mpdFilepath}. File: {config.InputFilePath}");
                return(null);
            }
            finally
            {
                ReportProgress(progress, EncodingStage.PostProcess, 1);
            }
        }