で奇妙なメモリリークが発生しまし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に通して、次のことに気づきました。
すぐに証明するように、オーディオバイトはメモリに保持されています。それがメモリの増加です。オーディオバイトは、呼び出した後にのみ解放されます[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形式を実行している可能性はありますか?それがバッファリングされている理由ですか?