Ejemplo n.º 1
0
        /// <summary>
        /// Converts decoded, raw frame data in the frame source into a a usable frame. <br />
        /// The process includes performing picture, samples or text conversions
        /// so that the decoded source frame data is easily usable in multimedia applications
        /// </summary>
        /// <param name="input">The source frame to use as an input.</param>
        /// <param name="output">The target frame that will be updated with the source frame. If null is passed the frame will be instantiated.</param>
        /// <param name="siblings">The siblings to help guess additional frame parameters.</param>
        /// <returns>
        /// Return the updated output frame
        /// </returns>
        /// <exception cref="System.ArgumentNullException">input</exception>
        public override MediaBlock MaterializeFrame(MediaFrame input, ref MediaBlock output, List <MediaBlock> siblings)
        {
            if (output == null)
            {
                output = new VideoBlock();
            }
            var source = input as VideoFrame;
            var target = output as VideoBlock;

            if (source == null || target == null)
            {
                throw new ArgumentNullException($"{nameof(input)} and {nameof(output)} are either null or not of a compatible media type '{MediaType}'");
            }

            // Retrieve a suitable scaler or create it on the fly
            Scaler = ffmpeg.sws_getCachedContext(
                Scaler,
                source.Pointer->width,
                source.Pointer->height,
                NormalizePixelFormat(source.Pointer),
                source.Pointer->width,
                source.Pointer->height,
                OutputPixelFormat,
                ScalerFlags,
                null,
                null,
                null);
            RC.Current.Add(Scaler, $"311: {nameof(VideoComponent)}.{nameof(MaterializeFrame)}()");

            // Perform scaling and save the data to our unmanaged buffer pointer
            var targetBufferStride = ffmpeg.av_image_get_linesize(OutputPixelFormat, source.Pointer->width, 0);
            var targetStride       = new int[] { targetBufferStride };
            var targetLength       = ffmpeg.av_image_get_buffer_size(OutputPixelFormat, source.Pointer->width, source.Pointer->height, 1);

            // Ensure proper allocation of the buffer
            // If there is a size mismatch between the wanted buffer length and the existing one,
            // then let's reallocate the buffer and set the new size (dispose of the existing one if any)
            if (target.PictureBufferLength != targetLength)
            {
                if (target.PictureBuffer != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(target.PictureBuffer);
                }

                target.PictureBufferLength = targetLength;
                target.PictureBuffer       = Marshal.AllocHGlobal(target.PictureBufferLength);
            }

            var targetScan = default(byte_ptrArray8);

            targetScan[0] = (byte *)target.PictureBuffer;

            // The scaling is done here
            var outputHeight = ffmpeg.sws_scale(Scaler, source.Pointer->data, source.Pointer->linesize, 0, source.Pointer->height, targetScan, targetStride);

            // Flag the block if we have to
            target.IsStartTimeGuessed = source.HasValidStartTime == false;

            // Try to fix the start time, duration and End time if we don't have valid data
            if (source.HasValidStartTime == false && siblings != null && siblings.Count > 0)
            {
                // Get timing information from the last sibling
                var lastSibling = siblings[siblings.Count - 1];

                // We set the target properties
                target.StartTime = lastSibling.EndTime;
                target.Duration  = source.Duration.Ticks > 0 ? source.Duration : lastSibling.Duration;
                target.EndTime   = TimeSpan.FromTicks(target.StartTime.Ticks + target.Duration.Ticks);
            }
            else
            {
                // We set the target properties directly from the source
                target.StartTime = source.StartTime;
                target.Duration  = source.Duration;
                target.EndTime   = source.EndTime;
            }

            target.StreamIndex          = input.StreamIndex;
            target.SmtpeTimecode        = source.SmtpeTimecode;
            target.DisplayPictureNumber = source.DisplayPictureNumber;
            target.CodedPictureNumber   = source.DisplayPictureNumber;
            target.BufferStride         = targetStride[0];

            target.PixelHeight = source.Pointer->height;
            target.PixelWidth  = source.Pointer->width;

            var aspectRatio = source.Pointer->sample_aspect_ratio;

            if (aspectRatio.num == 0 || aspectRatio.den == 0)
            {
                target.AspectWidth  = 1;
                target.AspectHeight = 1;
            }
            else
            {
                target.AspectWidth  = aspectRatio.num;
                target.AspectHeight = aspectRatio.den;
            }

            return(target);
        }
        /// <inheritdoc />
        public override bool MaterializeFrame(MediaFrame input, ref MediaBlock output, MediaBlock previousBlock)
        {
            if (output == null)
            {
                output = new VideoBlock();
            }
            if (input is VideoFrame == false || output is VideoBlock == false)
            {
                throw new ArgumentNullException($"{nameof(input)} and {nameof(output)} are either null or not of a compatible media type '{MediaType}'");
            }

            var source = (VideoFrame)input;
            var target = (VideoBlock)output;

            // Retrieve a suitable scaler or create it on the fly
            var newScaler = ffmpeg.sws_getCachedContext(
                Scaler,
                source.Pointer->width,
                source.Pointer->height,
                NormalizePixelFormat(source.Pointer),
                source.Pointer->width,
                source.Pointer->height,
                Constants.Video.VideoPixelFormat,
                ScalerFlags,
                null,
                null,
                null);

            // if it's the first time we set the scaler, simply assign it.
            if (Scaler == null)
            {
                Scaler = newScaler;
                RC.Current.Add(Scaler);
            }

            // Reassign to the new scaler and remove the reference to the existing one
            // The get cached context function automatically frees the existing scaler.
            if (Scaler != newScaler)
            {
                RC.Current.Remove(Scaler);
                Scaler = newScaler;
            }

            // Perform scaling and save the data to our unmanaged buffer pointer
            if (target.Allocate(source, Constants.Video.VideoPixelFormat) &&
                target.TryAcquireWriterLock(out var writeLock))
            {
                using (writeLock)
                {
                    var targetStride = new[] { target.PictureBufferStride };
                    var targetScan   = default(byte_ptrArray8);
                    targetScan[0] = (byte *)target.Buffer;

                    // The scaling is done here
                    var outputHeight = ffmpeg.sws_scale(
                        Scaler,
                        source.Pointer->data,
                        source.Pointer->linesize,
                        0,
                        source.Pointer->height,
                        targetScan,
                        targetStride);

                    if (outputHeight <= 0)
                    {
                        return(false);
                    }
                }
            }
            else
            {
                return(false);
            }

            // After scaling, we need to copy and guess some of the block properties
            // Flag the block if we have to
            target.IsStartTimeGuessed = source.HasValidStartTime == false;

            // Try to fix the start time, duration and End time if we don't have valid data
            if (source.HasValidStartTime == false && previousBlock != null)
            {
                // Get timing information from the previous block
                target.StartTime = TimeSpan.FromTicks(previousBlock.EndTime.Ticks + 1);
                target.Duration  = source.Duration.Ticks > 0 ? source.Duration : previousBlock.Duration;
                target.EndTime   = TimeSpan.FromTicks(target.StartTime.Ticks + target.Duration.Ticks);

                // Guess picture number and SMTPE
                var timeBase = ffmpeg.av_guess_frame_rate(Container.InputContext, Stream, source.Pointer);
                target.DisplayPictureNumber = Extensions.ComputePictureNumber(target.StartTime, target.Duration, 1);
                target.SmtpeTimeCode        = Extensions.ComputeSmtpeTimeCode(StartTime, target.Duration, timeBase, target.DisplayPictureNumber);
            }
            else
            {
                // We set the target properties directly from the source
                target.StartTime = source.StartTime;
                target.Duration  = source.Duration;
                target.EndTime   = source.EndTime;

                // Copy picture number and SMTPE
                target.DisplayPictureNumber = source.DisplayPictureNumber;
                target.SmtpeTimeCode        = source.SmtpeTimeCode;
            }

            // Fill out other properties
            target.IsHardwareFrame         = source.IsHardwareFrame;
            target.HardwareAcceleratorName = source.HardwareAcceleratorName;
            target.CompressedSize          = source.CompressedSize;
            target.CodedPictureNumber      = source.CodedPictureNumber;
            target.StreamIndex             = source.StreamIndex;
            target.ClosedCaptions          = new ReadOnlyCollection <ClosedCaptionPacket>(source.ClosedCaptions);

            // Update the stream info object if we get Closed Caption Data
            if (StreamInfo.HasClosedCaptions == false && target.ClosedCaptions.Count > 0)
            {
                StreamInfo.HasClosedCaptions = true;
            }

            // Process the aspect ratio
            var aspectRatio = ffmpeg.av_guess_sample_aspect_ratio(Container.InputContext, Stream, source.Pointer);

            if (aspectRatio.num == 0 || aspectRatio.den == 0)
            {
                target.PixelAspectWidth  = 1;
                target.PixelAspectHeight = 1;
            }
            else
            {
                target.PixelAspectWidth  = aspectRatio.num;
                target.PixelAspectHeight = aspectRatio.den;
            }

            return(true);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Converts decoded, raw frame data in the frame source into a a usable frame. <br />
        /// The process includes performing picture, samples or text conversions
        /// so that the decoded source frame data is easily usable in multimedia applications
        /// </summary>
        /// <param name="input">The source frame to use as an input.</param>
        /// <param name="output">The target frame that will be updated with the source frame. If null is passed the frame will be instantiated.</param>
        /// <returns>
        /// Return the updated output frame
        /// </returns>
        /// <exception cref="System.ArgumentNullException">input</exception>
        internal override MediaBlock MaterializeFrame(MediaFrame input, ref MediaBlock output)
        {
            if (output == null)
            {
                output = new VideoBlock();
            }
            var source = input as VideoFrame;
            var target = output as VideoBlock;

            if (source == null || target == null)
            {
                throw new ArgumentNullException($"{nameof(input)} and {nameof(output)} are either null or not of a compatible media type '{MediaType}'");
            }

            // Retrieve a suitable scaler or create it on the fly
            Scaler = ffmpeg.sws_getCachedContext(Scaler,
                                                 source.Pointer->width, source.Pointer->height, GetPixelFormat(source.Pointer),
                                                 source.Pointer->width, source.Pointer->height,
                                                 OutputPixelFormat, ScalerFlags, null, null, null);
            RC.Current.Add(Scaler, $"311: {nameof(VideoComponent)}.{nameof(MaterializeFrame)}()");

            // Perform scaling and save the data to our unmanaged buffer pointer
            var targetBufferStride = ffmpeg.av_image_get_linesize(OutputPixelFormat, source.Pointer->width, 0);
            var targetStride       = new int[] { targetBufferStride };
            var targetLength       = ffmpeg.av_image_get_buffer_size(OutputPixelFormat, source.Pointer->width, source.Pointer->height, 1);

            // Ensure proper allocation of the buffer
            // If there is a size mismatch between the wanted buffer length and the existing one,
            // then let's reallocate the buffer and set the new size (dispose of the existing one if any)
            if (target.PictureBufferLength != targetLength)
            {
                if (target.PictureBuffer != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(target.PictureBuffer);
                }

                target.PictureBufferLength = targetLength;
                target.PictureBuffer       = Marshal.AllocHGlobal(target.PictureBufferLength);
            }

            var targetScan = new byte_ptrArray8();

            targetScan[0] = (byte *)target.PictureBuffer;

            // The scaling is done here
            var outputHeight = ffmpeg.sws_scale(Scaler, source.Pointer->data, source.Pointer->linesize, 0, source.Pointer->height, targetScan, targetStride);

            // We set the target properties
            target.EndTime      = source.EndTime;
            target.StartTime    = source.StartTime;
            target.BufferStride = targetStride[0];
            target.Duration     = source.Duration;
            target.PixelHeight  = source.Pointer->height;
            target.PixelWidth   = source.Pointer->width;

            var aspectRatio = source.Pointer->sample_aspect_ratio;

            if (aspectRatio.num == 0 || aspectRatio.den == 0)
            {
                target.AspectWidth  = 1;
                target.AspectHeight = 1;
            }
            else
            {
                target.AspectWidth  = aspectRatio.num;
                target.AspectHeight = aspectRatio.den;
            }

            return(target);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Converts decoded, raw frame data in the frame source into a a usable frame. <br />
        /// The process includes performing picture, samples or text conversions
        /// so that the decoded source frame data is easily usable in multimedia applications
        /// </summary>
        /// <param name="input">The source frame to use as an input.</param>
        /// <param name="output">The target frame that will be updated with the source frame. If null is passed the frame will be instantiated.</param>
        /// <param name="siblings">The siblings to help guess additional frame parameters.</param>
        /// <returns>
        /// Return the updated output frame
        /// </returns>
        /// <exception cref="ArgumentNullException">input</exception>
        public override MediaBlock MaterializeFrame(MediaFrame input, ref MediaBlock output, List <MediaBlock> siblings)
        {
            if (output == null)
            {
                output = new VideoBlock();
            }
            var source = input as VideoFrame;
            var target = output as VideoBlock;

            if (source == null || target == null)
            {
                throw new ArgumentNullException($"{nameof(input)} and {nameof(output)} are either null or not of a compatible media type '{MediaType}'");
            }

            // Retrieve a suitable scaler or create it on the fly
            var newScaler = ffmpeg.sws_getCachedContext(
                Scaler,
                source.Pointer->width,
                source.Pointer->height,
                NormalizePixelFormat(source.Pointer),
                source.Pointer->width,
                source.Pointer->height,
                Constants.Video.VideoPixelFormat,
                ScalerFlags,
                null,
                null,
                null);

            // if it's the first time we set the scaler, simply assign it.
            if (Scaler == null)
            {
                Scaler = newScaler;
                RC.Current.Add(Scaler, $"311: {nameof(VideoComponent)}.{nameof(MaterializeFrame)}()");
            }

            // Reassign to the new scaler and remove the reference to the existing one
            // The get cached context function automatically frees the existing scaler.
            if (Scaler != newScaler)
            {
                RC.Current.Remove(Scaler);
                Scaler = newScaler;
            }

            // Perform scaling and save the data to our unmanaged buffer pointer
            target.EnsureAllocated(source, Constants.Video.VideoPixelFormat);
            var targetStride = new int[] { target.PictureBufferStride };
            var targetScan   = default(byte_ptrArray8);

            targetScan[0] = (byte *)target.PictureBuffer;

            // The scaling is done here
            var outputHeight = ffmpeg.sws_scale(
                Scaler,
                source.Pointer->data,
                source.Pointer->linesize,
                0,
                source.Pointer->height,
                targetScan,
                targetStride);

            // After scaling, we need to copy and guess some of the block properties
            // Flag the block if we have to
            target.IsStartTimeGuessed = source.HasValidStartTime == false;

            // Try to fix the start time, duration and End time if we don't have valid data
            if (source.HasValidStartTime == false && siblings != null && siblings.Count > 0)
            {
                // Get timing information from the last sibling
                var lastSibling = siblings[siblings.Count - 1];

                // We set the target properties
                target.StartTime = lastSibling.EndTime;
                target.Duration  = source.Duration.Ticks > 0 ? source.Duration : lastSibling.Duration;
                target.EndTime   = TimeSpan.FromTicks(target.StartTime.Ticks + target.Duration.Ticks);

                // Guess picture number and SMTPE
                var timeBase = ffmpeg.av_guess_frame_rate(Container.InputContext, Stream, source.Pointer);
                target.DisplayPictureNumber = Extensions.ComputePictureNumber(target.StartTime, target.Duration, 1);
                target.SmtpeTimecode        = Extensions.ComputeSmtpeTimeCode(StartTimeOffset, target.Duration, timeBase, target.DisplayPictureNumber);
            }
            else
            {
                // We set the target properties directly from the source
                target.StartTime = source.StartTime;
                target.Duration  = source.Duration;
                target.EndTime   = source.EndTime;

                // Copy picture number and SMTPE
                target.DisplayPictureNumber = source.DisplayPictureNumber;
                target.SmtpeTimecode        = source.SmtpeTimecode;
            }

            // Fill out other properties
            target.CodedPictureNumber = source.CodedPictureNumber;
            target.StreamIndex        = source.StreamIndex;
            target.ClosedCaptions     = new ReadOnlyCollection <ClosedCaptions.ClosedCaptionPacket>(source.ClosedCaptions);

            // Process the aspect ratio
            var aspectRatio = source.Pointer->sample_aspect_ratio;

            if (aspectRatio.num == 0 || aspectRatio.den == 0)
            {
                target.AspectWidth  = 1;
                target.AspectHeight = 1;
            }
            else
            {
                target.AspectWidth  = aspectRatio.num;
                target.AspectHeight = aspectRatio.den;
            }

            return(target);
        }