【问题标题】:Invalid image metadata when try to display a livephoto with PHLivePhotoView objective-c尝试使用 PHLivePhotoView Objective-c 显示实时照片时图像元数据无效
【发布时间】:2018-05-11 17:24:12
【问题描述】:

我正在尝试在 ios 设备上使用objective-c 加载jpg 图像和mov 文件以显示实时照片,我在viewDidLoad 函数中编写了以下代码sn-p 来执行此操作:

- (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.jpglivePhoto.mov 拖到 Xcode 项目中

但是在构建这个 Xcode 时会记录这个错误:

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根据打印的内容被调用了两次?

【问题讨论】:

    标签: ios objective-c xcode phlivephoto


    【解决方案1】:

    TL;DR

    以下是存储 Live Photos 并将其上传到服务器的代码:
    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。由于此代码 sn-p 是 >= 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 会在 kCGImagePropertyMakerAppleDictionary(资产标识符)中搜索键 17,并将其与 mov 文件的 com.apple.quicktime.content.identifier 匹配。 mov 文件还需要有一个用于捕获静止图像的时间的条目 (com.apple.quicktime.still-image-time)。

    确保您的文件没有在某处被编辑(或导出)。事件 UIImageJPEGRepresentation 函数将从图像中删除此数据。

    这是我用来将 UIImage 转换为 NSData 的代码 sn-p:

    - (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 元数据: 主要品牌:qt 次要版本:0 兼容品牌:qt 创建时间:2018-01-27T11:07:38.000000Z com.apple.quicktime.content.identifier:cf70b7de66bd89654967aeef1d557816 持续时间:00:00:15.05,开始:0.000000,比特率:1189 kb/s 流 #0:0(und):视频:h264(高)(avc1 / 0x31637661)、yuv420p(渐进式)、540x960、1051 kb/s、29.84 fps、29.97 tbr、30k tbn、59.94 tbc(默认) 元数据: 创建时间:2018-01-27T11:07:38.000000Z handler_name : 核心媒体数据处理程序 编码器:'avc1' 流 #0:1(und):音频:aac (LC) (mp4a / 0x6134706D),44100 Hz,立体声,fltp,128 kb/s(默认) 元数据: 创建时间:2018-01-27T11:07:38.000000Z handler_name : 核心媒体数据处理程序

    这里缺少com.apple.quicktime.still-image-time 键。

    以下是元数据的外观:

    元数据: 主要品牌:qt 次要版本:0 兼容品牌:qt 创建时间:2017-12-15T12: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:苹果 com.apple.quicktime.model: iPhone X com.apple.quicktime.software:11.1.2 com.apple.quicktime.creation 日期:2017-12-15T13:41:00+0100 持续时间:00:00:01.63,开始:0.000000,比特率:8902 kb/s 流 #0:0(und):视频:h264(高)(avc1 / 0x31637661)、yuvj420p(pc、smpte170m/smpte432/bt709)、1440x1080、8135 kb/s、26.94 fps、30 tbr、600 tbn、1200 tbc (默认) 元数据: 旋转:90 创建时间:2017-12-15T12:41:00.000000Z handler_name : 核心媒体数据处理程序 编码器:H.264 侧面数据: 显示矩阵:旋转 -90.00 度 流 #0:1(und):音频:pcm_s16le (lpcm / 0x6D63706C),44100 Hz,单声道,s16,705 kb/s(默认) 元数据: 创建时间:2017-12-15T12:41:00.000000Z handler_name : 核心媒体数据处理程序 流 #0:2(und):数据:无(mebx / 0x7862656D),12 kb/s(默认) 元数据: 创建时间:2017-12-15T12:41:00.000000Z handler_name : 核心媒体数据处理程序 流 #0:3(und):数据:无(mebx / 0x7862656D),43 kb/s(默认) 元数据: 创建时间:2017-12-15T12:41:00.000000Z handler_name : 核心媒体数据处理程序

    仅供参考,这是您的 JPEG 数据:

    $ magick identify -format %[EXIF:*] cf70b7de66bd89654967aeef1d557816.jpg exif:色彩空间=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

    【讨论】:

    • 感谢您的回答,现在我发现即使我将所有内容都正确设置(图像的17com.apple.quicktime.content.identifier视频)
    • JPEG 看起来不错,因为我已经用其他 .mov 文件测试了 JPEG 文件,它们可以一起工作
    • 你能上传两个文件吗?
    • 当然,图片是here,视频是here
    • 在上面编辑了我的答案。你是怎么把mov文件传到你的电脑上的?导出时密钥可能丢失
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-05
    • 2017-09-12
    • 2021-11-03
    • 2020-07-02
    • 1970-01-01
    • 2019-04-20
    相关资源
    最近更新 更多