So I have some code to create H264ParameterSets like:
var formatDesc: CMVideoFormatDescription?
func createH264FormatDescription(SPS: Array<UInt8>, PPS: Array<UInt8>) -> OSStatus {
if formatDesc != nil { formatDesc = nil }
let paramSet = [UnsafePointer<UInt8>(SPS), UnsafePointer<UInt8>(PPS)]
let paramPointers = UnsafePointer<UnsafePointer<UInt8>>(paramSet)
let paramSizes = UnsafePointer<Int>([SPS.count, PPS.count])
let status = CMVideoFormatDescriptionCreateFromH264ParameterSets(allocator: kCFAllocatorDefault, parameterSetCount: 2, parameterSetPointers: paramPointers, parameterSetSizes: paramSizes, nalUnitHeaderLength: 4, formatDescriptionOut: &formatDesc)
return status
}
Starting on Xcode 11.4 I got warnings for those UnsafePointer(), which seems not happen before:
Initialization of UnsafePointer<UInt8> results in a dangling pointer
Initialization of UnsafePointer<UnsafePointer<UInt8>> results in a dangling pointer
Initialization of UnsafePointer<Int> results in a dangling pointer
I'm not sure why we see this? and how can I remove the warning? Thank in advance.
The easiest way to explain this warning is to look at one of the cases causing it. So lets start with your use of SPS
.
It is an Array<UInt8>
so it is backed by a buffer of UInt8
just like in C. When you pass SPS
with UnsafePointer<UInt8>(SPS)
it creates a valid pointer to the buffer for that moment. The issue is that you could then mutate SPS
say by appending another value to it. This would mean that the buffer backing the Array
is potentially moved to another place in memory. This would mean that your pointer that is now part of paramSet
is invalid.
The other issue is that if you pass this pointer to something, like you do in this case, the other function could try to hold onto it and then it has an invalid pointer. So if you expect the other function to hold onto the pointer you need to manually manage memory with UnsafePointer
s and Unmanaged
yourself. If CMVideoFormatDescriptionCreateFromH264ParameterSets()
doesn't hold onto the pointers then the code I'll share is correct, if it does you will need to adjust it to create/destory the memory as needed.
Also it is worth noting that in this case, you can't mutate any of the Array
s you have because they are constants but in general the principle is still the same. This means that in theory it could never be mutated but the Swift compiler prefers to help us write code that is always safe and correct whenever possible, even with UnsafePointer
types.
So how can you fix this? You will need to be able to call withUnsafeBufferPointer
and then access the pointer through the UnsafeBufferPointer
like this:
var formatDesc: CMVideoFormatDescription?
func createH264FormatDescription(SPS: Array<UInt8>, PPS: Array<UInt8>) -> OSStatus {
if formatDesc != nil { formatDesc = nil }
let status = SPS.withUnsafeBufferPointer { SPS in
PPS.withUnsafeBufferPointer { PPS in
let paramSet = [SPS.baseAddress!, PPS.baseAddress!]
let paramSizes = [SPS.count, PPS.count]
return paramSet.withUnsafeBufferPointer { paramSet in
paramSizes.withUnsafeBufferPointer { paramSizes in
CMVideoFormatDescriptionCreateFromH264ParameterSets(allocator: kCFAllocatorDefault, parameterSetCount: 2, parameterSetPointers: paramSet.baseAddress!, parameterSetSizes: paramSizes.baseAddress!, nalUnitHeaderLength: 4, formatDescriptionOut: &formatDesc)
}
}
}
}
return status
}
The reason this approach works is that for the scope of withUnsafeBufferPointer
the Law of Exclusivity is protecting the arrays so they can't be mutated.
If you are worried about the baseAddress!
usage you can check that it isn't nil
but it is guaranteed to not be nil
when count > 0
according the the compiler engineers (they have stated this on either Twitter or the Swift forums I forget...).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With