예제 #1
0
 /// <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;
 }
예제 #2
0
 /// <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;
 }
예제 #3
0
파일: Encoder.cs 프로젝트: tohoff82/DEnc
        /// <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);
        }
예제 #4
0
    private void ParseMPD(string path, bool UseDebug)
    {
        MPD parser = new MPD();

        parser.UseDebug = UseDebug;
        parser.Parse(path);
        parser.BuildPrefab();
    }
예제 #5
0
        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);
        }
예제 #6
0
파일: Encoder.cs 프로젝트: tohoff82/DEnc
        /// <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}");
        }
예제 #7
0
        /// <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);
        }
예제 #8
0
    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;
        }
    }
예제 #9
0
        /// <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);
        }
예제 #10
0
        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);
        }
예제 #11
0
        /// <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);
            }
        }
예제 #12
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);
            }
        }
예제 #13
0
파일: Encoder.cs 프로젝트: tohoff82/DEnc
        /// <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);
        }