Недопустимые метаданные изображения при попытке отобразить живое фото с помощью PHLivePhotoView target-c

Я пытаюсь загрузить jpg изображение вместе с mov файл с целью-c на устройстве ios для отображения живой фотографии, и я делаю следующий фрагмент кода, чтобы сделать это в viewDidLoad функция:

- (void)viewDidLoad {
    [super viewDidLoad];

    PHLivePhotoView *photoView = [[PHLivePhotoView alloc]initWithFrame:self.view.bounds];

    NSURL *imageUrl = [[NSBundle mainBundle] URLForResource:@"livePhoto" withExtension:@"jpg"];
    NSURL *videoUrl = [[NSBundle mainBundle] URLForResource:@"livePhoto" withExtension:@"mov"];

    [PHLivePhoto requestLivePhotoWithResourceFileURLs:@[videoUrl, imageUrl] placeholderImage:[UIImage imageNamed:@"livePhoto.jpg"] targetSize:self.view.bounds.size contentMode:PHImageContentModeAspectFit resultHandler:^(PHLivePhoto *livePhoto, NSDictionary *info){
        NSLog(@"we are in handler");
        photoView.livePhoto = livePhoto;
        photoView.contentMode = UIViewContentModeScaleAspectFit;
        photoView.tag = 87;
        [self.view addSubview:photoView];
        [self.view sendSubviewToBack:photoView];
    }];


}

Я перетащил файл livePhoto.jpg а также livePhoto.mov в проект Xcode

Но при сборке этого Xcode log эта ошибка:

2017-11-28 17:46:08.568455+0800 Live Photos[3669:1276778] we are in handler
2017-11-28 17:46:08.580439+0800 Live Photos[3669:1276778] we are in handler
2017-11-28 17:46:08.597147+0800 Live Photos[3669:1276806] Error: Invalid image metadata
2017-11-28 17:46:08.607881+0800 Live Photos[3669:1276806] Error: Invalid video metadata
2017-11-28 17:46:08.608329+0800 Live Photos[3669:1276778] we are in handler

Есть идеи по этому поводу? Благодарю.

И еще одна вещь, чтобы спросить:

Почему resultHandler был вызван дважды в зависимости от того, что напечатано?

1 ответ

TL;DR

Вот код для хранения живых фотографий и загрузки их на сервер:
1. Захват живого фото

- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)photoSampleBuffer previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings bracketSettings:(AVCaptureBracketedStillImageSettings *)bracketSettings error:(NSError *)error {
    if (error) {
        [self raiseError:error];
        return;
    }
    NSData *imageData = [AVCapturePhotoOutput JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer previewPhotoSampleBuffer:previewPhotoSampleBuffer];
    CIImage *image = [CIImage imageWithData:imageData];
    [self.expectedAsset addInput:image.properties]; // 1. This is the metadata (which will be lost in step 2.)
    [self.expectedAsset addInput:[UIImage imageWithCIImage:image]]; // 2. Creating image, but UIImage is not designed to contain the required metadata
}
- (void)captureOutput:(AVCapturePhotoOutput *)output 
didFinishProcessingLivePhotoToMovieFileAtURL:(NSURL *)outputFileURL duration:(CMTime)duration photoDisplayTime:(CMTime)photoDisplayTime resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings error:(nullable NSError *)error {
    if (error) {
        [self raiseError:error];
    } else {
        [self.expectedAsset addInput:outputFileURL]; // 3. Store the URL to the actual video file
    }
}

expectedAsset это просто объект, содержащий всю необходимую информацию. Вместо этого вы можете использовать NSDictionary. А так как этот фрагмент кода является>= iOS 11 API, вот тот, который для "устаревшей" iOS...

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error {
    if (error) {
        [self raiseError:error];
    } else {
        [self.expectedAsset addInput:[photo metadata]];
        [self.expectedAsset addInput:[UIImage imageWithData:[photo fileDataRepresentation]]];
    }
}
#pragma clang diagnostic pop 


2. Создать NSData

- (NSData*)imageData {
        NSData *jpgData = UIImageJPEGRepresentation(self.image, 1); // This is the UIImage (without metadata) from step 2 above
        CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) jpgData, NULL);
        NSMutableData *dest_data = [NSMutableData data];
        CFStringRef uti = CGImageSourceGetType(source);
        NSMutableDictionary *maker = [NSMutableDictionary new];
        [maker setObject:[self.imageMetadata objectForKey:(NSString*)kCGImagePropertyMakerAppleDictionary] forKey:(NSString *)kCGImagePropertyMakerAppleDictionary];    // imageMetadata is the dictionary form step 1 above
        CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)dest_data,uti,1,NULL);
        CGImageDestinationAddImageFromSource(destination, source , 0, (__bridge CFDictionaryRef) maker);
        CGImageDestinationFinalize(destination);
        return dest_data;
    }

- (void)dataRepresentation:(DataRepresentationLoaded)callback {
    callback(@{@"image": self.imageData, @"video": [NSData dataWithContentsOfURL:self.livePhotoURL]}); // LivePhotoURL is the url from step 3 above
}

Длинный ответ

Это вызвано неправильными метаданными в файле видео / изображения. При создании живой фотографии PHLivePhoto ищет ключ 17 в kCGImagePropertyMakerAppleDictionary (который является идентификатором актива) и соответствует этому с com.apple.quicktime.content.identifier файла MOV. Файл MOV также должен иметь запись для времени, когда было снято неподвижное изображение (com.apple.quicktime.still-image-time).

