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(); } }
/// <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; }
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(); } }
/// <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; }
/// <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); } } }