[VTDecompressionStatus] Enable nullability + numerous other code updates (#13051)

* Enable nullability and fix code accordingly.
* Use 'is' and 'is not' instead of '==' and '!=' for object identity.
* Use the null-safe NativeObjectExtensions.GetHandle extension method to get
  the handle instead of checking for null (avoids some code duplication).
* Use 'nameof (parameter)' instead of string constants.
* Call 'GetCheckedHandle ()' (which will throw an ObjectDisposedException if
  Handle == IntPtr.Zero) instead of manually checking for IntPtr.Zero and
  throwing ObjectDisposedException.
* Simplify block code to not need to be unsafe.
* Fix extraneous call to CFObject.CFRelease in the CopyBlackPixelBuffer method.
This commit is contained in:
Rolf Bjarne Kvinge 2021-10-20 20:18:55 +02:00 коммит произвёл GitHub
Родитель d2cdeba986
Коммит fa51d143f7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
1 изменённых файлов: 65 добавлений и 85 удалений

Просмотреть файл

@ -7,6 +7,8 @@
// Copyright 2015 Xamarin Inc.
//
#nullable enable
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
@ -28,27 +30,17 @@ namespace VideoToolbox {
GCHandle callbackHandle;
/* invoked by marshallers */
#if !XAMCORE_4_0
protected internal VTDecompressionSession (IntPtr handle) : base (handle)
{
}
#endif
[Preserve (Conditional=true)]
internal VTDecompressionSession (IntPtr handle, bool owns) : base (handle, owns)
{
}
~VTDecompressionSession ()
{
Dispose (false);
}
public void Dispose ()
{
Dispose (true);
GC.SuppressFinalize (this);
}
protected override void Dispose (bool disposing)
{
if (Handle != IntPtr.Zero)
@ -82,12 +74,12 @@ namespace VideoToolbox {
//
// Here for legacy code, which would only work under duress (user had to manually ref the CMSampleBuffer on the callback)
//
static DecompressionOutputCallback _static_decompressionCallback;
static DecompressionOutputCallback? _static_decompressionCallback;
static DecompressionOutputCallback static_DecompressionOutputCallback {
get {
if (_static_decompressionCallback == null)
if (_static_decompressionCallback is null)
_static_decompressionCallback = new DecompressionOutputCallback (DecompressionCallback);
return _static_decompressionCallback;
return _static_decompressionCallback!;
}
}
#endif
@ -103,25 +95,28 @@ namespace VideoToolbox {
VTDecodeInfoFlags infoFlags, IntPtr imageBufferPtr, CMTime presentationTimeStamp, CMTime presentationDuration)
{
var gch = GCHandle.FromIntPtr (outputCallbackClosure);
var func = (VTDecompressionOutputCallback) gch.Target;
var func = gch.Target as VTDecompressionOutputCallback;
if (func is null)
return;
// Apple headers states that the callback should get a CVImageBuffer but it turned out that not all of them are a
// CVImageBuffer, some can be instances of CVImageBuffer and others can be instances of CVPixelBuffer. So we go one
// step further in the inheritance hierarchy and supply the callback a CVPixelBuffer and the callback supplies
// to the developer a CVImageBuffer, so the developer can choose when to use one or the other and we mimic
// what Apple provides on its headers.
using (var sampleBuffer = new CVPixelBuffer (imageBufferPtr)) {
using (var sampleBuffer = new CVPixelBuffer (imageBufferPtr, false)) {
func (sourceFrame, status, infoFlags, sampleBuffer, presentationTimeStamp, presentationDuration);
}
}
#if !NET
static DecompressionOutputCallback _static_newDecompressionCallback;
static DecompressionOutputCallback? _static_newDecompressionCallback;
static DecompressionOutputCallback static_newDecompressionOutputCallback {
get {
if (_static_newDecompressionCallback == null)
if (_static_newDecompressionCallback is null)
_static_newDecompressionCallback = new DecompressionOutputCallback (NewDecompressionCallback);
return _static_newDecompressionCallback;
return _static_newDecompressionCallback!;
}
}
#endif
@ -137,7 +132,10 @@ namespace VideoToolbox {
VTDecodeInfoFlags infoFlags, IntPtr imageBufferPtr, CMTime presentationTimeStamp, CMTime presentationDuration)
{
var gch = GCHandle.FromIntPtr (outputCallbackClosure);
var func = (VTDecompressionOutputCallback) gch.Target;
var func = gch.Target as VTDecompressionOutputCallback;
if (func is null)
return;
// Apple headers states that the callback should get a CVImageBuffer but it turned out that not all of them are a
// CVImageBuffer, some can be instances of CVImageBuffer and others can be instances of CVPixelBuffer. So we go one
@ -184,10 +182,10 @@ namespace VideoToolbox {
#endif
[Obsolete ("This overload requires that the provided compressionOutputCallback manually CFRetain the passed CMSampleBuffer, use Create(VTDecompressionOutputCallback,CMVideoFormatDescription,VTVideoDecoderSpecification,CVPixelBufferAttributes) variant instead which does not have that requirement.")]
public static VTDecompressionSession Create (VTDecompressionOutputCallback outputCallback,
public static VTDecompressionSession? Create (VTDecompressionOutputCallback outputCallback,
CMVideoFormatDescription formatDescription,
VTVideoDecoderSpecification decoderSpecification = null, // hardware acceleration is default behavior on iOS. no opt-in required.
NSDictionary destinationImageBufferAttributes = null)
VTVideoDecoderSpecification? decoderSpecification = null, // hardware acceleration is default behavior on iOS. no opt-in required.
NSDictionary? destinationImageBufferAttributes = null)
{
#if NET
unsafe {
@ -198,32 +196,35 @@ namespace VideoToolbox {
#endif
}
public static VTDecompressionSession Create (VTDecompressionOutputCallback outputCallback,
public static VTDecompressionSession? Create (VTDecompressionOutputCallback outputCallback,
CMVideoFormatDescription formatDescription,
VTVideoDecoderSpecification decoderSpecification, // hardware acceleration is default behavior on iOS. no opt-in required.
CVPixelBufferAttributes destinationImageBufferAttributes)
VTVideoDecoderSpecification? decoderSpecification, // hardware acceleration is default behavior on iOS. no opt-in required.
CVPixelBufferAttributes? destinationImageBufferAttributes)
{
#if NET
unsafe {
return Create (outputCallback, formatDescription, decoderSpecification, destinationImageBufferAttributes == null ? null : destinationImageBufferAttributes.Dictionary, &NewDecompressionCallback);
return Create (outputCallback, formatDescription, decoderSpecification, destinationImageBufferAttributes?.Dictionary, &NewDecompressionCallback);
}
#else
return Create (outputCallback, formatDescription, decoderSpecification, destinationImageBufferAttributes == null ? null : destinationImageBufferAttributes.Dictionary, static_newDecompressionOutputCallback);
return Create (outputCallback, formatDescription, decoderSpecification, destinationImageBufferAttributes?.Dictionary, static_newDecompressionOutputCallback);
#endif
}
unsafe static VTDecompressionSession Create (VTDecompressionOutputCallback outputCallback,
unsafe static VTDecompressionSession? Create (VTDecompressionOutputCallback outputCallback,
CMVideoFormatDescription formatDescription,
VTVideoDecoderSpecification decoderSpecification, // hardware acceleration is default behavior on iOS. no opt-in required.
NSDictionary destinationImageBufferAttributes,
VTVideoDecoderSpecification? decoderSpecification, // hardware acceleration is default behavior on iOS. no opt-in required.
NSDictionary? destinationImageBufferAttributes,
#if NET
delegate* unmanaged</* void* */ IntPtr, /* void* */ IntPtr, /* OSStatus */ VTStatus, VTDecodeInfoFlags, /* CVImageBuffer */ IntPtr, CMTime, CMTime, void> cback)
#else
DecompressionOutputCallback cback)
#endif
{
if (formatDescription == null)
throw new ArgumentNullException ("formatDescription");
if (outputCallback is null)
throw new ArgumentNullException (nameof (outputCallback));
if (formatDescription is null)
throw new ArgumentNullException (nameof (formatDescription));
var callbackHandle = GCHandle.Alloc (outputCallback);
var callbackStruct = new VTDecompressionOutputCallbackRecord () {
@ -233,8 +234,8 @@ namespace VideoToolbox {
IntPtr ret;
var result = VTDecompressionSessionCreate (IntPtr.Zero, formatDescription.Handle,
decoderSpecification != null ? decoderSpecification.Dictionary.Handle : IntPtr.Zero,
destinationImageBufferAttributes != null ? destinationImageBufferAttributes.Handle : IntPtr.Zero,
decoderSpecification.GetHandle (),
destinationImageBufferAttributes.GetHandle (),
ref callbackStruct,
out ret);
@ -260,22 +261,20 @@ namespace VideoToolbox {
public VTStatus DecodeFrame (CMSampleBuffer sampleBuffer, VTDecodeFrameFlags decodeFlags, IntPtr sourceFrame, out VTDecodeInfoFlags infoFlags)
{
if (Handle == IntPtr.Zero)
throw new ObjectDisposedException ("DecompressionSession");
if (sampleBuffer == null)
throw new ArgumentNullException ("sampleBuffer");
if (sampleBuffer is null)
throw new ArgumentNullException (nameof (sampleBuffer));
return VTDecompressionSessionDecodeFrame (Handle, sampleBuffer.Handle, decodeFlags, sourceFrame, out infoFlags);
return VTDecompressionSessionDecodeFrame (GetCheckedHandle (), sampleBuffer.Handle, decodeFlags, sourceFrame, out infoFlags);
}
#if false // Disabling for now until we have some tests on this
[Mac (10,11), iOS (9,0)]
[DllImport (Constants.VideoToolboxLibrary)]
extern static unsafe VTStatus VTDecompressionSessionDecodeFrameWithOutputHandler (
extern static VTStatus VTDecompressionSessionDecodeFrameWithOutputHandler (
/* VTDecompressionSessionRef */ IntPtr session,
/* CMSampleBufferRef */ IntPtr sampleBuffer,
/* VTDecodeFrameFlags */ VTDecodeFrameFlags decodeFlags,
/* VTDecodeInfoFlags */ out VTDecodeInfoFlags infoFlagsOut,
/* VTDecompressionOutputHandler */ BlockLiteral *outputHandler);
/* VTDecompressionOutputHandler */ ref BlockLiteral outputHandler);
public delegate void VTDecompressionOutputHandler (VTStatus status, VTDecodeInfoFlags infoFlags,
CVImageBuffer imageBuffer, CMTime presentationTimeStamp, CMTime presentationDuration);
@ -291,32 +290,26 @@ namespace VideoToolbox {
CMTime presentationTimeStamp, CMTime presentationDuration)
{
var del = (VTDecompressionOutputHandler)(block->Target);
if (del != null)
del (status, infoFlags, new CVImageBuffer (imageBuffer), presentationTimeStamp, presentationDuration);
if (del is not null)
del (status, infoFlags, new CVImageBuffer (imageBuffer, false), presentationTimeStamp, presentationDuration);
}
[Mac (10,11), iOS (9,0)]
public VTStatus DecodeFrame (CMSampleBuffer sampleBuffer, VTDecodeFrameFlags decodeFlags,
out VTDecodeInfoFlags infoFlags, VTDecompressionOutputHandler outputHandler)
{
if (Handle == IntPtr.Zero)
throw new ObjectDisposedException ("DecompressionSession");
if (sampleBuffer == null)
throw new ArgumentNullException ("sampleBuffer");
if (outputHandler == null)
throw new ArgumentNullException ("outputHandler");
if (sampleBuffer is null)
throw new ArgumentNullException (nameof (sampleBuffer));
if (outputHandler is null)
throw new ArgumentNullException (nameof (outputHandler));
unsafe {
var block = new BlockLiteral ();
var blockPtr = &block;
block.SetupBlockUnsafe (decompressionOutputHandlerTrampoline, outputHandler);
try {
return VTDecompressionSessionDecodeFrameWithOutputHandler (Handle,
sampleBuffer.Handle, decodeFlags, out infoFlags, blockPtr);
} finally {
blockPtr->CleanupBlock ();
}
var block = new BlockLiteral ();
block.SetupBlockUnsafe (decompressionOutputHandlerTrampoline, outputHandler);
try {
return VTDecompressionSessionDecodeFrameWithOutputHandler (GetCheckedHandle (),
sampleBuffer.Handle, decodeFlags, out infoFlags, ref block);
} finally {
block.CleanupBlock ();
}
}
#endif
@ -325,10 +318,7 @@ namespace VideoToolbox {
public VTStatus FinishDelayedFrames ()
{
if (Handle == IntPtr.Zero)
throw new ObjectDisposedException ("DecompressionSession");
return VTDecompressionSessionFinishDelayedFrames (Handle);
return VTDecompressionSessionFinishDelayedFrames (GetCheckedHandle ());
}
[DllImport (Constants.VideoToolboxLibrary)]
@ -336,10 +326,10 @@ namespace VideoToolbox {
public VTStatus CanAcceptFormatDescriptor (CMFormatDescription newDescriptor)
{
if (Handle == IntPtr.Zero)
throw new ObjectDisposedException ("DecompressionSession");
if (newDescriptor is null)
throw new ArgumentNullException (nameof (newDescriptor));
return VTDecompressionSessionCanAcceptFormatDescription (Handle, newDescriptor.Handle);
return VTDecompressionSessionCanAcceptFormatDescription (GetCheckedHandle (), newDescriptor.Handle);
}
[DllImport (Constants.VideoToolboxLibrary)]
@ -347,35 +337,25 @@ namespace VideoToolbox {
public VTStatus WaitForAsynchronousFrames ()
{
if (Handle == IntPtr.Zero)
throw new ObjectDisposedException ("DecompressionSession");
return VTDecompressionSessionWaitForAsynchronousFrames (Handle);
return VTDecompressionSessionWaitForAsynchronousFrames (GetCheckedHandle ());
}
[DllImport (Constants.VideoToolboxLibrary)]
extern static VTStatus VTDecompressionSessionCopyBlackPixelBuffer (IntPtr sesion, out IntPtr pixelBufferOut);
public VTStatus CopyBlackPixelBuffer (out CVPixelBuffer pixelBuffer)
public VTStatus CopyBlackPixelBuffer (out CVPixelBuffer? pixelBuffer)
{
if (Handle == IntPtr.Zero)
throw new ObjectDisposedException ("DecompressionSession");
IntPtr ret;
var result = VTDecompressionSessionCopyBlackPixelBuffer (Handle, out ret);
var result = VTDecompressionSessionCopyBlackPixelBuffer (GetCheckedHandle (), out var ret);
pixelBuffer = Runtime.GetINativeObject<CVPixelBuffer> (ret, true);
CFObject.CFRelease (ret);
return result;
}
public VTStatus SetDecompressionProperties (VTDecompressionProperties options)
{
if (Handle == IntPtr.Zero)
throw new ObjectDisposedException ("DecompressionSession");
if (options == null)
throw new ArgumentNullException ("options");
if (options is null)
throw new ArgumentNullException (nameof (options));
return VTSessionSetProperties (Handle, options.Dictionary.Handle);
return VTSessionSetProperties (GetCheckedHandle (), options.Dictionary.Handle);
}
#if NET