/// <summary> /// Sets the entry thumbnail. /// Deletes the prior thumbnail file is found or previously set. /// </summary> /// <param name="mediaUrl">The media URL.</param> /// <param name="bitmap">The bitmap.</param> public void AddOrUpdateEntryThumbnail(string mediaUrl, BitmapDataBuffer bitmap) { lock (SyncRoot) { var entry = FindEntryByMediaUrl(mediaUrl); if (entry == null) { return; } if (entry.Thumbnail != null) { var existingThumbnailFilePath = Path.Combine(ViewModel.ThumbsDirectory, entry.Thumbnail); if (File.Exists(existingThumbnailFilePath)) { File.Delete(existingThumbnailFilePath); } entry.Thumbnail = null; } using (var bmp = bitmap.CreateDrawingBitmap()) { entry.Thumbnail = ThumbnailGenerator.SnapThumbnail(bmp, ViewModel.ThumbsDirectory); } } }
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; } }
public unsafe BitmapDataBuffer Write(VideoBlock block) { lock (SyncLock) { if (IsDisposed) { return(null); } EnsureBuffers(block); // 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, BackBufferView.Capacity); var scan0 = BackBufferView.SafeMemoryMappedViewHandle.DangerousGetHandle(); // Copy the block data into the back buffer of the target bitmap. Buffer.MemoryCopy( block.Buffer.ToPointer(), scan0.ToPointer(), bufferLength, bufferLength); if (BitmapData == null || BitmapData.Scan0 != scan0) { BitmapData = new BitmapDataBuffer( scan0, block.PictureBufferStride, block.PixelWidth, block.PixelHeight, Parent.DpiX, Parent.DpiY); } BackBufferView.Flush(); return(BitmapData); } }
/// <summary> /// Initializes the target bitmap if not available and locks it for loading the back-buffer. /// </summary> /// <param name="block">The block.</param> /// <param name="priority">The priority.</param> /// <returns> /// The locking result. Returns a null pointer on back buffer for invalid. /// </returns> private BitmapDataBuffer LockTarget(VideoBlock block, DispatcherPriority priority) { // Result will be set on the GUI thread BitmapDataBuffer result = null; WindowsPlatform.Instance.Gui?.Invoke(priority, () => { // Skip the locking if scrubbing is not enabled if (MediaElement.ScrubbingEnabled == false && MediaElement.IsPlaying == false) { return; } // Figure out what we need to do var needsCreation = TargetBitmap == null && MediaElement.HasVideo; var needsModification = needsCreation == false && (TargetBitmap.PixelWidth != block.PixelWidth || TargetBitmap.PixelHeight != block.PixelHeight); var hasValidDimensions = block.PixelWidth > 0 && block.PixelHeight > 0; // Instantiate or update the target bitmap if ((needsCreation || needsModification) && hasValidDimensions) { TargetBitmap = new WriteableBitmap( block.PixelWidth, block.PixelHeight, DpiX, DpiY, MediaPixelFormats[Constants.Video.VideoPixelFormat], null); } else if (hasValidDimensions == false) { TargetBitmap = null; } // Update the target ViewBox image if not already set if (MediaElement.VideoView.Source != TargetBitmap) { MediaElement.VideoView.Source = TargetBitmap; } // Don't set the result if (TargetBitmap == null) { return; } // Lock the back-buffer and create a pointer to it TargetBitmap.Lock(); result = BitmapDataBuffer.FromWriteableBitmap(TargetBitmap); if (LoadBlockBufferOnGui) { LoadTarget(result, block); } }); return(result); }
/// <summary> /// Renders the target bitmap. /// </summary> /// <param name="bitmapData">The bitmap data.</param> private void RenderTargetBitmap(BitmapDataBuffer bitmapData) { try { // Signal an update on the rendering surface TargetBitmap?.AddDirtyRect(bitmapData.UpdateRect); TargetBitmap?.Unlock(); } catch (Exception ex) { this.LogError(Aspects.VideoRenderer, $"{nameof(VideoRenderer)}.{nameof(RenderTargetBitmap)}", ex); } }
/// <summary> /// Loads that target data buffer with block data /// </summary> /// <param name="target">The target.</param> /// <param name="source">The source.</param> private void LoadTargetBitmapBuffer(BitmapDataBuffer target, VideoBlock source) { if (source != null && source.TryAcquireReaderLock(out var readLock)) { using (readLock) { // Compute a safe number of bytes to copy // At this point, we it is assumed the strides are equal var bufferLength = Convert.ToUInt32(Math.Min(source.BufferLength, target.BufferLength)); // Copy the block data into the back buffer of the target bitmap. WindowsNativeMethods.Instance.CopyMemory(target.Scan0, source.Buffer, bufferLength); } } }
/// <summary> /// Renders the target bitmap. /// </summary> /// <param name="bitmapData">The bitmap data.</param> /// <param name="clockPosition">The clock position.</param> private void RenderTargetBitmap(BitmapDataBuffer bitmapData, TimeSpan clockPosition) { try { // Signal an update on the rendering surface TargetBitmap?.AddDirtyRect(bitmapData.UpdateRect); TargetBitmap?.Unlock(); } catch (Exception ex) { MediaElement?.MediaCore?.Log( MediaLogMessageType.Error, $"{nameof(VideoRenderer)} {ex.GetType()}: {ex.Message}. Stack Trace:\r\n{ex.StackTrace}"); } }
internal void RaiseRenderingVideoEvent(VideoBlock videoBlock, BitmapDataBuffer bitmap, TimeSpan clock) { if (RenderingVideo == null) { return; } var e = new RenderingVideoEventArgs( bitmap, MediaCore.MediaInfo.Streams[videoBlock.StreamIndex], videoBlock.ClosedCaptions, videoBlock.SmtpeTimecode, videoBlock.DisplayPictureNumber, videoBlock.StartTime, videoBlock.Duration, clock); RenderingVideo?.Invoke(this, e); }
/// <summary> /// Loads that target data buffer with block data /// </summary> /// <param name="target">The target.</param> /// <param name="source">The source.</param> private void LoadTargetBitmapBuffer(BitmapDataBuffer target, VideoBlock source) { // Copy the block data into the back buffer of the target bitmap. if (target.Stride == source.BufferStride) { WindowsNativeMethods.Instance.CopyMemory(target.Scan0, source.Buffer, Convert.ToUInt32(source.BufferLength)); } else { var format = MediaPixelFormats[Constants.Video.VideoPixelFormat]; var bytesPerPixel = format.BitsPerPixel / 8; var copyLength = Convert.ToUInt32(Math.Min(target.Stride, source.BufferStride)); Parallel.For(0, source.PixelHeight, (i) => { var sourceOffset = source.Buffer + (i * source.BufferStride); var targetOffset = target.Scan0 + (i * target.Stride); WindowsNativeMethods.Instance.CopyMemory(targetOffset, sourceOffset, copyLength); }); } }
/// <summary> /// Loads that target data buffer with block data /// </summary> /// <param name="target">The target.</param> /// <param name="source">The source.</param> private unsafe void LoadTargetBitmapBuffer(BitmapDataBuffer target, VideoBlock source) { if (source == null || !source.TryAcquireReaderLock(out var readLock)) { return; } using (readLock) { // Compute a safe number of bytes to copy // At this point, we it is assumed the strides are equal var bufferLength = Math.Min(source.BufferLength, target.BufferLength); // Copy the block data into the back buffer of the target bitmap. Buffer.MemoryCopy( source.Buffer.ToPointer(), target.Scan0.ToPointer(), bufferLength, bufferLength); } }
public void RenderAndBlend(int current, BitmapDataBuffer data) { UpdateRenderSize(data.PixelWidth, data.PixelHeight); var updated = 0; var imageRaw = ass_render_frame(_renderer, _track, current, ref updated); if (imageRaw == IntPtr.Zero) { return; } try { unsafe { var sourceRaw = (uint *)data.Scan0.ToPointer(); int srcStride = data.Stride / sizeof(uint); while (imageRaw != IntPtr.Zero) { var image = Marshal.PtrToStructure <ASS_Image>(imageRaw); var imgRaw = (byte *)image.bitmap.ToPointer(); int h = image.h, w = image.w; if (h != 0 && w != 0) { int dstStride = image.stride; uint color = image.color; int srcCurrentPixel, dstCurrentPixel; uint image1Pixel; byte image2Pixel; uint srcRed, dstRed, finRed; uint srcGreen, dstGreen, finGreen; uint srcBlue, dstBlue, finBlue; uint dstAlpha, srcAlpha, finAlpha; // RGBA dstAlpha = 255 - (color & 0xFF); dstRed = ((color >> 24) & 0xFF) * dstAlpha / 255; dstGreen = ((color >> 16) & 0xFF) * dstAlpha / 255; dstBlue = ((color >> 8) & 0xFF) * dstAlpha / 255; for (var x = 0; x < w; x++) { for (var y = 0; y < h; y++) { srcCurrentPixel = (y + image.dst_y) * srcStride + x + image.dst_x; dstCurrentPixel = y * dstStride + x; image1Pixel = sourceRaw[srcCurrentPixel]; image2Pixel = imgRaw[dstCurrentPixel]; // ARGB srcAlpha = ((image1Pixel >> 24) & 0xFF); srcRed = ((image1Pixel >> 16) & 0xFF) * srcAlpha / 255; srcGreen = ((image1Pixel >> 8) & 0xFF) * srcAlpha / 255; srcBlue = (image1Pixel & 0xFF) * srcAlpha / 255; uint dstAlpha2 = image2Pixel; uint srcAlpha2 = 255 - dstAlpha2; finRed = dstRed * dstAlpha2 / 255 + srcRed * srcAlpha2 / 255; finGreen = dstGreen * dstAlpha2 / 255 + srcGreen * srcAlpha2 / 255; finBlue = dstBlue * dstAlpha2 / 255 + srcBlue * srcAlpha2 / 255; finAlpha = dstAlpha2 + srcAlpha * srcAlpha2 / 256; // ARGB sourceRaw[srcCurrentPixel] = finBlue | finGreen << 8 | finRed << 16 | finAlpha << 24; } } } imageRaw = image.next; } } } catch (Exception e) { Console.WriteLine(@"[libass] Exception rendering subtitle: " + e); } }
private void Me_RenderingVideo(object sender, RenderingVideoEventArgs e) { bmpBuffer = e.Bitmap; }