private Mp4BoxRenderedCommand GenerateDashManifest(DashConfig config, IEnumerable <StreamVideoFile> videoFiles, IEnumerable <StreamAudioFile> audioFiles, CancellationToken cancel) { string mpdOutputPath = Path.Combine(config.OutputDirectory, config.OutputFileName) + ".mpd"; var mp4boxCommand = Mp4BoxCommandBuilder.BuildMp4boxMpdCommand( videoFiles: videoFiles, audioFiles: audioFiles, mpdOutputPath: mpdOutputPath, keyInterval: (config.KeyframeInterval / config.Framerate) * 1000, additionalFlags: config.Options.AdditionalMP4BoxFlags); // Generate DASH files. ExecutionResult mpdResult; stderrLog.Invoke($"Running MP4Box with arguments: {mp4boxCommand.RenderedCommand}"); try { mpdResult = ManagedExecution.Start(BoxPath, mp4boxCommand.RenderedCommand, stdoutLog, stderrLog, cancel); // Dash Failed TODO: Add in Progress report behavior that was excluded from this // Detect error in MP4Box process and cleanup, then return null. if (mpdResult.ExitCode != 0) { MPD mpdFile = MPD.LoadFromFile(mpdOutputPath); var filePaths = mpdFile.GetFileNames().Select(x => Path.Combine(config.OutputDirectory, x)); stderrLog.Invoke($"ERROR: MP4Box returned code {mpdResult.ExitCode}. File: {config.InputFilePath}"); CleanOutputFiles(filePaths); CleanOutputFiles(mpdResult.Output); return(null); } } catch (Exception ex) { if (ex is OperationCanceledException) { throw new OperationCanceledException($"Exception running MP4box on {config.InputFilePath}", ex); } else { throw new Exception($"Exception running MP4box on {config.InputFilePath}", ex); } } finally { CleanOutputFiles(videoFiles.Select(x => x.Path)); CleanOutputFiles(audioFiles.Select(x => x.Path)); } return(mp4boxCommand); }
/// <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); }