2013-02-13 8 views
18

Mam dziwny "wyciek" pamięci z AVAssetWriterInput appendSampleBuffer. Piszę wideo i audio w tym samym czasie, więc mam jeden AVAssetWriter z dwoma wejściami, jeden dla wideo i jeden dla audio:appendSampleBuffer z dźwiękiem AVAssetWriterInput "wycieka" pamięć do czasu zakończenia endSessionAtSourceTime

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

zacznę pisać i wszystko działa poprawnie na powierzchni. Wideo i audio uzyskać pisemną i są wyrównane, itd. Jednak Kładę kodu za pośrednictwem instrumentu Allocations i zauważył, co następuje:

CoreMedia allocations

Bajty audio są coraz zachowane w pamięci, a ja udowodnię za sekundę. To wzrost w pamięci. Bajty audio są wydawane dopiero po wywołaniu [self.videoWriter endSessionAtSourceTime:...], co można uznać za dramatyczny spadek wykorzystania pamięci. Tu jest mój kodu pisanie dźwięku, które jest wysyłane jako blok na kolejce szeregowego:

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

Jak widać, jestem zwalniając każdy bufor raz utworzyć. Mam prześledzić „przeciek” do linii, gdzie bufory audio załączonym:

[self.audioWriterInput appendSampleBuffer:sampleBuffer] 

udowodniłem to sobie przez zakomentowanie tę linię, po której pojawia się następujący „leak-free” przydziały wykres (mimo że nagrany film ma teraz żadnego dźwięku teraz, oczywiście):

No leak

próbowałem jedną rzecz, która ma dodać z powrotem linię appendSamplebuffer i zamiast podwójnego uwalniania blockBuffer2:

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

Spowoduje to zrobił nie przyczyną podwójnie za darmo, wskazując, że blockBuffer2 „s zachować liczyć w tym momencie jest 2. produkowane wykres same«szczelny»przydziałów, z tą różnicą, że gdy zadzwoniłem [self.videoWriter endSessionAtSourceTime:...], Wystąpił błąd z podwójnego wydania (wskazując, że self.videoWriter próbuje zwolnić wszystkie swoje wskaźniki do blockBuffer2 s, które zostały przekazane).

Jeśli zamiast tego spróbuj wykonać następujące czynności:

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

następnie [self.audioWriterInput appendSampleBuffer:sampleBuffer]i wezwanie do dołączania klatki wideo rozpocznie się niepowodzeniem dla każdej rozmowy po tym.

Mój wniosek jest taki, że AVAssetWriter lub AVAssetWriterInput zachowuje blockBuffer2, dopóki film nie zakończy nagrania. Oczywiście może to powodować problemy z pamięcią rzeczywistą, jeśli nagranie trwa wystarczająco długo. czy robię coś źle?

Edytuj: Bajt audio, który otrzymuję, jest w formacie PCM, natomiast format wideo, który piszę, to MPEG4, a format audio tego filmu to MPEG4AAC. Czy jest możliwe, że program zapisujący wideo wykonuje w locie format PCM -> AAC, i dlatego jest buforowany?

+0

Jestem pewien, że rozejrzałeś się, ale widziałeś te dwa pytania/odpowiedzi. Mogą być pomocne/powiązane. 1) http://stackoverflow.com/questions/4914853/help-fix-memory-leak-release 2) http://stackoverflow.com/questions/11274652/performance-issues-when-using-avcapturevideodataoutput-and-avcaptureaudiodataout – JSuar

+0

Dzięki za sugestie @JSuar. Próbowałem już pierwszego, ale wywołanie CMSampleBufferInvalidate wydaje się łamać wszystkie przyszłe zapisy do twórcy filmów. Drugi wygląda interesująco, ale nie jestem pewien, czy współbieżne lub kolejki szeregowe wytłumaczyłyby "wyciek" pamięci, który wydaje się być spowodowany nagrywaniem wideo bloków audio. – kevlar

+0

Czy to możliwe, że po prostu piszesz dźwięk znacznie szybciej niż wideo, więc musi być buforowany, aby zachować prawidłowe przeplatanie? –

Odpowiedz

1

Ponieważ czekałeś miesiąc na odpowiedź, dam Ci mniej niż idealną, ale wykonalną odpowiedź.

Można użyć funkcji ExtendedAudioFile do napisania osobnego pliku. Wtedy możesz po prostu odtwarzać wideo i audio razem z AVComposition. Myślę, że możesz użyć AVFoundation do złożenia caf i wideo razem bez ponownego kodowania, jeśli potrzebujesz ich złożonego na końcu nagrania.

To sprawi, że będziesz działać i będziesz mógł rozwiązać problem wycieku pamięci w wolnym czasie.

Powiązane problemy