Beispiel #1
0
        public void GenerateDash_MidLevelApiOptions_ProducesCorrectDashEncodeResult()
        {
            Encoder encoder = new Encoder(ffmpegPath, ffprobePath, mp4boxPath,
                                          ffmpegCommandGenerator: (dashConfig, mediaMetadata) =>
            {
                DashConfig c    = dashConfig;
                MediaMetadata m = mediaMetadata;
                FFmpegCommand r = Encoder.GenerateFFmpegCommand(c, m);
                return(r);
            },
                                          mp4BoxCommandGenerator: (dashConfig, videoStreams, audioStreams) =>
            {
                DashConfig c = dashConfig;
                IEnumerable <VideoStreamCommand> v = videoStreams;
                IEnumerable <AudioStreamCommand> a = audioStreams;
                Mp4BoxCommand r = Encoder.GenerateMp4BoxCommand(c, v, a);
                return(r);
            });
            DashConfig config = new DashConfig(testFileName, RunPath, Qualities, "output");

            encodeResult = encoder.GenerateDash(config, encoder.ProbeFile(config.InputFilePath, out _));

            Assert.NotNull(encodeResult.DashFilePath);
            Assert.NotNull(encodeResult.DashFileContent);
            Assert.NotNull(encodeResult.MediaFiles);
            Assert.Equal(4, encodeResult.MediaFiles.Count());
        }
Beispiel #2
0
        /// <summary>
        /// Converts the input file into an MPEG DASH representations.
        /// This includes multiple bitrates, subtitle tracks, audio tracks, and an MPD manifest.
        /// </summary>
        /// <param name="config">A configuration specifying how DASHing should be performed.</param>
        /// <param name="probedInputData">The output from running <see cref="ProbeFile">ProbeFile</see> on the input file.</param>
        /// <param name="progress">Gives progress through the ffmpeg process, which takes the longest of all the parts of DASHing.</param>
        /// <param name="cancel">Allows the process to be ended part way through.</param>
        /// <returns>A value containing metadata about the artifacts of the DASHing process.</returns>
        /// <exception cref="ArgumentNullException">The probe data parameter is null.</exception>
        /// <exception cref="FFMpegFailedException">The ffmpeg process returned an error code other than 0 or threw an inner exception such as <see cref="OperationCanceledException"/>.</exception>
        /// <exception cref="Mp4boxFailedException">The MP4Box process returned an error code other than 0, threw an inner exception such as <see cref="OperationCanceledException"/>, or did not generate an MPD file.</exception>
        /// <exception cref="DashManifestNotCreatedException">Everything seemed to go okay until the final step with MP4Box, where an MPD file was not found.</exception>
        /// <exception cref="OperationCanceledException">The cancellation token was triggered.</exception>
        public DashEncodeResult GenerateDash(DashConfig config, MediaMetadata probedInputData, IProgress <double> progress = null, CancellationToken cancel = default)
        {
            cancel.ThrowIfCancellationRequested();

            if (probedInputData == null)
            {
                throw new ArgumentNullException(nameof(probedInputData), "Probe data cannot be null. Get this parameter from calling ProbeFile.");
            }

            //Field declarations
            IQuality compareQuality;
            bool     enableStreamCopy = false;

            config.Qualities = QualityCrusher.CrushQualities(config.Qualities, probedInputData.KBitrate, config.QualityCrushTolerance);
            compareQuality   = config.Qualities.First();

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

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

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

            cancel.ThrowIfCancellationRequested();

            FFmpegCommand ffmpegCommand = EncodeVideo(config, probedInputData, progress, cancel);

            Mp4BoxCommand mp4BoxCommand = GenerateDashManifest(config, ffmpegCommand.VideoCommands, ffmpegCommand.AudioCommands, cancel, ffmpegCommand);

            if (File.Exists(mp4BoxCommand.MpdPath))
            {
                int maxFileIndex = ffmpegCommand.AllStreamCommands.Max(x => x.Index);
                IEnumerable <SubtitleStreamCommand> allSubtitles = ProcessSubtitles(config, ffmpegCommand.SubtitleCommands, maxFileIndex + 1);
                MPD mpd = PostProcessMpdFile(mp4BoxCommand.MpdPath, allSubtitles);

                return(new DashEncodeResult(mp4BoxCommand.MpdPath, mpd, ffmpegCommand, probedInputData));
            }

            throw new DashManifestNotCreatedException(mp4BoxCommand.MpdPath, ffmpegCommand, mp4BoxCommand,
                                                      $"MP4Box did not produce the expected mpd file at path {mp4BoxCommand.MpdPath}. File: {config.InputFilePath}");
        }
