我正在参加「启航计划」

前言

项目接入了 unity ,想找一下突破点,到现在功能也是越做越多,前段时刻都忙着开发,现在偷得浮生半日闲,也来写下与 unity 之间发生的有趣事情。上一篇写的是 iOS嵌入虚拟引擎unity3d ,有爱好的小伙伴也能够去看下。下面就写了2个例子来聊聊。

dylib

其时呢,项目是想做一个根据声音来让虚拟偶像动嘴的功能,有点像游戏直播里边弄一个动物代替自己。

我 与 unity 之间有趣的事情

只要人说话,它的动物嘴就会张开,大体如此吧。

一开始,安卓是先和 unity 调研的,然后发现了一个插件,是又支撑 iOS 又支撑安卓, 然后联调起来,2个端呢,效果都不错,好事对吧。接着就是一顿开发,忘乎所以。

最后当咱们上线要提交到 AppStore 审核,就杯具了。

ITMS-90429: Invalid Swift Support - The files libswiftDarwin.dylib, libswiftMetal.dylib, libswiftCoreAudio.dylib, libswiftsimd.dylib, libswiftQuartzCore.dylib, libswiftos.dylib, libswiftObjectiveC.dylib, libswiftDispatch.dylib, libswiftAccelerate.dylib, libswiftCoreGraphics.dylib, libswiftCoreFoundation.dylib, libswiftUIKit.dylib, libswiftCoreMedia.dylib, libswiftAVFoundation.dylib, libswiftCore.dylib, libswiftFoundation.dylib, libswiftCoreImage.dylib aren’t at the expected location /Payload/App.app/Frameworks. Move the file to the expected location, rebuild your app using the current public (GM) version of Xcode, and resubmit it.

然后才发现,unity 运用的插件是个动态库。。。

完犊子了,苹果是不支撑自己制造的 dylib 上商店了,然后就没有然后了。。。

录制视频

unity 需求录制视频,其时呢也是安卓先去联调的,咱们后边跟着做,还想着能够削减工作量,然后听了方案,是 unity 会一帧一帧传过来,然后客户端再生成一个视频。这方案没有问题,仅仅说怎样一帧一帧的接纳,听他们说用 openGL

那好吧,行动起来,咱们也有openGL ES,于是我花了2天去学习了这方面关于纹路的常识。然后再试了不知道多少次后,最后仍是不能把一帧转为图片。急啊!!!然后就无意看到这个。。。

我 与 unity 之间有趣的事情

谁说iOS 运用 openGL ES 去做的呢,这,那我赶忙去看 Metal的东西。

功夫不负有心人,总算查到了如何把纹路转成图片了。

+ (nullable CIImage *)imageWithMTLTexture:(id<MTLTexture>)texture
                                  options:(nullable NSDictionary<CIImageOption, id> *)options NS_AVAILABLE(10_11, 9_0);
// 根据纹路ID 创立图片
- (UIImage *)createImageByMTlTexture:(id)MTLTexture
{
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    NSDictionary* pDic = [NSDictionary dictionaryWithObject:(__bridge_transfer id)colorSpace forKey:kCIImageColorSpace];
    CIImage *outPutImage = [CIImage imageWithMTLTexture:MTLTexture options:nil];
    return [UIImage imageWithCIImage:outPutImage];
}

那转成图片能够,意味着咱们的确是拿到有用信息了。

那这时候咱们要知道的视频写入需求的是CVPixelBufferRef, CVPixelBufferRef是中心视频像素缓冲区。

- (BOOL)appendPixelBuffer:(CVPixelBufferRef)pixelBuffer withPresentationTime:(CMTime)presentationTime;

CMSampleBufferRef又能够转成CVPixelBufferRefCMSampleBufferRef表示一帧音频/视频数据。

CM_EXPORT
CVImageBufferRef CM_NULLABLE CMSampleBufferGetImageBuffer(
	CMSampleBufferRef CM_NONNULL sbuf);

CVPixelBufferRef又能够转成CMSampleBufferRefCMSampleBufferRef这一帧视频数据是包括像素图片类型数据,还需求填写其它信息,如时刻之类的。

//将CVPixelBufferRef转化成CMSampleBufferRef
-(CMSampleBufferRef)pixelBufferToSampleBuffer:(CVPixelBufferRef)pixelBuffer {
    CMSampleBufferRef sampleBuffer;
   CMTime frameTime = CMTimeMakeWithSeconds([[NSDate date] timeIntervalSince1970], 1000000000);
   CMSampleTimingInfo timing = {frameTime, frameTime, kCMTimeInvalid};
   CMVideoFormatDescriptionRef videoInfo = NULL;
   CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixelBuffer, &videoInfo);
   OSStatus status = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, true, NULL, NULL, videoInfo, &timing, &sampleBuffer);
   if (status != noErr) {
       NSLog(@"Failed to create sample buffer with error %d.", (int)status);
   }
   CVPixelBufferRelease(pixelBuffer);
   if(videoInfo)
       CFRelease(videoInfo);
   return sampleBuffer;
}

那最后得出的结论就是只要把纹路idtexture转成CVPixelBufferRef就能够了。

CVPixelBufferRef是一种像素图片类型,咱们设置尺寸巨细为 1080*1920

// 创立CVPixelBufferRef
-(CVPixelBufferRef)createPixelBufferWithSize:(CGSize)size {
    const void *keys[] = {
        kCVPixelBufferMetalCompatibilityKey,
        kCVPixelBufferIOSurfacePropertiesKey,
    };
    const void *values[] = {
        (__bridge const void *)([NSNumber numberWithBool:YES]),
        (__bridge const void *)([NSDictionary dictionary])
    };
    OSType bufferPixelFormat = kCVPixelFormatType_32ARGB;
    CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 2, NULL, NULL);
    CVPixelBufferRef pixelBuffer = NULL;
    CVPixelBufferCreate(kCFAllocatorDefault,
                        size.width,
                        size.height,
                        bufferPixelFormat,
                        optionsDictionary,
                        &pixelBuffer);
    CFRelease(optionsDictionary);
    return pixelBuffer;
}

颜色空间CGColorSpaceRef咱们设置为kCGColorSpaceGenericRGBLinear就能够了。

然后咱们把图烘托到给定的 CVPixelBufferRef就能够了。

- (CVPixelBufferRef)createCVPixelBufferRefByTexture:(id)texture {
    CVPixelBufferRef pixelBuffer = [self createPixelBufferWithSize:CGSizeMake(1080,1920)];
    static CIContext *_ciContext;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear);
    NSDictionary* pDic = [NSDictionary dictionaryWithObject:(__bridge_transfer id)colorSpace forKey:kCIImageColorSpace];
    CIImage *outputImage = [CIImage imageWithMTLTexture:texture options:pDic];
    if(outputImage) {
        if( _ciContext == nil) {
            _ciContext =  [CIContext context];
        }
        [_ciContext render: outputImage toCVPixelBuffer: pixelBuffer bounds:[outputImage extent] colorSpace:CGColorSpaceCreateDeviceRGB()];
    }
    CGColorSpaceRelease(colorSpace);
    return pixelBuffer;
}

通过层层转化,最后咱们处理了如何一帧一帧录制成视频,真不容易啊。

最后

本文写到这儿,除了展示了一些功能个例的注意事项,更想说的对于 unity 嵌入,参阅固然需求,更多仍是需求依靠咱们iOS本身,用自己合适的东西去考虑,去实现才对。