1

ビデオフレームをムービーに書き込むために、単一のビデオ入力で AVAssetWriter を使用しています。書き込みループの仕組みは問題ではありませんが、メモリ管理が問題であることがわかりました。

追加している CMSampleBufferRef に関して: a) いくつかの画像データ (生のバイト) から CVPixelBufferRef を作成します。したがって、CVPBR がデータを所有します。b) 次に、これを次のように CMSampleBufferRef にラップします (簡潔にするためにエラー チェックを削除しました)。

    + (CMSampleBufferRef)wrapImageBufferInCMSampleBuffer:(CVImageBufferRef)imageBuffer timingInfo:(CMSampleTimingInfo const *)timingInfo error:(NSError **)error {
    // Create a format description for the pixel buffer
        CMVideoFormatDescriptionRef formatDescription = 0;
        OSStatus result = CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, imageBuffer, &formatDescription);

        // Finally, create a CMSampleBuffer wrapper around it all
        CMSampleBufferRef sampleBuffer = nil;
        OSStatus sampleBufferResult = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, imageBuffer, YES, nil, nil, formatDescription, timingInfo, &sampleBuffer);

        if (formatDescription) {
            CFRelease(formatDescription);
        }

        return sampleBuffer;
    }

これにより、AVWriter に渡すことができるバッファーが作成されます。ここまでは順調ですね。問題は、バッファーをライターに追加した後で単純にバッファーを解放すると、奇妙な歪みが発生することです。

私がこれを行う場合:

    [avInput appendSampleBuffer:delayedBuffer]
    CFRelease(sampleBuffer);
    CFRelease(pixelBuffer);

その後、動作し、メモリ リークはありませんが、破損したフレームが表示されることがあります。フレーム 391,394 で時々同期フレームが外れていること、および明らかなメモリ破損があることに注意してください。私には、AVF がエンコードを完了する前にメモリ バッファが解放されているように見えます。

CFRelease(pixelBuffer) を削除すると、問題は解決します。結果のムービーは完全に滑らかで、破損はまったくありません。もちろん; 次に、数GBのメモリリークの問題があります!

他の誰かがこのようなものに出くわしましたか?

ところで: AVAssetWriterInputPixelBufferAdaptor を使用しても問題ありません。同じ結果が得られます。

問題を再現する完全なコード スニペットを次に示します。

- (void)recordMovieUsingStandardAVFTo:(NSURL *)url colorSequence:(NSArray *)colorSequence frameDuration:(CMTime)frameDuration size:(NSSize)size {
    NSError *error = nil;
    AVAssetWriter *writer = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeQuickTimeMovie error:&error];
    if ([self checkForError:error doing:@"creation of asset writer"] == NO) {
        return;
    }

    NSMutableDictionary *videoSettings = [NSMutableDictionary dictionary];
    [videoSettings setValue:[NSNumber numberWithLong:(long) size.width] forKey:AVVideoWidthKey];
    [videoSettings setValue:[NSNumber numberWithLong:(long) size.height] forKey:AVVideoHeightKey];
    [videoSettings setValue:AVVideoCodecH264 forKey:AVVideoCodecKey];

    AVAssetWriterInput *videoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];

    [writer addInput:videoInput];
    [writer startWriting];
    [writer startSessionAtSourceTime:kCMTimeZero];

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        NSError *localError = nil;
        CMTime frameTime = kCMTimeZero;
        int frameCounter = 0;
        for (int i = 0; i < 4; i++) {
            @autoreleasepool {
                for (NSColor *color in colorSequence) {
                    CMSampleTimingInfo timing = kCMTimingInfoInvalid;
                    timing.presentationTimeStamp = frameTime;
                    timing.duration = frameDuration;

                    while (videoInput.isReadyForMoreMediaData == NO) {
                        [NSThread sleepForTimeInterval:0.1];
                    }

                    CVPixelBufferRef pixelBuffer = [self createPixelBufferBufferOfColor:color size:size];
                    CMSampleBufferRef sampleBuffer = [OSGUtils wrapImageBufferInCMSampleBuffer:pixelBuffer timingInfo:&timing error:&localError];

                    BOOL recordingSuccess = [videoInput appendSampleBuffer:sampleBuffer];
                    if (recordingSuccess) {
                        frameTime = CMTimeAdd(frameTime, frameDuration);
                        frameCounter++;
                        if (frameCounter % 60 == 0) {
                            ApplicationLogInfo(@"Wrote frame at time %@", [NSString niceStringForCMTime:frameTime]);
                        }
                    } else {
                        ApplicationLogError(@"Can't write frame at time %@", localError);
                    }

                    CFRelease(sampleBuffer);
                    CFRelease(pixelBuffer);
                }
            }
        }

        [videoInput markAsFinished];
        [writer endSessionAtSourceTime:frameTime];
        BOOL success = [writer finishWriting];
        if (!success) {
            ApplicationLogError(@"Failed to finish writing, %@, %d", writer.error, writer.status);
        } else {
            ApplicationLogInfo(@"Write complete");
        }
    });
}
4

0 に答える 0