Ejemplo n.º 1
0
        /// <summary>
        /// Generate single encoded video stream based on JOB and OUTPUT DEFINITION
        /// </summary>
        /// <param name="v">Defines the video stream being generated</param>
        /// <param name="j">The overall job for this encode run</param>
        /// <param name="xEncoder">libx264 or libx265</param>
        public void EncodeX(VideoOutputDefinition v, VideoEncodeJob j, string xEncoder)
        {
            MediaProperties ma = j.InputMedia[0];
            MediaStream     vs = ma.Streams[ma.FirstVideoIndex];

            //implicitly set frame rate.
            //To do: this needs to be rationals, not floats
            var rateString = vs.VideoFrameRate > 0 ? $"-r {((j.MatchRate != 0) ? j.MatchRate : vs.VideoFrameRate)}" : String.Empty;

            //Select proper prefix based on input media.
            //MPEG files require a specific syntax on the input to prevent time-code issues.
            var FFRoot = this.MpegFiles.Contains(Path.GetExtension(ma.MediaFile).ToLower()) ? this.FFRootCmdMpg : this.FFRootCmdDefault;

            FilterChain f = this.ConfigureFilters(v, j);

            for (int pass = 1; pass <= v.NumPasses; pass++)
            {
                var FFCmd = $"{FFRoot} -i {j.InputMedia[0].MediaFile} {f.GetVideoFilters()} " +
                            $"-pix_fmt {this.PixelFormatMap[j.PixelFmt]} " +
                            $"-preset {this.EncoderPresetMap[j.Preset]} " +
                            $"-an -c:v lib{xEncoder} " +
                            $"-metadata:s:v:0 language={j.Language} " +
                            //ToDo: this needs to be a rational like "24000/1001" to prevent rate drift during sitching or concetnation.
                            $"{rateString} " +
                            $"{this.BuildX26xEncOptions(v, j, pass, xEncoder)} " +
                            $"{Path.Combine(j.OutputFolder, v.OutputFileName)}";
                //Run
                this.utils.LogProxy($"\r\nSTARTING {xEncoder} Encode pass {pass} of {v.NumPasses}", Utilities.LogLevel.Info);
                this.ExecuteFFMpeg_LogProgress(FFCmd, ma.MediaDuration);
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Derives x264/x265 options from JOB and OUTPUT DEFINITION
        /// This only supports the parameters that are common between x264 and x265 and relies in their shared syntax.
        /// VideoOutputDefinition.EncoderOptionOverride.Add() is used for unique encoder specific overrides.
        /// </summary>
        /// <param name="v">Defines the video stream being generated</param>
        /// <param name="j">The overall job for this encode run</param>
        /// <param name="pass">pass number of multi pass encode</param>
        /// <param name="xEncoder">encoder to use</param>
        protected string BuildX26xEncOptions(VideoOutputDefinition v, VideoEncodeJob j, int pass, string xEncoder)
        {
            MediaProperties ma        = j.InputMedia[0];
            MediaStream     vs        = ma.Streams[ma.FirstVideoIndex];
            var             statsFile = $"{Path.GetFileNameWithoutExtension(v.OutputFileName)}_STATS";

            //Scene detection breaks GOP alignment across streams. Disable by default.
            //May be re-enabled at the stream level, for example for DONWLOAD encodes.
            var sceneDetection = v.AllowSceneDetection ? "" : "scenecut=0:";

            //If job does not have color space for output implicitly set, derive it from source media.
            var ColorArgs = (j.ColorSpec == VideoEncodeJob.OutputColorSpec.Unknown) ? this.getX26xColorSpaceArgs(vs) : this.ColorSpaceMap[j.ColorSpec];

            var args = $"-{xEncoder}-params \"" +
                       $"bitrate={v.TargetBitrate}:" +
                       $"vbv-maxrate={v.PeakBitrate}:" +
                       $"vbv-bufsize={v.VBVBufferSize}:" +
                       $"min-keyint={Convert.ToInt32(j.GopLengthSeconds * vs.VideoFrameRate)}:" +
                       $"keyint={Convert.ToInt32(j.GopLengthSeconds * vs.VideoFrameRate)}:" +
                       $"rc-lookahead={j.LookAheadFrames}:" +
                       "open-gop=0:" +  //GOPS must always be closed
                       $"{sceneDetection}" +
                       $"pass={pass}:" +
                       $"stats=\"{statsFile}\":" +
                       $"{ColorArgs}";

            //handle HDR settings.
            //HDR is only supported when the output is HEVC

            if (xEncoder == "x265" && j.HdrMasteringDisplayPrimaries != null)
            {
                args += $":{this.Getx26xHdrString(j)}";
            }

            foreach (string overrideOption in v.EncoderOptionOverride)
            {
                args += $":{overrideOption}";
            }

            args += "\"";

            return(args);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Manages filters used to process video.
        /// Scaling
        /// De-interlacing
        /// Cropping
        /// Matching to master (for stitching scenarios, IE: dub cards)
        /// Burning in sub-titles.
        /// </summary>
        /// <param name="outputVideo">Defines the video stream being generated</param>
        /// <param name="videoEncodeJob">The overall job for this encode run</param>
        protected FilterChain ConfigureFilters(VideoOutputDefinition outputVideo, VideoEncodeJob videoEncodeJob)
        {
            //Video stream
            MediaProperties videoProperties = videoEncodeJob.InputMedia[0];
            MediaStream     inputVideo      = videoProperties.Streams[videoProperties.FirstVideoIndex];
            FilterChain     filters         = new FilterChain();

            //Derived from desired width, source aspect and cropping options.
            var TargetOutputHeight = 0;

            if (!String.IsNullOrEmpty(videoProperties.FFCropFilter) && videoEncodeJob.AutoCrop)
            {
                //must crop before any scaling happens...
                filters.AddFilter(videoProperties.FFCropFilter);

                if (inputVideo.VideoPixelAspect != 1)
                {
                    inputVideo.SquareVideoWidth  = Convert.ToInt32(videoProperties.cropValue.XExtent * inputVideo.VideoPixelAspect);
                    inputVideo.SquareVideoHeight = videoProperties.cropValue.YExtent;
                }
                else
                {
                    inputVideo.SquareVideoWidth  = videoProperties.cropValue.XExtent;
                    inputVideo.SquareVideoHeight = videoProperties.cropValue.YExtent;
                }
            }
            else
            {
                if (inputVideo.VideoPixelAspect != 1)
                {
                    inputVideo.SquareVideoWidth = Convert.ToInt32(inputVideo.VideoWidth * inputVideo.VideoPixelAspect);
                }
            }

            //De-interlace filter behavior.
            //Must happen before any scaling operations.
            //Only invoke on HD and SD at non-film frame rates
            if (videoProperties.HasCombing && inputVideo.SquareVideoWidth <= 1920 && inputVideo.VideoFrameRate > 24)
            {
                if (videoEncodeJob.DeintOverride != VideoEncodeJob.DeinterlaceOverride.None)
                {
                    //The job manager may desire to override behavior.
                    //For example, in the event where the main feature is cleanly telecined film
                    //but the credits are combed video. HBO LatAm does this.
                    filters.AddFilter(this.DeinterlaceOverrideMap[videoEncodeJob.DeintOverride]);
                }
                else if (videoProperties.IsMixedFilmVideo)
                {
                    //Typical: 30i animation with some telecine segments.
                    //Preserve original frame rate. Inverse telecine film.
                    //De-comb video. There will be judder in the film segments.
                    filters.AddFilter(FFBase.DeintFilter_VideoBias);
                }
                else if (videoProperties.IsPureFilm)
                {
                    //Old telecined content. Rare.
                    filters.AddFilter(FFBase.DeintFilter_PureTelecine);
                }
                else if (videoProperties.IsPureVideo)
                {
                    //Talk shows. Next day TV. Old sitcoms.
                    //This filter setting is slow, but yields good results.
                    filters.AddFilter(FFBase.DeintFilter_PureVideoSD);
                }
            }

            if (videoEncodeJob.MatchWidth != 0 && videoEncodeJob.MatchHeight != 0)
            {
                //Job indicates matching to master source.
                filters.AddFilter(this.GetScaleMatchFilter(inputVideo.VideoPixelAspect == 1, videoEncodeJob.MatchWidth, videoEncodeJob.MatchHeight));
                TargetOutputHeight = outputVideo.Width * videoEncodeJob.MatchHeight / videoEncodeJob.MatchWidth;
                TargetOutputHeight = (TargetOutputHeight % 2 == 0) ? TargetOutputHeight : ++TargetOutputHeight;
                filters.AddFilter($"scale={outputVideo.Width}:{TargetOutputHeight}");
            }
            else if (outputVideo.Width != inputVideo.SquareVideoWidth || inputVideo.VideoPixelAspect != 1)
            {
                //Input is either non-square or does not match desired output
                TargetOutputHeight = outputVideo.Width * inputVideo.SquareVideoHeight / inputVideo.SquareVideoWidth;
                TargetOutputHeight = (TargetOutputHeight % 2 == 0) ? TargetOutputHeight : ++TargetOutputHeight;
                filters.AddFilter($"scale={outputVideo.Width}:{TargetOutputHeight}");
            }

            //Set source pixel aspect
            filters.AddFilter("setsar=1/1");

            //Sub titles go last to ensure some degree of readability on low res streams
            if (!String.IsNullOrEmpty(videoEncodeJob.BurnSubs))
            {
                //Need to escape characters so the ASS filter will be happy.
                videoEncodeJob.BurnSubs = videoEncodeJob.BurnSubs.Replace(@"\", @"\\");
                videoEncodeJob.BurnSubs = videoEncodeJob.BurnSubs.Replace(@":", @"\:");

                filters.AddFilter($"ass='{videoEncodeJob.BurnSubs}'");
            }

            return(filters);
        }
Ejemplo n.º 4
0
 /// <summary>
 /// Adds output definition to job.
 /// </summary>
 /// <param name="v">VideoOutputDefinition describing one video stream</param>
 public void AddOutputMedia(VideoOutputDefinition v)
 {
     //Any pre--add work goes here...
     this.OutputMedia.Add(v);
 }