public override void Render(MediaBlock mediaBlock, TimeSpan clockPosition) { var block = BeginRenderingCycle(mediaBlock); if (block == null) { return; } if (!block.TryAcquireReaderLock(out var blockLock)) { return; } try { var bitmap = Graphics.Write(block); if (bitmap == null) { return; } MediaElement?.RaiseRenderingVideoEvent(block, bitmap, clockPosition); UpdateTargetImage(DispatcherPriority.Loaded, true); } catch (Exception ex) { this.LogError(Aspects.VideoRenderer, $"{nameof(InteropVideoRenderer)}.{nameof(Render)} bitmap failed.", ex); } finally { blockLock.Dispose(); FinishRenderingCycle(block, clockPosition); } }
private void RenderTarget(VideoBlock block, BitmapDataBuffer bitmapData, TimeSpan clockPosition) { try { if (RaiseVideoEventOnGui) { MediaElement.RaiseRenderingVideoEvent(block, bitmapData, clockPosition); } // Signal an update on the rendering surface TargetBitmap.AddDirtyRect(bitmapData.UpdateRect); TargetBitmap.Unlock(); ApplyScaleTransform(block); } catch (Exception ex) { MediaElement?.MediaCore?.Log( MediaLogMessageType.Error, $"{nameof(VideoRenderer)} {ex.GetType()}: {ex.Message}. Stack Trace:\r\n{ex.StackTrace}"); } finally { IsRenderingInProgress.Value = false; } }
/// <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 void Render(MediaBlock mediaBlock, TimeSpan clockPosition) { var block = mediaBlock as VideoBlock; if (block == null) { return; } if (IsRenderingInProgress.Value == true) { MediaElement?.MediaCore?.Log(MediaLogMessageType.Debug, $"{nameof(VideoRenderer)}: Frame skipped at {mediaBlock.StartTime}"); return; } // Flag the start of a rendering cycle IsRenderingInProgress.Value = true; // Ensure the target bitmap can be loaded GuiContext.Current.EnqueueInvoke(DispatcherPriority.Render, () => { if (block.IsDisposed) { IsRenderingInProgress.Value = false; return; } try { MediaElement.CaptionsView.RenderPacket(block, MediaCore); var bitmapData = LockTargetBitmap(block); if (bitmapData != null) { LoadTargetBitmapBuffer(bitmapData, block); MediaElement.RaiseRenderingVideoEvent(block, bitmapData, clockPosition); RenderTargetBitmap(block, bitmapData, clockPosition); } } catch { /* swallow */ } finally { IsRenderingInProgress.Value = false; } }); }
/// <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 void Render(MediaBlock mediaBlock, TimeSpan clockPosition) { var block = mediaBlock as VideoBlock; if (block == null) { return; } if (IsRenderingInProgress.Value == true) { MediaElement?.MediaCore?.Log(MediaLogMessageType.Debug, $"{nameof(VideoRenderer)}: Frame skipped at {mediaBlock.StartTime}"); return; } // Flag the start of a rendering cycle IsRenderingInProgress.Value = true; // Ensure the target bitmap can be loaded var bitmapData = LockTarget(block, BlockRenderPriority); // Check if we have a valid pointer to the back-buffer if (bitmapData == null) { IsRenderingInProgress.Value = false; return; } // Write the pixels on a non-UI thread if (LoadBlockBufferOnGui == false) { LoadTarget(bitmapData, block); } // Fire the rendering event o a non-UI thread if (RaiseVideoEventOnGui == false) { MediaElement.RaiseRenderingVideoEvent(block, bitmapData, clockPosition); } // Send to the rendering to the UI WindowsPlatform.Instance.Gui?.EnqueueInvoke( BlockRenderPriority, RenderTargetDelegate, block, bitmapData, clockPosition); }
private unsafe void WriteVideoFrameBuffer(VideoBlock block, TimeSpan clockPosition) { var bitmap = TargetBitmap; var target = TargetBitmapData; if (bitmap == null || target == null || block == null || block.IsDisposed || !block.TryAcquireReaderLock(out var readLock)) { return; } // Lock the video block for reading try { // Lock the bitmap bitmap.Lock(); // Compute a safe number of bytes to copy // At this point, we it is assumed the strides are equal var bufferLength = Math.Min(block.BufferLength, target.BufferLength); // Copy the block data into the back buffer of the target bitmap. Buffer.MemoryCopy( block.Buffer.ToPointer(), target.Scan0.ToPointer(), bufferLength, bufferLength); // with the locked video block, raise the rendering video event. MediaElement?.RaiseRenderingVideoEvent(block, target, clockPosition); // Mark the region as dirty so it's updated on the UI bitmap.AddDirtyRect(target.UpdateRect); } finally { readLock.Dispose(); bitmap.Unlock(); } }
private unsafe void WriteVideoFrameBuffer(VideoBlock block, TimeSpan clockPosition) { var bitmap = TargetBitmap; var target = TargetBitmapData; if (bitmap == null || target == null || block == null || block.IsDisposed || !block.TryAcquireReaderLock(out var readLock)) { return; } // Lock the video block for reading using (readLock) { if (!bitmap.TryLock(BitmapLockTimeout)) { this.LogDebug(Aspects.VideoRenderer, $"{nameof(VideoRenderer)} bitmap lock timed out at {clockPosition}"); bitmap.Lock(); } // Compute a safe number of bytes to copy // At this point, we it is assumed the strides are equal var bufferLength = Math.Min(block.BufferLength, target.BufferLength); // Copy the block data into the back buffer of the target bitmap. Buffer.MemoryCopy( block.Buffer.ToPointer(), target.Scan0.ToPointer(), bufferLength, bufferLength); // with the locked video block, raise the rendering video event. MediaElement?.RaiseRenderingVideoEvent(block, TargetBitmapData, clockPosition); } bitmap.AddDirtyRect(TargetBitmapData.UpdateRect); bitmap.Unlock(); }
/// <inheritdoc /> public void Render(MediaBlock mediaBlock, TimeSpan clockPosition) { if (mediaBlock is VideoBlock == false) { return; } var block = (VideoBlock)mediaBlock; if (IsRenderingInProgress.Value) { if (MediaCore?.State.IsPlaying ?? false) { this.LogDebug(Aspects.VideoRenderer, $"{nameof(VideoRenderer)} frame skipped at {mediaBlock.StartTime}"); } return; } // Flag the start of a rendering cycle IsRenderingInProgress.Value = true; // Send the packets to the CC renderer MediaElement?.CaptionsView?.SendPackets(block, MediaCore); // Create an action that holds GUI thread actions var foregroundAction = new Action(() => { MediaElement?.CaptionsView?.Render(MediaElement.ClosedCaptionsChannel, clockPosition); ApplyLayoutTransforms(block); }); var canStartForegroundTask = MediaElement.VideoView.ElementDispatcher != MediaElement.Dispatcher; var foregroundTask = canStartForegroundTask ? MediaElement.Dispatcher.InvokeAsync(foregroundAction) : null; // Ensure the target bitmap can be loaded MediaElement?.VideoView?.InvokeAsync(DispatcherPriority.Render, () => { if (block.IsDisposed) { IsRenderingInProgress.Value = false; return; } // Run the foreground action if we could not start it in parallel. if (foregroundTask == null) { try { foregroundAction(); } catch (Exception ex) { this.LogError(Aspects.VideoRenderer, $"{nameof(VideoRenderer)}.{nameof(Render)} layout/CC failed.", ex); } } try { // Render the bitmap data var bitmapData = LockTargetBitmap(block); if (bitmapData == null) { return; } LoadTargetBitmapBuffer(bitmapData, block); MediaElement.RaiseRenderingVideoEvent(block, bitmapData, clockPosition); RenderTargetBitmap(bitmapData); } catch (Exception ex) { this.LogError(Aspects.VideoRenderer, $"{nameof(VideoRenderer)}.{nameof(Render)} bitmap failed.", ex); } finally { if (foregroundTask != null) { try { foregroundTask.Wait(); } catch (Exception ex) { this.LogError(Aspects.VideoRenderer, $"{nameof(VideoRenderer)}.{nameof(Render)} layout/CC failed.", ex); } } // Always reset the rendering state IsRenderingInProgress.Value = false; } }); }
/// <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); }
/// <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 void Render(MediaBlock mediaBlock, TimeSpan clockPosition) { var block = mediaBlock as VideoBlock; if (block == null) { return; } if (IsRenderingInProgress.Value == true) { MediaElement?.MediaCore?.Log(MediaLogMessageType.Debug, $"{nameof(VideoRenderer)}: Frame skipped at {mediaBlock.StartTime}"); return; } // Flag the start of a rendering cycle IsRenderingInProgress.Value = true; // Create an action that holds GUI thread actions var foregroundAction = new Action(() => { MediaElement.CaptionsView.RenderPacket(block, MediaCore); ApplyLayoutTransforms(block); }); var canStartForegroundTask = MediaElement.VideoView.ElementDispatcher != MediaElement.Dispatcher; var foregroundTask = canStartForegroundTask ? MediaElement.Dispatcher.InvokeAsync(foregroundAction) : null; // Ensure the target bitmap can be loaded MediaElement.VideoView.InvokeAsync(DispatcherPriority.Render, () => { if (block.IsDisposed) { IsRenderingInProgress.Value = false; return; } // Run the foreground action if we could not start it. if (foregroundTask == null) { foregroundAction(); } try { var bitmapData = LockTargetBitmap(block); if (bitmapData != null) { LoadTargetBitmapBuffer(bitmapData, block); MediaElement.RaiseRenderingVideoEvent(block, bitmapData, clockPosition); RenderTargetBitmap(block, bitmapData, clockPosition); } } catch (Exception ex) { MediaElement?.MediaCore?.Log( MediaLogMessageType.Error, $"{nameof(VideoRenderer)} {ex.GetType()}: {nameof(Render)} failed. {ex.Message}."); } finally { foregroundTask?.Wait(); IsRenderingInProgress.Value = false; } }); }