How to use CFDictionarySetValue in swift?

2k views Asked by At

Currently, I am converting the hardware decoding Obj-C code to Swift version. (Xcode 8, Swift 3).

I don't know how to set up a dictionary to set up an output option on the screen, also, I don't know how to use it.

The following code works fine in Obj-C project:

// set some values of the sample buffer's attachments
CFArrayRef attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, YES);
CFMutableDictionaryRef dict = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(attachments, 0);
CFDictionarySetValue(dict, kCMSampleAttachmentKey_DisplayImmediately, kCFBooleanTrue);

I tried the following Swift code, but there was a runtime error:

// i got run-time error
let attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer!, true)
let dict = unsafeBitCast(attachments, to: CFMutableDictionary.self)
CFDictionarySetValue(dict, unsafeBitCast(kCMSampleAttachmentKey_DisplayImmediately, to: UnsafeRawPointer.self), unsafeBitCast(kCFBooleanTrue, to: UnsafeRawPointer.self))

Is it wrong to convert the CFString to UnsafeRawPointer? or Is it wrong to use the CFDictionarySetValue method?

this is my error log.

2016-11-24 16:50:44.458 MyApp[35288:3519253] -[__NSSingleObjectArrayI __setObject:forKey:]: unrecognized selector sent to instance 0x6000002045a0
2016-11-24 16:50:44.466 MyApp[35288:3519253] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSSingleObjectArrayI __setObject:forKey:]: unrecognized selector sent to instance 0x6000002045a0'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010b02734b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x000000010a0e421e objc_exception_throw + 48
    2   CoreFoundation                      0x000000010b096f34 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
    3   CoreFoundation                      0x000000010afacc15 ___forwarding___ + 1013
    4   CoreFoundation                      0x000000010afac798 _CF_forwarding_prep_0 + 120
    5   MyApp                             0x000000010937ac7a _TFC14MyApp16ViewerController21receivedRawVideoFramefT5frameGSaVs5UInt8_4withVs5Int32_T_ + 4890
    6   MyApp                             0x000000010937d8e2 _TFC14MyApp16ViewerController12MainCallBackfTVs5Int3212callbackCodeS1_8argumentGSqSv_7argSizeS1__T_ + 4242
    7   MyApp                             0x000000010937daed _TToFC14MyApp16ViewerController12MainCallBackfTVs5Int3212callbackCodeS1_8argumentGSqSv_7argSizeS1__T_ + 61
    8   MyApp                            0x000000010937dd56 _TTDFC14MyApp16ViewerController12MainCallBackfTVs5Int3212callbackCodeS1_8argumentGSqSv_7argSizeS1__T_ + 70
    9   MyApp                             0x000000010937dcfe _TTWC14MyApp16ViewerControllerS_18IJCallbackProtocolS_FS1_12MainCallBackfTVs5Int3212callbackCodeS2_8argumentGSqSv_7argSizeS2__T_ + 62
    10  MyApp                             0x00000001093b22b2 _TFC14MyApp11AppDelegate19MainCallBack_StreamfTVs5Int32S1_GSqSv_S1__T_ + 258
    11  MyApp                             0x00000001093d26d3 _TFZFC14MyApp19IJStreamCoreWrapper6AttachFTSv2ipSS4portSi_T_U_FTVs5Int32S1_GSqSv_S1_GSqSv__T_ + 355
    12  MyApp                             0x00000001093d2719 _TToFZFC14MyApp19IJStreamCoreWrapper6AttachFTSv2ipSS4portSi_T_U_FTVs5Int32S1_GSqSv_S1_GSqSv__T_ + 9
    13  MyApp                             0x00000001093ee176 _ZL8CallbackPN14CStreamManager9Session_TEiPvi + 70
    14  MyApp                             0x00000001093f09d3 _Z25StreamManagerThread_VideoPv + 3155
    15  libsystem_pthread.dylib             0x000000010e494aab _pthread_body + 180
    16  libsystem_pthread.dylib             0x000000010e4949f7 _pthread_body + 0
    17  libsystem_pthread.dylib             0x000000010e494221 thread_start + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException
3

There are 3 answers

0
rob mayoff On BEST ANSWER

In the first code snippet, you have a call to CFArrayGetValueAtIndex, which returns a dictionary that you pass to CFDictionarySetValue.

In the second code snippet, you don't call CFArrayGetValueAtIndex. You just pass the array to CFDictionarySetValue. Since an array is not a dictionary, you get a fatal error.

0
tmm1 On

I was only able to get this working with the following:

let attachments = CMSampleBufferGetSampleAttachmentsArray(buf!, createIfNecessary: true)
let dict = unsafeBitCast(CFArrayGetValueAtIndex(attachments, 0), to: CFMutableDictionary.self)
CFDictionarySetValue(dict,
                     unsafeBitCast(kCMSampleAttachmentKey_DisplayImmediately, to: UnsafeRawPointer.self),
                     unsafeBitCast(kCFBooleanTrue, to: UnsafeRawPointer.self))

I tried @Alexander's suggestion, and although it compiles and executes, the underlying CFDictionary is not mutated and the desired result is not achieved.

//  let attachments = CMSampleBufferGetSampleAttachmentsArray(buf!, createIfNecessary: true) as! Array<Dictionary<String, Any>>
//  var dict = attachments[0]
//  dict[kCMSampleAttachmentKey_DisplayImmediately as String] = true
1
Alexander On

When you convert code to Swift, it's much best to understand the semantic of the program, and rewrite Swift code that matches. Don't try to just convert the syntax bit by bit.

CMSampleBufferGetSampleAttachmentsArray returns you a reference to a CFArray of CFDictionary instances. Casting this CFArrayRef to CFMutableDictionaryRef has the effect of just making a reference to the first instance.

It's best to just use Swift's native types. I can't provide an exact implementation because I'm not to sure of the types in play, but here's a start

//TODO: give me a better name
let array = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer!, true) as [[String: Bool]] //TODO:

//TODO: give me a better name
var firstDict = array[0]

firstDict[kCMSampleAttachmentKey_DisplayImmediately as String] = true