/// <summary> /// Create a typical instance of DashEncodeResult. /// </summary> /// <param name="file">The exact mpd content deserialized from XML.</param> /// <param name="metadata">Arbitrary metadata about the output streams.</param> /// <param name="duration">The play duration of the media.</param> /// <param name="path">The exact path to the mpd file.</param> public DashEncodeResult(MPD file, IReadOnlyDictionary <string, string> metadata, TimeSpan duration, string path) { DashFileContent = file; Metadata = metadata; FileDuration = duration; DashFilePath = path; }
/// <summary> /// Create a typical instance of DashEncodeResult. /// </summary> /// <param name="mpdPath">The exact path to the mpd file.</param> /// <param name="mpdContent">The exact mpd content deserialized from XML.</param> /// <param name="ffmpegCommand">The generated ffmpeg command used when </param> /// <param name="inputMetadata">Metadata about the DASHed input file.</param> public DashEncodeResult(string mpdPath, MPD mpdContent, FFmpegCommand ffmpegCommand, MediaMetadata inputMetadata) { DashFileContent = mpdContent; DashFilePath = mpdPath; FFmpegCommand = ffmpegCommand; InputMetadata = inputMetadata; }
/// <summary> /// Performs on-disk post processing of the generated MPD file. /// Subtitles are added, useless tags removed, etc. /// </summary> private MPD PostProcessMpdFile(string filepath, IEnumerable <SubtitleStreamCommand> subtitles) { MPD.TryLoadFromFile(filepath, out MPD mpd, out Exception ex); mpd.ProgramInformation = new ProgramInformation() { Title = $"DEnc", MoreInformationURL = "https://github.com/bloomtom/DEnc" }; // Get the highest used representation ID so we can increment it for new IDs. int.TryParse(mpd.Period.Max(x => x.AdaptationSet.Max(y => y.Representation.Max(z => z.Id))), out int representationId); representationId++; foreach (var period in mpd.Period) { // Add subtitles to this period. foreach (var sub in subtitles) { AdaptationSet subtitleSet = GenerateSubtitleAdaptationSet(representationId, sub, out int nextRepresentationId); if (subtitleSet != null) { period.AdaptationSet.Add(subtitleSet); representationId = nextRepresentationId; } } } mpd.SaveToFile(filepath); return(mpd); }
private void ParseMPD(string path, bool UseDebug) { MPD parser = new MPD(); parser.UseDebug = UseDebug; parser.Parse(path); parser.BuildPrefab(); }
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> /// 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}"); }
/// <summary> /// Performs on-disk post processing of the generated MPD file. /// Subtitles are added, useless tags removed, etc. /// </summary> private static MPD PostProcessMpdFile(string filepath, List <StreamSubtitleFile> subtitles) { MPD.LoadFromFile(filepath, out MPD mpd, out Exception ex); mpd.ProgramInformation = null; // Get the highest used representation ID so we can increment it for new IDs. int.TryParse(mpd.Period.Max(x => x.AdaptationSet.Max(y => y.Representation.Max(z => z.Id))), out int representationId); representationId++; foreach (var period in mpd.Period) { // Add subtitles to this period. foreach (var sub in subtitles) { period.AdaptationSet.Add(new AdaptationSet() { MimeType = "text/vtt", Lang = sub.Language, ContentType = "text", Representation = new List <Representation>() { new Representation() { Id = representationId.ToString(), Bandwidth = 256, BaseURL = new List <string>() { Path.GetFileName(sub.Path) } } }, Role = new DescriptorType() { SchemeIdUri = "urn:gpac:dash:role:2013", Value = $"{sub.Language} {representationId.ToString()}" } }); representationId++; } } mpd.SaveToFile(filepath); return(mpd); }
public void OnClick() { if (selectedRoomFilePath != null) { foreach (Transform child in container.transform) { Destroy(child.gameObject); } MPD roomParser = new MPD(); //roomParser.debugger = true; roomParser.Parse(selectedRoomFilePath); GameObject roomModel = roomParser.BuildGameObject(); roomModel.transform.parent = container.transform; roomModel.transform.localPosition = Vector3.zero; roomModel.transform.localRotation = new Quaternion(); roomModel.transform.localScale = Vector3.one * 10; } }
/// <summary> /// Gets all the BaseURL file names from the MPD file /// </summary> /// <param name="mpdFile"></param> /// <returns></returns> public static IEnumerable <string> GetFileNames(this MPD mpdFile) { if (mpdFile is null) { return(new List <string>()); } List <string> names = new List <string>(); foreach (var period in mpdFile.Period) { foreach (var set in period.AdaptationSet) { foreach (var representation in set.Representation) { names.AddRange(representation.BaseURL); } } } return(names); }
static List <Face> Get_FaceBasicInformation() { List <Face> Person = new List <Face>(); //Creat a new list called Person StringBuilder W_FileJSON = new StringBuilder(); //W_FileJSON is the String be written into the TXT file. W_FileJSON.Clear(); MyValue.Finish = ""; string[] MyPhotoDicrectory = Directory.GetFiles("PhotoGroup"); //MyPhotoDicrectory can help API upload all the file in the float photo. string[] MySampleDicere = Directory.GetFiles("Sample"); MyValue.Count = 0; MyValue.T_Sample = "false"; foreach (string MPD in MyPhotoDicrectory) { MyValue.T_Timeout = false; int time = 0; API_Detect(MPD); while (MyValue.Finish != "OK") { time++; System.Threading.Thread.Sleep(200); if (time == 150) { MyValue.T_Timeout = true; break; } } MyValue.Finish = ""; if (MyValue.T_FindFace == true & MyValue.T_Timeout == false) { int t_count = 0; foreach (Face T_FaceValue in MyValue.TA_FaceValue) { if (t_count == 0) { Console.WriteLine("You have upload {0} successful.", T_FaceValue.Directory_F); } Person.Add(T_FaceValue); t_count++; } W_FileJSON.Append("|"); W_FileJSON.Append(MyValue.TB_FileJSON); MyValue.Count++; } else { if (MyValue.T_Timeout == true) { Console.WriteLine("Time out!!! Fail to upload {0}", MPD); File.Copy(Path.GetFullPath(MPD), "Error\\" + "TimeOut " + MPD.Substring(11), true); } else { Console.WriteLine("We can't find face in file {0}, please recheck this picture. ", MPD.ToString()); } } } MyValue.T_Sample = "true"; foreach (string MSD in MySampleDicere) { MyValue.T_Timeout = false; int time = 0; API_Detect(MSD); while (MyValue.Finish != "OK") { time++; System.Threading.Thread.Sleep(200); if (time == 150) { MyValue.T_Timeout = true; break; } } MyValue.Finish = ""; if (MyValue.T_FindFace == true & MyValue.T_Timeout == false) { int t_count = 0; foreach (Face T_FaceValue in MyValue.TA_FaceValue) { if (t_count == 0) { Console.WriteLine("You have upload {0} successful.", T_FaceValue.Directory_F); } Person.Add(T_FaceValue); t_count++; } W_FileJSON.Append("|"); W_FileJSON.Append(MyValue.TB_FileJSON); MyValue.Count++; } else { if (MyValue.T_Timeout == true) { Console.WriteLine("Time out!!! Fail to upload {0}", MSD); File.Copy(Path.GetFullPath(MSD), "Error\\" + "TimeOut " + MSD.Substring(11), true); } else { Console.WriteLine("We can't find face in file {0}, please recheck this picture. ", MSD.ToString()); } } } Console.WriteLine("***********----------------SUCCESS----------------***********"); foreach (Face people in Person) { Console.WriteLine("Name:{0} || Gender:{1} || Age:{2} || FaceID:{3}", people.Name_F, people.Gender_F, people.Age_F, people.ID_F); } StreamWriter WriteJSON_TXT = new StreamWriter("JSON_Value.txt"); WriteJSON_TXT.Write(W_FileJSON.ToString()); WriteJSON_TXT.Close(); Console.WriteLine("Warning:The FaceID will expire after 24 hour!!!"); return(Person); }
/// <summary> /// Converts the input file into an MPEG DASH representation with multiple bitrates. /// </summary> /// <param name="inFile">The video file to convert.</param> /// <param name="outFilename">The base filename to use for the output files. Files will be overwritten if they exist.</param> /// <param name="framerate">Output video stream framerate. Pass zero to make this automatic based on the input file.</param> /// <param name="keyframeInterval">Output video keyframe interval. Pass zero to make this automatically 3x the framerate.</param> /// <param name="qualities">Parameters to pass to ffmpeg when performing the preparation encoding. Bitrates must be distinct, an exception will be thrown if they are not.</param> /// <param name="options">Options for the ffmpeg encode.</param> /// <param name="outDirectory">The directory to place output files and intermediary files in.</param> /// <param name="progress">A callback for progress events. The collection will contain values with the Name property of "Encode", "DASHify", "Post Process"</param> /// <param name="cancel">Allows cancellation of the operation.</param> /// <returns>An object containing a representation of the generated MPD file, it's path, and the associated filenames, or null if no file generated.</returns> public DashEncodeResult GenerateDash(string inFile, string outFilename, int framerate, int keyframeInterval, IEnumerable <IQuality> qualities, IEncodeOptions options = null, string outDirectory = null, IProgress <IEnumerable <EncodeStageProgress> > progress = null, CancellationToken cancel = default(CancellationToken)) { cancel.ThrowIfCancellationRequested(); options = options ?? new H264EncodeOptions(); outDirectory = outDirectory ?? WorkingDirectory; // Input validation. if (inFile == null || !File.Exists(inFile)) { throw new FileNotFoundException("Input path does not exist."); } if (!Directory.Exists(outDirectory)) { throw new DirectoryNotFoundException("Output directory does not exist."); } if (string.IsNullOrEmpty(outFilename)) { throw new ArgumentNullException("Output filename is null or empty."); } if (qualities == null || qualities.Count() == 0) { throw new ArgumentOutOfRangeException("No qualitied specified. At least one quality is required."); } // Check for invalid characters and remove them. outFilename = RemoveSymbols(outFilename, '#', '&', '*', '<', '>', '/', '?', ':', '"'); // Another check to ensure we didn't remove all the characters. if (outFilename.Length == 0) { throw new ArgumentNullException("Output filename is null or empty."); } // Check bitrate distinction. if (qualities.GroupBy(x => x.Bitrate).Count() != qualities.Count()) { throw new ArgumentOutOfRangeException("Duplicate bitrates found. Bitrates must be distinct."); } var inputStats = ProbeFile(inFile); if (inputStats == null) { throw new NullReferenceException("ffprobe query returned a null result."); } int inputBitrate = (int)(inputStats.Bitrate / 1024); if (!DisableQualityCrushing) { qualities = QualityCrusher.CrushQualities(qualities, inputBitrate); } var compareQuality = qualities.First(); bool enableStreamCopy = EnableStreamCopying && compareQuality.Bitrate == 0 && Copyable264Infer.DetermineCopyCanBeDone(compareQuality.PixelFormat, compareQuality.Level, compareQuality.Profile, inputStats.VideoStreams); var progressList = new List <EncodeStageProgress>() { new EncodeStageProgress("Encode", 0), new EncodeStageProgress("DASHify", 0), new EncodeStageProgress("Post Process", 0) }; const int encodeStage = 0; const int dashStage = 1; const int postStage = 2; var stdErrShim = stderrLog; if (progress != null) { stdErrShim = new Action <string>(x => { stderrLog(x); if (x != null) { var match = Encode.Regexes.ParseProgress.Match(x); if (match.Success && TimeSpan.TryParse(match.Value, out TimeSpan p)) { ReportProgress(progress, progressList, encodeStage, Math.Min(1, (float)(p.TotalMilliseconds / 1000) / inputStats.Duration)); } } }); } framerate = framerate <= 0 ? (int)Math.Round(inputStats.Framerate) : framerate; keyframeInterval = keyframeInterval <= 0 ? framerate * 3 : keyframeInterval; // Build task definitions. var ffmpegCommand = CommandBuilder.BuildFfmpegCommand( inPath: inFile, outDirectory: WorkingDirectory, outFilename: outFilename, options: options, framerate: framerate, keyframeInterval: keyframeInterval, qualities: qualities.OrderByDescending(x => x.Bitrate), metadata: inputStats, defaultBitrate: inputBitrate, enableStreamCopying: enableStreamCopy); cancel.ThrowIfCancellationRequested(); // Generate intermediates try { ExecutionResult ffResult; stderrLog.Invoke($"Running ffmpeg with arguments: {ffmpegCommand.RenderedCommand}"); ffResult = ManagedExecution.Start(FFmpegPath, ffmpegCommand.RenderedCommand, stdoutLog, stdErrShim, cancel); // Detect error in ffmpeg process and cleanup, then return null. if (ffResult.ExitCode != 0) { stderrLog.Invoke($"ERROR: ffmpeg returned code {ffResult.ExitCode}. File: {inFile}"); CleanOutputFiles(ffmpegCommand.CommandPieces.Select(x => x.Path)); return(null); } } catch (Exception ex) { CleanOutputFiles(ffmpegCommand.CommandPieces.Select(x => x.Path)); if (ex is OperationCanceledException) { throw new OperationCanceledException($"Exception running ffmpeg on {inFile}", ex); } else { throw new Exception($"Exception running ffmpeg on {inFile}", ex); } } var audioVideoFiles = ffmpegCommand.CommandPieces.Where(x => x.Type == StreamType.Video || x.Type == StreamType.Audio); var mp4boxCommand = CommandBuilder.BuildMp4boxMpdCommand( inFiles: audioVideoFiles.Select(x => x.Path), outFilePath: Path.Combine(outDirectory, outFilename) + ".mpd", keyInterval: (keyframeInterval / framerate) * 1000); // Generate DASH files. ExecutionResult mpdResult; stderrLog.Invoke($"Running MP4Box with arguments: {mp4boxCommand.RenderedCommand}"); try { mpdResult = ManagedExecution.Start(BoxPath, mp4boxCommand.RenderedCommand, stdoutLog, stderrLog, cancel); } catch (Exception ex) { CleanOutputFiles(audioVideoFiles.Select(x => x.Path)); if (ex is OperationCanceledException) { throw new OperationCanceledException($"Exception running MP4box on {inFile}", ex); } else { throw new Exception($"Exception running MP4box on {inFile}", ex); } } // Report DASH complete. if (mpdResult.ExitCode == 0) { ReportProgress(progress, progressList, dashStage, 1); } // Cleanup intermediates. CleanOutputFiles(audioVideoFiles.Select(x => x.Path)); ReportProgress(progress, progressList, postStage, 0.33); // Move subtitles found in media List <StreamFile> subtitles = new List <StreamFile>(); foreach (var subFile in ffmpegCommand.CommandPieces.Where(x => x.Type == StreamType.Subtitle)) { string oldPath = subFile.Path; subFile.Path = Path.Combine(outDirectory, Path.GetFileName(subFile.Path)); subtitles.Add(subFile); if (oldPath != subFile.Path) { if (File.Exists(subFile.Path)) { File.Delete(subFile.Path); } File.Move(oldPath, subFile.Path); } } // Add external subtitles int originIndex = ffmpegCommand.CommandPieces.Max(x => x.Origin) + 1; string baseFilename = Path.GetFileNameWithoutExtension(inFile); foreach (var vttFile in Directory.EnumerateFiles(Path.GetDirectoryName(inFile), baseFilename + "*", SearchOption.TopDirectoryOnly)) { if (vttFile.EndsWith(".vtt")) { string vttFilename = Path.GetFileName(vttFile); string vttName = GetSubtitleName(vttFilename); string vttOutputPath = Path.Combine(outDirectory, $"{outFilename}_subtitle_{vttName}_{originIndex}.vtt"); var subFile = new StreamFile() { Type = StreamType.Subtitle, Origin = originIndex, Path = vttOutputPath, Name = $"{vttName}_{originIndex}" }; originIndex++; File.Copy(vttFile, vttOutputPath, true); subtitles.Add(subFile); } } ReportProgress(progress, progressList, postStage, 0.66); try { string mpdFilepath = mp4boxCommand.CommandPieces.FirstOrDefault().Path; if (File.Exists(mpdFilepath)) { MPD mpd = PostProcessMpdFile(mpdFilepath, subtitles); var result = new DashEncodeResult(mpd, inputStats.Metadata, TimeSpan.FromMilliseconds((inputStats.VideoStreams.FirstOrDefault()?.duration ?? 0) * 1000), mpdFilepath); // Detect error in MP4Box process and cleanup, then return null. if (mpdResult.ExitCode != 0) { stderrLog.Invoke($"ERROR: MP4Box returned code {mpdResult.ExitCode}. File: {inFile}"); CleanOutputFiles(result.MediaFiles.Select(x => Path.Combine(outDirectory, x))); CleanOutputFiles(mpdResult.Output); return(null); } // Success. return(result); } stderrLog.Invoke($"ERROR: MP4Box did not produce the expected mpd file at path {mpdFilepath}. File: {inFile}"); return(null); } finally { ReportProgress(progress, progressList, postStage, 1); } }
/// <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); } }
/// <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); }