Убедитесь, что ваши файлы не были отредактированы (или экспортированы) где-либо. В случае события функция UIImageJPEGRepresentation удалит эти данные из изображения.

Вот фрагмент кода, который я использую для преобразования UIImage в NSData:

- (NSData*)imageData {
    NSData *jpgData = UIImageJPEGRepresentation(self.image, 1);
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) jpgData, NULL);
    NSMutableData *dest_data = [NSMutableData data];
    CFStringRef uti = CGImageSourceGetType(source);
    NSMutableDictionary *maker = [NSMutableDictionary new];
    [maker setObject:[self.imageMetadata objectForKey:(NSString*)kCGImagePropertyMakerAppleDictionary] forKey:(NSString *)kCGImagePropertyMakerAppleDictionary];
    CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)dest_data,uti,1,NULL);
    CGImageDestinationAddImageFromSource(destination, source , 0, (__bridge CFDictionaryRef) maker);
    CGImageDestinationFinalize(destination);
    return dest_data;
}

Обработчик вызывается дважды, чтобы сначала сообщить вам о поврежденных данных, а второй раз - об отмене процесса (это два разных ключа).

РЕДАКТИРОВАТЬ:

Вот ваши данные MOV:

    $ ffmpeg -i cf70b7de66bd89654967aeef1d557816.mov
    Метаданные:
        Major_brand: Qt  
        minor_version: 0
        compatibility_brands: qt  
        Время создания: 2018-01-27Т11:07:38.000000Z
        com.apple.quicktime.content.identifier: cf70b7de66bd89654967aeef1d557816
      Продолжительность: 00: 00: 15.05, старт: 0.000000, битрейт: 1189 кбит / с
        Поток #0:0(und): видео: h264 (высокий) (avc1 / 0x31637661), yuv420p(прогрессивный), 540x960, 1051 кбит / с, 29,84 кадра в секунду, 29,97 тб, 30 тыс. Тбит, 59,94 тбк (по умолчанию)
        Метаданные:
          Время создания: 2018-01-27Т11: 07: 38.000000Z
          имя обработчика: основной обработчик данных
          кодировщик: 'avc1'
        Поток #0:1(und): Аудио: aac (LC) (mp4a / 0x6134706D), 44100 Гц, стерео, fltp, 128 кбит / с (по умолчанию)
        Метаданные:
          Время создания: 2018-01-27Т11: 07: 38.000000Z
          имя обработчика: основной обработчик данных

com.apple.quicktime.still-image-time ключ здесь отсутствует

Вот метаданные, как это должно выглядеть:

    Метаданные:
        Major_brand: Qt  
        minor_version: 0
        compatibility_brands: qt  
        Время создания: 2017-12-15Т12:41:00.000000Z
        com.apple.quicktime.content.identifier: 89CB44DA-D129-43F3-A0BC-2C980767B810
        com.apple.quicktime.location.ISO6709: +51,5117+007,4668+086,000/
        com.apple.quicktime.make: Apple
        com.apple.quicktime.model: iPhone X
        com.apple.quicktime.software: 11.1.2
        com.apple.quicktime.creationdate: 2017-12-15T13:41:00+0100
      Длительность: 00:00:01.63, старт: 0,000000, битрейт: 8902 кбит / с
        Поток #0:0(und): Видео: h264 (High) (avc1 / 0x31637661), yuvj420p(pc, smpte170m/smpte432/bt709), 1440x1080, 8135 кбит / с, 26,94 к / с, 30 тб, 600 тбн, 1200 т / с (дефолт)
        Метаданные:
          повернуть: 90
          Время создания: 2017-12-15Т12: 41: 00.000000Z
          имя обработчика: основной обработчик данных
          кодировщик: H.264
        Дополнительные данные:
          displaymatrix: вращение -90,00 градусов
        Поток #0:1(und): аудио: pcm_s16le (lpcm / 0x6D63706C), 44100 Гц, моно, s16, 705 кбит / с (по умолчанию)
        Метаданные:
          Время создания: 2017-12-15Т12: 41: 00.000000Z
          имя обработчика: основной обработчик данных
        Поток № 0:2(und): Данные: нет (mebx / 0x7862656D), 12 Кбит / с (по умолчанию)
        Метаданные:
          Время создания: 2017-12-15Т12: 41: 00.000000Z
          имя обработчика: основной обработчик данных
        Поток № 0:3(отмена): данные: нет (mebx / 0x7862656D), 43 кбит / с (по умолчанию)
        Метаданные:
          Время создания: 2017-12-15Т12: 41: 00.000000Z
          имя обработчика: основной обработчик данных

И только к вашему сведению, вот ваши данные JPEG:

    $ магический идентификатор -формат%[EXIF:*] cf70b7de66bd89654967aeef1d557816.jpg
    EXIF:ColorSpace=1
    EXIF:ExifImageLength=960
    EXIF:ExifImageWidth=540
    EXIF:ExifOffset=26
    exif:MakerNote=65, 112, 112, 108, 101, 32, 105, 79, 83, 0, 0, 1, 77, 77, 0, 1, 0, 17, 0, 2, 0, 0, 0, 33, 0, 0, 0, 32, 0, 0, 0, 0, 99, 102, 55, 48, 98, 55, 100, 101, 54, 54, 98, 100, 56, 57, 54, 53, 52, 57, 54, 55, 97, 101, 101, 102, 49, 100, 53, 53, 55, 56, 49, 54, 0, 0
Другие вопросы по тегам