18

で奇妙なメモリリークが発生しましAVAssetWriterInput appendSampleBufferた。私はビデオとオーディオを同時に書いているのでAVAssetWriter、1つはビデオ用、もう1つはオーディオ用の2つの入力があります。

self.videoWriter = [[[AVAssetWriter alloc] initWithURL:[self.currentVideo currentVideoClipLocalURL]
                                              fileType:AVFileTypeMPEG4
                                                 error:&error] autorelease];
...
self.videoWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo
                                                           outputSettings:videoSettings];
self.videoWriterInput.expectsMediaDataInRealTime = YES;
[self.videoWriter addInput:self.videoWriterInput];
...
self.audioWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio
                                                           outputSettings:audioSettings];
self.audioWriterInput.expectsMediaDataInRealTime = YES;
[self.videoWriter addInput:self.audioWriterInput];

私は書き始め、表面上はすべてうまくいきます。ビデオとオーディオが書き込まれ、位置合わせされるなど。ただし、コードをAllocations Instrumentに通して、次のことに気づきました。

CoreMediaの割り当て

すぐに証明するように、オーディオバイトはメモリに保持されています。それがメモリの増加です。オーディオバイトは、呼び出した後にのみ解放されます[self.videoWriter endSessionAtSourceTime:...]。これは、メモリ使用量の劇的な低下と見なされます。これが私のオーディオ書き込みコードで、ブロックとしてシリアルキューにディスパッチされます。

@autoreleasepool
{
    // The objects that will hold the audio data
    CMSampleBufferRef sampleBuffer;
    CMBlockBufferRef  blockBuffer1;
    CMBlockBufferRef  blockBuffer2;

    size_t nbytes = numSamples * asbd_.mBytesPerPacket;

    OSStatus status = noErr;
    status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,
                                                data,
                                                nbytes,
                                                kCFAllocatorNull,
                                                NULL,
                                                0,
                                                nbytes,
                                                kCMBlockBufferAssureMemoryNowFlag,
                                                &blockBuffer1);

    if (status != noErr)
    {
        NLog(@"CMBlockBufferCreateWithMemoryBlock error at buffer 1");
        return;
    }

    status = CMBlockBufferCreateContiguous(kCFAllocatorDefault,
                                           blockBuffer1,
                                           kCFAllocatorDefault,
                                           NULL,
                                           0,
                                           nbytes,
                                           kCMBlockBufferAssureMemoryNowFlag | kCMBlockBufferAlwaysCopyDataFlag,
                                           &blockBuffer2);

    if (status != noErr)
    {
        NSLog(@"CMBlockBufferCreateWithMemoryBlock error at buffer 2");
        CFRelease(blockBuffer1);
        return;
    }

    // Finally, create the CMSampleBufferRef
    status = CMAudioSampleBufferCreateWithPacketDescriptions(kCFAllocatorDefault,
                                                             blockBuffer2,
                                                             YES,   // Yes data is ready
                                                             NULL,  // No callback needed to make data ready
                                                             NULL,
                                                             audioFormatDescription_,
                                                             1,
                                                             timestamp,
                                                             NULL,
                                                             &sampleBuffer);


    if (status != noErr)
    {
        NSLog(@"CMAudioSampleBufferCreateWithPacketDescriptions error.");
        CFRelease(blockBuffer1);
        CFRelease(blockBuffer2);
        return;
    }

    if ([self.audioWriterInput isReadyForMoreMediaData])
    {
        if (![self.audioWriterInput appendSampleBuffer:sampleBuffer])
        {
            NSLog(@"Couldn't append audio sample buffer: %d", numAudioCallbacks_);
        }
    } else {
        NSLog(@"AudioWriterInput isn't ready for more data.");
    }

    // One release per create
    CFRelease(blockBuffer1);
    CFRelease(blockBuffer2);
    CFRelease(sampleBuffer);
}

ご覧のとおり、作成ごとに1回ずつ各バッファーを解放しています。オーディオバッファが追加されている行まで「リーク」を追跡しました。

[self.audioWriterInput appendSampleBuffer:sampleBuffer]

その行をコメントアウトすることでこれを証明しました。その後、次の「リークのない」割り当てグラフが表示されます(もちろん、録画されたビデオには音声がありません)。

漏れなし

もう1つ試してみました。それは、appendSamplebuffer行を追加し直して、代わりにダブルリリースすることblockBuffer2です。

CFRelease(blockBuffer1);
CFRelease(blockBuffer2);
CFRelease(blockBuffer2); // Double release to test the hypothesis that appendSamplebuffer is retaining this
CFRelease(sampleBuffer);

これを行ってもダブルフリーは発生しませんでしblockBuffer2た。これは、その時点での保持カウントが2であることを示しています。これにより、同じ「リークフリー」割り当てグラフが生成されまし[self.videoWriter endSessionAtSourceTime:...]た。 release(渡されself.videoWriterたsへのすべてのポインタを解放しようとしblockBuffer2ていることを示します)。

代わりに、次のことを試してみます。

CFRelease(blockBuffer1);
CFRelease(blockBuffer2);
CMSampleBufferInvalidate(sampleBuffer); // Invalidate sample buffer
CFRelease(sampleBuffer);

その後[self.audioWriterInput appendSampleBuffer:sampleBuffer] ビデオフレームを追加する呼び出しは、その後のすべての呼び出しで失敗し始めます。

だから私の結論は、ビデオが録画を終了するまで、AVAssetWriterまたはAVAssetWriterInput保持しているということです。blockBuffer2明らかに、ビデオが十分に長く記録されている場合、これは実際のメモリの問題を引き起こす可能性があります。私は何か間違ったことをしていますか?

編集:私が取得しているオーディオバイトはPCM形式ですが、私が書いているビデオ形式はMPEG4であり、そのビデオのオーディオ形式はMPEG4AACです。ビデオライターがその場でPCM->AAC形式を実行している可能性はありますか?それがバッファリングされている理由ですか?

4

1 に答える 1

1

回答を 1 か月待っているので、理想的とは言えませんが実行可能な回答を差し上げます。

ExtendedAudioFile 関数を使用して、別のファイルを書き込むことができます。次に、AVComposition と一緒にビデオとオーディオを再生するだけです。AVFoundation を使用して、記録の最後に合成する必要がある場合は、再エンコードせずにカフェとビデオを合成できると思います。

これですぐに実行できるようになり、自由にメモリリークを解決できます。

于 2013-03-20T17:16:37.033 に答える