appendicsamplebuffer avec une mémoire audio AVAssetWriterInput "leaks" jusqu'à la finsessionats sourcetime

j'ai un étrange fuite de mémoire" avec AVAssetWriterInput appendSampleBuffer. J'écris vidéo et audio en même temps, donc j'en ai un AVAssetWriter avec deux entrées, une pour la vidéo et un pour l'audio:

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

je commence à écrire et tout fonctionne bien sur la surface. La vidéo et l'audio s'écrivent et sont alignés, etc. Cependant, j'ai mis mon code dans L'instrument des Allocations et j'ai remarqué ce qui suit:

CoreMedia allocations

les octets audio sont retenus mémoire, comme je le prouverai dans une seconde. C'est la montée en puissance de la mémoire. Les octets audio ne sont libérés qu'après que j'ai appelé [self.videoWriter endSessionAtSourceTime:...], que vous voyez comme la chute dramatique dans l'utilisation de la mémoire. Voici mon code d'écriture audio, qui est envoyé comme un bloc sur une file d'attente série:

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

comme vous pouvez le voir, je libère chaque tampon une fois par création. J'ai tracé la "fuite" jusqu'à la ligne où les tampons audio sont ajoutés:

[self.audioWriterInput appendSampleBuffer:sampleBuffer]

je me le suis prouvé en commentant que line, après quoi j'obtiens le graphique D'Allocations "sans fuite" suivant (bien que la vidéo enregistrée n'ait plus de son maintenant, bien sûr):

No leak

j'ai essayé une autre chose, qui est de rajouter de la appendSamplebuffer ligne et au lieu de double-release blockBuffer2:

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

en Faisant cela n' cause un double-free, indiquant que blockBuffer2conserver compter à ce point est de 2. Cela a produit le même" répartition graphique, à l'exception que lorsque j'ai appelé [self.videoWriter endSessionAtSourceTime:...], j'obtiens un crash d'une double libération (indiquant que self.videoWriter essaie de libérer tous ses pointeurs à l' blockBuffer2s qui ont été passés).

Si au lieu de cela, j'ai essayer le code suivant:

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

[self.audioWriterInput appendSampleBuffer:sampleBuffer]et l'appel pour ajouter des images vidéo commencent à échouer pour chaque appel après que.

Donc ma conclusion est que AVAssetWriter ou AVAssetWriterInput est en conservant blockBuffer2 jusqu'à ce que la vidéo a terminé l'enregistrement. Évidemment, cela peut causer de vrais problèmes de mémoire si la vidéo enregistre assez longtemps. Suis-je en train de faire quelque chose de mal?

Edit: les octets audio que je reçois sont en format PCM, alors que le format vidéo que j'écris est MPEG4 et le format audio pour cette vidéo est MPEG4AAC. Est-il possible que le rédacteur de vidéo exécute le format PCM --> AAC à la volée, et c'est pour cela qu'il est tamponné?

18
demandé sur kevlar 2013-02-13 10:07:58

1 réponses

puisque vous attendez depuis un mois une réponse, je vais vous donner une réponse Moins qu'idéale mais réalisable.

vous pouvez utiliser les fonctions ExtendedAudioFile pour écrire un fichier séparé. Ensuite, vous pouvez simplement lire la vidéo et l'audio avec un AVComposition. Je pense que vous pourriez être en mesure d'utiliser AVFoundation pour composer le caf et la vidéo ensemble sans réencodage si vous avez besoin d'eux compostés à la fin de l'enregistrement.

cela vous permettra de sortir et de courir, ensuite, vous pouvez résoudre la fuite de mémoire à votre guise.

1
répondu Scrooch 2013-03-20 17:16:37