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);
            }
        }
예제 #2
0
        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;
            }
        }
예제 #3
0
        /// <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;
                }
            });
        }
예제 #4
0
        /// <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);
        }
예제 #5
0
        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();
            }
        }
예제 #6
0
        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();
        }
예제 #7
0
        /// <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;
                }
            });
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
        /// <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;
                }
            });
        }