Get metadata description format identifier

This is a Objective-C to Swift Question :

I would like to change demo code AVLocationPlayer: Using AVFoundation Metadata Reading APIs to Swift.

Objective-C

NSArray *metadataTracks = [asset tracksWithMediaType:AVMediaTypeMetadata];
  for (AVAssetTrack *track in metadataTracks)
  {
      for (id formatDescription in track.formatDescriptions)
      {
          NSArray *identifiers = (__bridge NSArray *)(CMMetadataFormatDescriptionGetIdentifiers((__bridge CMMetadataFormatDescriptionRef)formatDescription));
          if ([identifiers containsObject:AVMetadataIdentifierQuickTimeMetadataLocationISO6709])
          {
                locationTrack = track;
                break;
          }
      }
  }

Swift

var metadataTracks = asset.tracksWithMediaType(AVMediaTypeMetadata)
    for track: AVAssetTrack in metadataTracks {
      for formatDescription in track.formatDescriptions {
          var identifiers = (CMMetadataFormatDescriptionGetIdentifiers(formatDescription as! CMMetadataFormatDescription))
         /*  Question's Here
          if identifiers.containsObject(AVMetadataIdentifierQuickTimeMetadataLocationISO6709) {
                        locationTrack = track
             }
         */
        }
    }

The identifier is CFArray type and there is no member of containsOject. Any suggestion?

I bet identifiers is a Swift array.

You can use indexOf (only for equatable elements, which applies here because the identifiers are strings).


if let index = identifiers.indexOf(AVMetadataIdentifierQuickTimeMetadataLocationISO6709)
     {
}


Jan E

Unfortunately, CFArray only have three members

String debugDescription
    U? flatMap(f: (CFArray) throw -> U?) rethrows
    U? map(f: (CFArray) throw -> U) rethrows

Also, I tried to turn it as NSArray, but fails.

Maybe this works:


if identifiers != nil
     {
let cfStr = CFStringCreateWithCString(nil, AVMetadataIdentifierQuickTimeMetadataLocationISO6709, CFStringBuiltInEncodings.UTF8.rawValue)
let contains = CFArrayContainsValue( identifiers!,CFRangeMake(0,CFArrayGetCount(identifiers!)),cfStr)
}

It seems right ! But one more question, CFArrayContainsValue(theArray: CFArray!, _ range: CFRange, _ value: UnsafePointer<Void>)
how can I pass cfStr as UnsafePointer? Many thanks!

Also, I tried to turn it as NSArray, but fails.

I'm under way of translating the same sample code into Swift, so, I'm not sure, but does this not work?

        let metadataTracks = asset.tracksWithMediaType(AVMediaTypeMetadata)
        for track in metadataTracks {
            for formatDescription in track.formatDescriptions as! [CMFormatDescription] {
                if let identifiers = CMMetadataFormatDescriptionGetIdentifiers(formatDescription) as? NSArray
                where identifiers.containsObject(AVMetadataIdentifierQuickTimeMetadataLocationISO6709) {
                        locationTrack = track
                        break
                }
            }
        }
let identifiers = CMMetadataFormatDescriptionGetIdentifiers(formatDescription) as? NSArray

By doing so, I'll always get nil of identifiers

Thanks. I'll dig it a little more further when ready.

Good question.

I can't even manage to create a CFArray of CFStrings for testing.

OOPer, can you give an example?


Jan E.

How is this?

var x = CFStringCreateWithCString(nil, "x", CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding))
var y = CFStringCreateWithCString(nil, "y", CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding))
var values: [UnsafePointer<Void>] = [unsafeAddressOf(x), unsafeAddressOf(y)]
var arr = CFArrayCreate(nil, &values, values.count, nil)


And till now, converting CFArrays created as such to NSArray never fails...

Accepted Answer

So we have the alternatives


let metadataTracks = asset.tracksWithMediaType(AVMediaTypeMetadata)
        for track in metadataTracks {
            for formatDescription in track.formatDescriptions as! [CMFormatDescription] {
                if let identifiers = CMMetadataFormatDescriptionGetIdentifiers(formatDescription)
                where (identifiers as NSArray).containsObject(AVMetadataIdentifierQuickTimeMetadataLocationISO6709) {
                        locationTrack = track
                        break
                }
            }
        }


and


let cfStr = CFStringCreateWithCString(nil, AVMetadataIdentifierQuickTimeMetadataLocationISO6709,        CFStringBuiltInEncodings.UTF8.rawValue)


let metadataTracks = asset.tracksWithMediaType(AVMediaTypeMetadata)
for track in metadataTracks {

for formatDescription in track.formatDescriptions as! [CMFormatDescription] {
           if let identifiers = CMMetadataFormatDescriptionGetIdentifiers(formatDescription)

                where CFArrayContainsValue( identifiers,CFRangeMake(0,CFArrayGetCount(identifiers)),unsafeAddressOf(cfStr))

     {


                        locationTrack = track

                        break               

   }

}


Does either of these work?


Jan E.


BTW can somebody please fix this editor? Thank you 😠

After some modification, this works!

for formatDescription in track.formatDescriptions as! [CMFormatDescription] {
    var identifiers = (CMMetadataFormatDescriptionGetIdentifiers(formatDescription as! CMMetadataFormatDescription))
    let cfStr = CFStringCreateWithCString(nil, AVMetadataIdentifierQuickTimeMetadataLocationISO6709, CFStringBuiltInEncodings.UTF8.rawValue)
    if CFArrayContainsValue( identifiers, CFRangeMake(0,CFArrayGetCount(identifiers)), unsafeAddressOf(cfStr))
    {
          locationTrack = track
    }
}

Except that identifiers might be nil. Better use optional binding.

So, I found one thing, converting CFArray to NSArray always succeeds (we use `as` for this case),

but converting CFArray? to NSArray always fails and returns nil (using `as?`, of course).

(This seems to be a runtime bug for me, compiler does not show any `always fails` warnings.)


If you can make some time, try my code with replacing one line:

let identifiers = CMMetadataFormatDescriptionGetIdentifiers(formatDescription) as NSArray?
Get metadata description format identifier
 
 
Q