From dd06fbb6720115a33c9710b710de360296467a74 Mon Sep 17 00:00:00 2001 From: Alex Soto Date: Wed, 4 May 2016 10:23:26 -0500 Subject: [PATCH] [CoreMedia] Add convenience byte [] overloads to CMBlockBuffer API (#26) reference: https://trello.com/c/RX7BMktQ/591-verify-coremedia-memory-management-of-public-api --- src/CoreMedia/CMBlockBuffer.cs | 53 +++++++++++++++++++++++++ src/CoreMedia/CMCustomBlockAllocator.cs | 22 ++++++++++ 2 files changed, 75 insertions(+) diff --git a/src/CoreMedia/CMBlockBuffer.cs b/src/CoreMedia/CMBlockBuffer.cs index f86c39d38c..4bbd978f5e 100644 --- a/src/CoreMedia/CMBlockBuffer.cs +++ b/src/CoreMedia/CMBlockBuffer.cs @@ -178,9 +178,27 @@ namespace XamCore.CoreMedia { public CMBlockBufferError CopyDataBytes (nuint offsetToData, nuint dataLength, IntPtr destination) { + if (Handle == IntPtr.Zero) + throw new ObjectDisposedException ("BlockBuffer"); + return CMBlockBufferCopyDataBytes (handle, offsetToData, dataLength, destination); } + public CMBlockBufferError CopyDataBytes (nuint offsetToData, nuint dataLength, out byte [] destination) + { + if (Handle == IntPtr.Zero) + throw new ObjectDisposedException ("BlockBuffer"); + + destination = new byte [dataLength]; + GCHandle destPinned = GCHandle.Alloc (destination, GCHandleType.Pinned); + IntPtr destPtr = destPinned.AddrOfPinnedObject (); + var error = CMBlockBufferCopyDataBytes (handle, offsetToData, dataLength, destPtr); + if (error != CMBlockBufferError.None) + destination = default (byte []); + destPinned.Free (); + return error; + } + [DllImport(Constants.CoreMediaLibrary)] extern static /* OSStatus */ CMBlockBufferError CMBlockBufferReplaceDataBytes ( /* void* */ IntPtr sourceBytes, @@ -196,6 +214,21 @@ namespace XamCore.CoreMedia { return CMBlockBufferReplaceDataBytes (sourceBytes, handle, offsetIntoDestination, dataLength); } + public CMBlockBufferError ReplaceDataBytes (byte [] sourceBytes, nuint offsetIntoDestination) + { + if (Handle == IntPtr.Zero) + throw new ObjectDisposedException ("BlockBuffer"); + if (Handle == IntPtr.Zero) + throw new ArgumentNullException (nameof (sourceBytes)); + + GCHandle replacePinned = GCHandle.Alloc (sourceBytes, GCHandleType.Pinned); + IntPtr replacePtr = replacePinned.AddrOfPinnedObject (); + + var error = ReplaceDataBytes (replacePtr, offsetIntoDestination, (nuint) sourceBytes.Length); + replacePinned.Free (); + return error; + } + [DllImport(Constants.CoreMediaLibrary)] extern static /* OSStatus */ CMBlockBufferError CMBlockBufferFillDataBytes ( /* char */ byte fillByte, @@ -307,6 +340,15 @@ namespace XamCore.CoreMedia { return block; } + public static CMBlockBuffer FromMemoryBlock (byte [] data, nuint offsetToData, CMBlockBufferFlags flags, out CMBlockBufferError error) + { + if (data == null) + throw new ArgumentNullException (nameof (data)); + + var allocator = new CMManagedArrayBlockAllocator (data); + return FromMemoryBlock (IntPtr.Zero, (uint) data.Length, allocator, offsetToData, (uint) data.Length, flags, out error); + } + [DllImport(Constants.CoreMediaLibrary)] extern static /* OSStatus */ CMBlockBufferError CMBlockBufferCreateContiguous ( /* CFAllocatorRef */ IntPtr structureAllocator, @@ -381,5 +423,16 @@ namespace XamCore.CoreMedia { else return CMBlockBufferAppendMemoryBlock (Handle, memoryBlock, blockLength, blockAllocator, ref customBlockSource.Cblock, offsetToData, dataLength, flags); } + + public CMBlockBufferError AppendMemoryBlock (byte [] data, nuint offsetToData, CMBlockBufferFlags flags) + { + if (Handle == IntPtr.Zero) + throw new ObjectDisposedException ("BlockBuffer"); + if (data == null) + throw new ArgumentNullException (nameof (data)); + + var allocator = new CMManagedArrayBlockAllocator (data); + return AppendMemoryBlock (IntPtr.Zero, (uint) data.Length, allocator, offsetToData, (uint) data.Length, flags); + } } } diff --git a/src/CoreMedia/CMCustomBlockAllocator.cs b/src/CoreMedia/CMCustomBlockAllocator.cs index a0e3c944ca..b3e2e31961 100644 --- a/src/CoreMedia/CMCustomBlockAllocator.cs +++ b/src/CoreMedia/CMCustomBlockAllocator.cs @@ -90,5 +90,27 @@ namespace XamCore.CoreMedia { gch.Free(); } } + + // This class is used internally by a couple of CMBlockBuffer methods + // that take a managed array as input parameter + internal class CMManagedArrayBlockAllocator : CMCustomBlockAllocator { + + GCHandle dataHandle; + public CMManagedArrayBlockAllocator (byte [] data) + { + dataHandle = GCHandle.Alloc (data, GCHandleType.Pinned); + } + + public override IntPtr Allocate (nuint sizeInBytes) + { + return dataHandle.AddrOfPinnedObject (); + } + + public override void Free (IntPtr doomedMemoryBlock, nuint sizeInBytes) + { + if (dataHandle.IsAllocated) + dataHandle.Free (); + } + } }