示例#1
0
        internal void RemoveComponent(MediaType mediaType)
        {
            lock (ComponentSyncLock)
            {
                var component = default(MediaComponent);
                if (mediaType == MediaType.Audio)
                {
                    component = m_Audio;
                    m_Audio   = null;
                }
                else if (mediaType == MediaType.Video)
                {
                    component = m_Video;
                    m_Video   = null;
                }
                else if (mediaType == MediaType.Subtitle)
                {
                    component  = m_Subtitle;
                    m_Subtitle = null;
                }

                component?.Dispose();
                UpdateComponentBackingFields();
            }
        }
示例#2
0
 /// <summary>
 /// Initializes a new instance of the <see cref="HardwareAccelerator"/> class.
 /// </summary>
 /// <param name="component">The component this accelerator is attached to.</param>
 /// <param name="selectedConfig">The selected hardware device configuration.</param>
 public HardwareAccelerator(VideoComponent component, HardwareDeviceInfo selectedConfig)
 {
     Component         = component;
     Name              = selectedConfig.DeviceTypeName;
     DeviceType        = selectedConfig.DeviceType;
     PixelFormat       = selectedConfig.PixelFormat;
     GetFormatCallback = GetPixelFormat;
 }
示例#3
0
        internal void AddComponent(MediaComponent component)
        {
            lock (ComponentSyncLock)
            {
                if (component == null)
                {
                    throw new ArgumentNullException(nameof(component));
                }

                var errorMessage = $"A component for '{component.MediaType}' is already registered.";
                switch (component.MediaType)
                {
                case MediaType.Audio:
                    if (m_Audio != null)
                    {
                        throw new ArgumentException(errorMessage);
                    }

                    m_Audio = component as AudioComponent;
                    break;

                case MediaType.Video:
                    if (m_Video != null)
                    {
                        throw new ArgumentException(errorMessage);
                    }

                    m_Video = component as VideoComponent;
                    break;

                case MediaType.Subtitle:
                    if (m_Subtitle != null)
                    {
                        throw new ArgumentException(errorMessage);
                    }

                    m_Subtitle = component as SubtitleComponent;
                    break;

                default:
                    throw new NotSupportedException($"Unable to register component with {nameof(MediaType)} '{component.MediaType}'");
                }

                UpdateComponentBackingFields();
            }
        }
示例#4
0
        /// <summary>
        /// Initializes a new instance of the <see cref="VideoFrame" /> class.
        /// </summary>
        /// <param name="frame">The frame.</param>
        /// <param name="component">The video component.</param>
        public VideoFrame(AVFrame *frame, VideoComponent component)
            : base(frame, component, MediaType.Video)
        {
            var frameRate     = ffmpeg.av_guess_frame_rate(component.Container.InputContext, component.Stream, frame);
            var frameTimeBase = new AVRational {
                num = frameRate.den, den = frameRate.num
            };
            var repeatFactor = 1d + (0.5d * frame->repeat_pict);

            Duration = frame->pkt_duration <= 0 ?
                       repeatFactor.ToTimeSpan(frameTimeBase) :
                       Convert.ToInt64(repeatFactor * frame->pkt_duration).ToTimeSpan(StreamTimeBase);

            // for video frames, we always get the best effort timestamp as dts and pts might
            // contain different times.
            frame->pts = frame->best_effort_timestamp;
            var previousFramePts = component.LastFramePts;

            component.LastFramePts = frame->pts;

            if (previousFramePts != null && previousFramePts.Value == frame->pts)
            {
                HasValidStartTime = false;
            }
            else
            {
                HasValidStartTime = frame->pts != ffmpeg.AV_NOPTS_VALUE;
            }

            StartTime = frame->pts == ffmpeg.AV_NOPTS_VALUE ?
                        TimeSpan.FromTicks(0) :
                        frame->pts.ToTimeSpan(StreamTimeBase);

            EndTime = TimeSpan.FromTicks(StartTime.Ticks + Duration.Ticks);

            // Picture Type, Number and SMTPE TimeCode
            PictureType          = frame->pict_type;
            DisplayPictureNumber = frame->display_picture_number == 0 ?
                                   Utilities.ComputePictureNumber(component.StartTime, StartTime, frameRate) :
                                   frame->display_picture_number;

            CodedPictureNumber      = frame->coded_picture_number;
            SmtpeTimeCode           = Utilities.ComputeSmtpeTimeCode(DisplayPictureNumber, frameRate);
            IsHardwareFrame         = component.IsUsingHardwareDecoding;
            HardwareAcceleratorName = component.HardwareAccelerator?.Name;
        }
示例#5
0
        /// <summary>
        /// Initializes a new instance of the <see cref="VideoFrame" /> class.
        /// </summary>
        /// <param name="frame">The frame.</param>
        /// <param name="component">The video component.</param>
        internal VideoFrame(AVFrame *frame, VideoComponent component)
            : base(frame, component, MediaType.Video)
        {
            var frameRate     = ffmpeg.av_guess_frame_rate(component.Container.InputContext, component.Stream, frame);
            var frameTimeBase = new AVRational {
                num = frameRate.den, den = frameRate.num
            };
            var repeatFactor = 1d + (0.5d * frame->repeat_pict);

            Duration = frame->pkt_duration <= 0 ?
                       repeatFactor.ToTimeSpan(frameTimeBase) :
                       Convert.ToInt64(repeatFactor * frame->pkt_duration).ToTimeSpan(StreamTimeBase);

            // for video frames, we always get the best effort timestamp as dts and pts might
            // contain different times.
            frame->pts = frame->best_effort_timestamp;
            var previousFramePts = component.LastFramePts;

            component.LastFramePts = frame->pts;

            if (previousFramePts != null && previousFramePts.Value == frame->pts)
            {
                HasValidStartTime = false;
            }
            else
            {
                HasValidStartTime = frame->pts != ffmpeg.AV_NOPTS_VALUE;
            }

            StartTime = frame->pts == ffmpeg.AV_NOPTS_VALUE ?
                        TimeSpan.FromTicks(0) :
                        frame->pts.ToTimeSpan(StreamTimeBase);

            EndTime = TimeSpan.FromTicks(StartTime.Ticks + Duration.Ticks);

            // Picture Type, Number and SMTPE TimeCode
            PictureType          = frame->pict_type;
            DisplayPictureNumber = frame->display_picture_number == 0 ?
                                   Utilities.ComputePictureNumber(component.StartTime, StartTime, frameRate) :
                                   frame->display_picture_number;

            CodedPictureNumber      = frame->coded_picture_number;
            SmtpeTimeCode           = Utilities.ComputeSmtpeTimeCode(DisplayPictureNumber, frameRate);
            IsHardwareFrame         = component.IsUsingHardwareDecoding;
            HardwareAcceleratorName = component.HardwareAccelerator?.Name;

            // Process side data such as CC packets
            for (var i = 0; i < frame->nb_side_data; i++)
            {
                var sideData = frame->side_data[i];

                // Get the Closed-Caption packets
                if (sideData->type != AVFrameSideDataType.AV_FRAME_DATA_A53_CC)
                {
                    continue;
                }

                // Parse 3 bytes at a time
                for (var p = 0; p < sideData->size; p += 3)
                {
                    var packet = new ClosedCaptionPacket(TimeSpan.FromTicks(StartTime.Ticks + p), sideData->data, p);
                    if (packet.PacketType == CaptionsPacketType.NullPad || packet.PacketType == CaptionsPacketType.Unrecognized)
                    {
                        continue;
                    }

                    // at this point, we have valid CC data
                    ClosedCaptions.Add(packet);
                }
            }
        }