7

ここで見つけたコードを使用して、UIImage からビデオを生成するプロジェクトに取り組んでおり、最適化するために数日間苦労しています (約 300 の画像の場合、シミュレーターで 5 分ほどかかります。メモリが原因でデバイスでクラッシュするだけです)。

私は今日持っている作業コードから始めます(私はarcで作業しています):

-(void) writeImageAsMovie:(NSArray *)array toPath:(NSString*)path size:(CGSize)size duration:(int)duration 
{
    NSError *error = nil;
    AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path] fileType:AVFileTypeQuickTimeMovie error:&error];
    NSParameterAssert(videoWriter);

    NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                               AVVideoCodecH264, AVVideoCodecKey,
                               [NSNumber numberWithInt:size.width], AVVideoWidthKey,
                               [NSNumber numberWithInt:size.height], AVVideoHeightKey,
                               nil];
    AVAssetWriterInput* writerInput = [AVAssetWriterInput
                                   assetWriterInputWithMediaType:AVMediaTypeVideo
                                   outputSettings:videoSettings];

    AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:nil];
    NSParameterAssert(writerInput);
    NSParameterAssert([videoWriter canAddInput:writerInput]);
    [videoWriter addInput:writerInput];


    //Start a session:
    [videoWriter startWriting];
    [videoWriter startSessionAtSourceTime:kCMTimeZero];

    CVPixelBufferRef buffer = NULL;
    buffer = [self newPixelBufferFromCGImage:[[self.frames objectAtIndex:0] CGImage]];

    CVPixelBufferPoolCreatePixelBuffer (NULL, adaptor.pixelBufferPool, &buffer);

    [adaptor appendPixelBuffer:buffer withPresentationTime:kCMTimeZero];

    dispatch_queue_t mediaInputQueue =  dispatch_queue_create("mediaInputQueue", NULL);
    int frameNumber = [self.frames count];

    [writerInput requestMediaDataWhenReadyOnQueue:mediaInputQueue usingBlock:^{
        NSLog(@"Entering block with frames: %i", [self.frames count]);
        if(!self.frames || [self.frames count] == 0)
        {
            return;
        }
        int i = 1;
        while (1)
        {
            if (i == frameNumber) 
            {
                break;
            }
            if ([writerInput isReadyForMoreMediaData]) 
            {
                freeMemory();
                NSLog(@"inside for loop %d (%i)",i, [self.frames count]);
                UIImage *image = [self.frames objectAtIndex:i];
                CGImageRef imageRef = [image CGImage];
                CVPixelBufferRef sampleBuffer = [self newPixelBufferFromCGImage:imageRef];
                CMTime frameTime = CMTimeMake(1, TIME_STEP);

                CMTime lastTime=CMTimeMake(i, TIME_STEP); 

                CMTime presentTime=CMTimeAdd(lastTime, frameTime);       

                if (sampleBuffer) 
                {
                    [adaptor appendPixelBuffer:sampleBuffer withPresentationTime:presentTime];
                    i++;
                    CVPixelBufferRelease(sampleBuffer);
                } 
                else 
                {
                    break;
                }
            }
        }

        [writerInput markAsFinished];
        [videoWriter finishWriting];
        self.frames = nil;

        CVPixelBufferPoolRelease(adaptor.pixelBufferPool);

    }];
}

そして今、私が苦労しているピクセルバッファを取得する関数:

- (CVPixelBufferRef) newPixelBufferFromCGImage: (CGImageRef) image
{
    CVPixelBufferRef pxbuffer = NULL;

    int width = CGImageGetWidth(image)*2;
    int height = CGImageGetHeight(image)*2;

    NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, [NSNumber numberWithInt:width], kCVPixelBufferWidthKey, [NSNumber numberWithInt:height], kCVPixelBufferHeightKey, nil];
    CVPixelBufferPoolRef pixelBufferPool; 
    CVReturn theError = CVPixelBufferPoolCreate(kCFAllocatorDefault, NULL, (__bridge CFDictionaryRef) attributes, &pixelBufferPool);
    NSParameterAssert(theError == kCVReturnSuccess);
    CVReturn status = CVPixelBufferPoolCreatePixelBuffer(NULL, pixelBufferPool, &pxbuffer);
    NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);

    CVPixelBufferLockBaseAddress(pxbuffer, 0);
    void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
    NSParameterAssert(pxdata != NULL);

    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(pxdata, width,
                                             height, 8, width*4, rgbColorSpace, 
                                             kCGImageAlphaNoneSkipFirst);
    NSParameterAssert(context);
    CGContextDrawImage(context, CGRectMake(0, 0, width, 
                                       height), image);
    CGColorSpaceRelease(rgbColorSpace);
    CGContextRelease(context);

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0);

    return pxbuffer;
}

最初の奇妙な点: この関数でわかるように、幅と高さを 2 倍にする必要があります。そうしないと、結果のビデオがすべて台無しになり、その理由がわかりません (スクリーンショットが役立つ場合は投稿できます。ピクセルは私の画像から来ているようですが、幅が正しくなく、ビデオの下半分に大きな黒い四角があります)。

もう 1 つの問題は、非常に大量のメモリを必要とすることです。ピクセル バッファの割り当てをうまく解除できなかったと思いますが、その理由はわかりません。

最後に、非常に遅いですが、改善するためのアイデアが 2 つありますが、使用できません。

  • 1 つ目は、(uint8_t *) データを使用して自分で UIImage を生成するため、UIImage を使用してピクセル バッファーを作成しないようにすることです。「CVPixelBufferCreateWithBytes」を使用しようとしましたが、うまくいきません。これが私が試した方法です:

    OSType pixFmt = CVPixelBufferGetPixelFormatType(pxbuffer);
    CVPixelBufferCreateWithBytes(kCFAllocatorDefault, width, height, pixFmt, self.composition.srcImage.resultImageData, width*2, NULL, NULL, (__bridge CFDictionaryRef) attributes, &pxbuffer);
    

(引数は上記の関数と同じです。私の画像データはピクセルあたり 16 ビットでエンコードされており、関数に与える適切な OSType 引数が見つかりませんでした。) 誰かがそれを使用する方法を知っている場合 (おそらくそれは16ビット/ピクセルデータでは不可能ですか?)、本当に役に立たない変換を避けるのに役立ちます.

  • 2 つ目は、ビデオに kCVPixelFormatType_32ARGB を使用したくないということです。ビット/ピクセルが少ないものを使用する方が速いと思いますが、試してみると(5ビット/コンポーネントとkCGImageAlphaNoneSkipFirstで作成されたコンテキストですべてのkCVPixelFormatType_16XXXXXフォーマットを試しました)、クラッシュするか、結果のビデオ何も含まれていません (kCVPixelFormatType_16BE555 を使用)。

たった1回の投稿で多くのことを尋ねていることは知っていますが、このコードではちょっと迷っています。非常に多くの組み合わせを試しましたが、どれもうまくいきませんでした...

4

1 に答える 1

0

I have to multiply the width and height by 2, otherwise, the result video is all messed up, and I can't understand why

ポイントとピクセル?高 dpi Retina スクリーンでは、ポイントあたりのピクセル数が 2 倍になります。

于 2016-02-11T16:05:28.247 に答える