2013-02-13 11 views
18

Ich habe einen seltsamen Speicher "Leck" mit AVAssetWriterInput appendSampleBuffer. Ich schreibe Video und Audio in der gleichen Zeit, also habe ich ein AVAssetWriter mit zwei Eingängen, eine für Video und einen für Audio:appendSampleBuffer mit einem audio AVAssetWriterInput "leckt" Speicher bis EndeSessionAtSourceTime

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]; 

Ich fange an zu schreiben und alles funktioniert auf der Oberfläche in Ordnung. Die Video- und Audio erhalten geschrieben und ausgerichtet sind, usw. Aber ich meinen Code durch die Zuteilungen Instrument setzen und bemerkte folgendes:

CoreMedia allocations

Die Audio-Bytes werden immer in Erinnerung behalten, wie ich beweisen werde in einer Sekunde. Das ist die Steigerung in Erinnerung. Die Audio-Bytes werden nur freigegeben, nachdem ich [self.videoWriter endSessionAtSourceTime:...] aufrufen, die Sie als den dramatischen Rückgang der Speichernutzung sehen. Hier ist mein Audio-Schreiben von Code, der als Block auf eine serielle Warteschlange geschickt wird:

@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); 
} 

Wie Sie sehen, ich bin Loslassen jeden Puffer einmal pro erstellen. Ich habe das „Leck“ bis auf die Linie verfolgt, wo der Audio-Puffer angehängt werden:

[self.audioWriterInput appendSampleBuffer:sampleBuffer] 

ich dies, dass die Linie durch Kommentare aus mir bewiesen, nach dem ich die folgenden „leckagefrei“ Belegungen Graph erhalten (obwohl das aufgezeichnete Video jetzt keinen Ton hat jetzt natürlich):

No leak

ich habe versucht, eine andere Sache, die die appendSamplebuffer Zeilen hinzufügen zurück ist und stattdessen Doppelrelease blockBuffer2:

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

diese tat Doing nicht Ursache ein zwei frei, was darauf hinweist, dass blockBuffer2 ‚s Zahl an diesem Punkt 2. Dies hat die gleichen‚leckagefrei‘Zuteilungen Graph erzeugt wird beibehalten, mit der Ausnahme, dass, wenn ich rief [self.videoWriter endSessionAtSourceTime:...], bekomme ich einen Absturz von einem Doppel-Release (zeigt an, dass self.videoWriter versucht, alle seine Zeiger auf die blockBuffer2 s, die übergeben wurden) zu veröffentlichen.

Wenn stattdessen versuche ich folgendes:

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

dann [self.audioWriterInput appendSampleBuffer:sampleBuffer]und der Anruf Video-Frames zu anhängen beginnen für jeden Anruf danach zum Scheitern verurteilt.

Also meine Schlussfolgerung ist, dass AVAssetWriter oder AVAssetWriterInputblockBuffer2 behält, bis das Video die Aufnahme beendet hat. Offensichtlich kann dies zu echten Speicherproblemen führen, wenn das Video lange genug aufnimmt. Mache ich etwas falsch?

Bearbeiten: Die Audio-Bytes, die ich bekomme, sind PCM-Format, während das Video-Format, das ich schreibe, MPEG4 ist und das Audio-Format für dieses Video ist MPEG4AAC. Ist es möglich, dass der Video-Brenner das PCM -> AAC-Format im laufenden Betrieb ausführt, und deshalb wird es gepuffert?

+0

Ich bin sicher, du hast dich umgesehen, aber hast du diese zwei Fragen/Antworten gesehen? Sie könnten hilfreich sein. 1) http://stackoverflow.com/questions/4914853/help-fix-memory-leak-release 2) http://stackoverflow.com/questions/11274652/performance-issues-when-using-avcapturevideodataoutput-and-avcaptureaudiatout – JSuar

+0

Danke für die Vorschläge @JSuar. Ich habe bereits das erste versucht, aber das Aufrufen von CMSampleBufferInvalidate scheint alle zukünftigen Schreibvorgänge in den Video-Writer zu bringen. Die zweite sieht interessant aus, aber ich bin mir nicht sicher, ob gleichzeitige oder serielle Warteschlangen das "Speicherleck" des Speichers erklären würden, das durch das Schreiben der Audioblöcke verursacht wird. – kevlar

+0

Ist es möglich, dass Sie das Audiomaterial viel schneller schreiben als das Video, so dass es gepuffert werden muss, damit das Interleave korrekt bleibt? –

Antwort

1

Da Sie seit einem Monat auf eine Antwort warten, gebe ich Ihnen eine weniger als ideale, aber praktikable Antwort.

Sie könnten die ExtendedAudioFile-Funktionen verwenden, um eine separate Datei zu schreiben. Dann könnten Sie einfach das Video und Audio zusammen mit einer AVComposition wiedergeben. Ich denke, dass Sie in der Lage sein, AVFoundation zu verwenden, um das CAF und das Video ohne Neucodierung zusammenzusetzen, wenn Sie sie am Ende der Aufnahme zusammensetzen müssen.

Das bringt Sie zum Laufen, dann können Sie das Speicherleck in Ihrer Freizeit lösen.