iosobjective-cnsdataalassetslibrarycgimage

iOS overwrite EXIF flash property with new value for front camera flash


I'm trying to change the kCGImagePropertyExifFlash property of the Exif dictionary. My app offers front facing flash modes, so I'm trying to change this Exif value. No matter what I do when I go to save the newImageData using writeImageDataToSavedPhotosAlbum method of ALAssetsLibrary, it removes the value I set.

I'd appreciate any help offered.

[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:
[self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo] 
completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error)
{
    if (imageSampleBuffer && !error)
    {
        CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate);

        NSDictionary *metadata = [NSDictionary dictionaryWithDictionary:(__bridge NSDictionary *)metadataDict];

        CFRelease(metadataDict);

        NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];

        if (metadata && imageData)
        {
            NSMutableDictionary *newMetaData = [NSMutableDictionary dictionaryWithDictionary:metadata];

            NSMutableDictionary *exifMetadata = [NSMutableDictionary dictionaryWithDictionary:
            [metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary]];

            if (self.isUsingFrontCamera && exifMetadata)
            {
                if (self.flashButton.flashMode == On)
                {
                    [exifMetadata setObject:@"On, Fired" forKey:(NSString *)kCGImagePropertyExifFlash];
                }
                else if (self.flashButton.flashMode == Off)
                {
                    [exifMetadata setObject:@"Off, did not fire" forKey:(NSString *)kCGImagePropertyExifFlash];
                }
                else if (self.frontFlashAutoDidFire)
                {
                    [exifMetadata setObject:@"Auto, Fired" forKey:(NSString *)kCGImagePropertyExifFlash];
                }
             }

             [newMetaData setObject:exifMetadata forKey:(NSString *)kCGImagePropertyExifDictionary];

             NSData *newImageData = [self writeMetadataIntoImageData:imageData metadata:newMetaData];

             [ALAssetsLibrary.new writeImageDataToSavedPhotosAlbum:newImageData metadata:nil completionBlock:nil];
         }
     }
}

- (NSData *)writeMetadataIntoImageData:(NSData *)imageData metadata:(NSDictionary *)metadata
{
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL);

    CFStringRef UTI = CGImageSourceGetType(source);

    NSMutableData *data = [NSMutableData data];

    CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)data, UTI, 1, NULL);

    CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef) metadata);

    CGImageDestinationFinalize(destination);

    CFRelease(destination);
    CFRelease(source);

    return data;
}

Solution

  • I figured this out; it turns out the kCGImagePropertyExifFlash is an NSCFNumber, not an NSCFString, which was my problem. I was able to log the default integer values for the flash modes:

    Code:

    [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:
    [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo] 
    completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error)
    {
        if (imageSampleBuffer && !error)
        {
            CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL, imageSampleBuffer, kCMAttachmentMode_ShouldPropagate);
    
            NSDictionary *metadata = [NSDictionary dictionaryWithDictionary:(__bridge NSDictionary *)metadataDict];
    
            CFRelease(metadataDict);
    
            NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
    
            if (metadata && imageData)
            {
                NSMutableDictionary *newMetaData = [NSMutableDictionary dictionaryWithDictionary:metadata];
    
                NSMutableDictionary *exifMetadata = [NSMutableDictionary dictionaryWithDictionary:
                [metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary]];
    
                if (self.isUsingFrontCamera && exifMetadata)
                {
                    if (self.flashButton.flashMode == On)
                    {
                        [exifMetadata setObject:@(9) forKey:(NSString *)kCGImagePropertyExifFlash];
                    }
                    else if (self.flashButton.flashMode == Off)
                    {
                        [exifMetadata setObject:@(16) forKey:(NSString *)kCGImagePropertyExifFlash];
                    }
                    else if (self.flashButton.flashMode == Auto)
                    {
                        [exifMetadata setObject:@(24) forKey:(NSString *)kCGImagePropertyExifFlash];
    
                        if (self.frontFlashAutoDidFire)
                        {
                            [exifMetadata setObject:@(25) forKey:(NSString *)kCGImagePropertyExifFlash];
                        }
                    }
                }
    
                [newMetaData setObject:exifMetadata forKey:(NSString *)kCGImagePropertyExifDictionary];
    
                [ALAssetsLibrary.new writeImageDataToSavedPhotosAlbum:imageData metadata:newMetaData completionBlock:nil];
            }
        }
    }