Beispiel #3
0
 ///<inheritdoc cref="DashManifestNotCreatedException"/>
 public DashManifestNotCreatedException(string expectedMpdPath, FFmpegCommand ffmpegCommand, Mp4BoxCommand mp4boxCommand, string message, Exception innerException) : base(message, innerException)
 {
     ExpectedMpdPath = expectedMpdPath;
     FFmpegCommand   = ffmpegCommand;
     MP4BoxCommand   = mp4boxCommand;
 }
Beispiel #4
0
        /// <summary>
        /// This method takes configuration, and a set of video and audio streams, and assemb
        /// </summary>
        /// <param name="config">The config to use to generate the MP4Box command.</param>
        /// <param name="videoFiles">A set of video files to include in the DASH process and manifest.</param>
        /// <param name="audioFiles">A set of audio files to include in the DASH process and manifest.</param>
        /// <param name="cancel">A cancel token to pass to the process.</param>
        /// <param name="originalFFmpegCommand">The ffmpeg command used to create the input files. This is for exception logging only, and may be left null.</param>
        protected virtual Mp4BoxCommand GenerateDashManifest(DashConfig config, IEnumerable <VideoStreamCommand> videoFiles, IEnumerable <AudioStreamCommand> audioFiles, CancellationToken cancel, FFmpegCommand originalFFmpegCommand = null)
        {
            Mp4BoxCommand   mp4boxCommand = null;
            ExecutionResult mpdResult;
            var             log = new StringBuilder();

            try
            {
                mp4boxCommand = Mp4BoxCommandGeneratorMethod(config, videoFiles, audioFiles);
                mpdResult     = ManagedExecution.Start(Mp4BoxPath, mp4boxCommand.RenderedCommand,
                                                       (x) =>
                {
                    log.AppendLine(x);
                    stdoutLog.Invoke(x);
                },
                                                       (x) =>
                {
                    log.AppendLine(x);
                    stderrLog.Invoke(x);
                }, cancel);

                if (mpdResult.ExitCode != 0)
                {
                    try
                    {
                        // Error in MP4Box.
                        if (File.Exists(mp4boxCommand.MpdPath))
                        {
                            MPD mpdFile   = MPD.LoadFromFile(mp4boxCommand.MpdPath);
                            var filePaths = mpdFile.GetFileNames().Select(x => Path.Combine(config.OutputDirectory, x));

                            CleanFiles(filePaths);
                            CleanFiles(mpdResult.Output);
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Mp4boxFailedException(originalFFmpegCommand, mp4boxCommand, log, $"MP4Box returned code {mpdResult.ExitCode}.", ex);
                    }
                    throw new Mp4boxFailedException(originalFFmpegCommand, mp4boxCommand, log, $"MP4Box returned code {mpdResult.ExitCode}.");
                }
                else if (!File.Exists(mp4boxCommand.MpdPath))
                {
                    throw new Mp4boxFailedException(originalFFmpegCommand, mp4boxCommand, log, $"MP4Box appeared to succeed, but no MPD file was created.");
                }
            }
            catch (Exception ex)
            {
                if (ex is Mp4boxFailedException)
                {
                    throw;
                }
                throw new Mp4boxFailedException(originalFFmpegCommand, mp4boxCommand, log, ex.Message, ex);
            }
            finally
            {
                CleanFiles(videoFiles.Select(x => x.Path));
                CleanFiles(audioFiles.Select(x => x.Path));
            }

            return(mp4boxCommand);
        }
Beispiel #5
0
 ///<inheritdoc cref="DashManifestNotCreatedException"/>
 public Mp4boxFailedException(FFmpegCommand ffmpegCommand, Mp4BoxCommand mp4boxCommand, StringBuilder log, string message, Exception innerException) : base(message, innerException)
 {
     FFmpegCommand = ffmpegCommand;
     MP4BoxCommand = mp4boxCommand;
     Log           = log;
 }