/// <summary> /// Initializes static members of the <see cref="MediaElement"/> class. /// </summary> static MediaElement() { var style = new Style(typeof(MediaElement), null); style.Setters.Add(new Setter(FlowDirectionProperty, FlowDirection.LeftToRight)); style.Seal(); StyleProperty.OverrideMetadata(typeof(MediaElement), new FrameworkPropertyMetadata(style)); // Platform specific implementation Platform.SetDllDirectory = NativeMethods.SetDllDirectory; Platform.CopyMemory = NativeMethods.CopyMemory; Platform.FillMemory = NativeMethods.FillMemory; Platform.CreateTimer = (priority) => { return(new CustomDispatcherTimer((DispatcherPriority)priority)); }; // Setup the Platform-specific factory callbacks Platform.UIInvoke = (priority, action) => WPFUtils.UIInvoke((DispatcherPriority)priority, action); Platform.UIEnqueueInvoke = (priority, action, args) => WPFUtils.UIEnqueueInvoke((DispatcherPriority)priority, action, args); Platform.CreateRenderer = (mediaType, m) => { if (mediaType == MediaType.Audio) { return(new AudioRenderer(m)); } else if (mediaType == MediaType.Video) { return(new VideoRenderer(m)); } else if (mediaType == MediaType.Subtitle) { return(new SubtitleRenderer(m)); } throw new ArgumentException($"No suitable renderer for Media Type '{mediaType}'"); }; // Simply forward the calls MediaElementCore.FFmpegMessageLogged += (o, e) => FFmpegMessageLogged?.Invoke(o, e); }
/// <summary> /// Sets the text to be rendered on the text blocks. /// Returns immediately because it enqueues the action on the UI thread. /// </summary> /// <param name="text">The text.</param> private async void SetText(string text) { if (RenderedText.Equals(text)) { return; } // We fire-and-forget the update of the text await WPFUtils.UIEnqueueInvoke( DispatcherPriority.DataBind, new Action <string>((s) => { lock (SyncLock) { var textBlocks = GetTextBlocks(); foreach (var tb in textBlocks) { tb.Text = s; } RenderedText = s; } }), text); }
/// <summary> /// Renders the specified media block. /// This needs to return immediately so the calling thread is not disturbed. /// </summary> /// <param name="mediaBlock">The media block.</param> /// <param name="clockPosition">The clock position.</param> public async void Render(MediaBlock mediaBlock, TimeSpan clockPosition) { var block = mediaBlock as VideoBlock; if (block == null) { return; } if (IsRenderingInProgress.Value == true) { MediaElement.Logger.Log(MediaLogMessageType.Debug, $"{nameof(VideoRenderer)}: Frame skipped at {mediaBlock.StartTime}"); return; } IsRenderingInProgress.Value = true; await WPFUtils.UIEnqueueInvoke( DispatcherPriority.Render, new Action <VideoBlock, TimeSpan>((b, cP) => { try { // Skip rendering if Scrubbing is not enabled if (MediaElement.ScrubbingEnabled == false && MediaElement.IsPlaying == false) { return; } if (TargetBitmap == null || TargetBitmap.PixelWidth != b.PixelWidth || TargetBitmap.PixelHeight != b.PixelHeight) { InitializeTargetBitmap(b); } var updateRect = new Int32Rect(0, 0, b.PixelWidth, b.PixelHeight); TargetBitmap.WritePixels(updateRect, b.Buffer, b.BufferLength, b.BufferStride); MediaElementCore.VideoSmtpeTimecode = b.SmtpeTimecode; MediaElementCore.VideoHardwareDecoder = (MediaElementCore.Container?.Components?.Video?.IsUsingHardwareDecoding ?? false) ? MediaElementCore.Container?.Components?.Video?.HardwareAccelerator?.Name ?? string.Empty : string.Empty; MediaElement.RaiseRenderingVideoEvent( TargetBitmap, MediaElementCore.Container.MediaInfo.Streams[b.StreamIndex], b.SmtpeTimecode, b.DisplayPictureNumber, b.StartTime, b.Duration, cP); ApplyScaleTransform(b); } catch (Exception ex) { Utils.Log(MediaElement, MediaLogMessageType.Error, $"{nameof(VideoRenderer)} {ex.GetType()}: {ex.Message}. Stack Trace:\r\n{ex.StackTrace}"); } finally { IsRenderingInProgress.Value = false; } }), block, clockPosition); }