Puis-je utiliser AVCaptureSession pour encoder un stream AAC en memory?

J'écris une application iOS qui diffuse la video et l'audio sur le réseau.

J'utilise AVCaptureSession pour saisir des images video brutes en utilisant AVCaptureVideoDataOutput et les encoder dans un logiciel utilisant x264 . Cela fonctionne très bien.

Je voulais faire la même chose pour l'audio, seulement que je n'ai pas besoin de beaucoup de contrôle du côté audio, donc je voulais utiliser le codeur matériel embedded pour produire un stream AAC. Cela signifiait utiliser Audio Converter depuis la couche Audio Toolbox. Pour ce faire, j'ai mis un gestionnaire pour les trames audio de AVCaptudeAudioDataOutput :

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { // get the audio samples into a common buffer _pcmBuffer CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer); CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, &_pcmBufferSize, &_pcmBuffer); // use AudioConverter to UInt32 ouputPacketsCount = 1; AudioBufferList bufferList; bufferList.mNumberBuffers = 1; bufferList.mBuffers[0].mNumberChannels = 1; bufferList.mBuffers[0].mDataByteSize = sizeof(_aacBuffer); bufferList.mBuffers[0].mData = _aacBuffer; OSStatus st = AudioConverterFillComplexBuffer(_converter, converter_callback, (__bridge void *) self, &ouputPacketsCount, &bufferList, NULL); if (0 == st) { // ... send bufferList.mBuffers[0].mDataByteSize bytes from _aacBuffer... } } 

Dans ce cas, la fonction de callback pour le convertisseur audio est assez simple (en supposant que les tailles de packages et les counts sont configurés correctement):

 - (void) putPcmSamplesInBufferList:(AudioBufferList *)bufferList withCount:(UInt32 *)count { bufferList->mBuffers[0].mData = _pcmBuffer; bufferList->mBuffers[0].mDataByteSize = _pcmBufferSize; } 

Et la configuration du convertisseur audio ressemble à ceci:

 { // ... AudioStreamBasicDescription pcmASBD = {0}; pcmASBD.mSampleRate = ((AVAudioSession *) [AVAudioSession sharedInstance]).currentHardwareSampleRate; pcmASBD.mFormatID = kAudioFormatLinearPCM; pcmASBD.mFormatFlags = kAudioFormatFlagsCanonical; pcmASBD.mChannelsPerFrame = 1; pcmASBD.mBytesPerFrame = sizeof(AudioSampleType); pcmASBD.mFramesPerPacket = 1; pcmASBD.mBytesPerPacket = pcmASBD.mBytesPerFrame * pcmASBD.mFramesPerPacket; pcmASBD.mBitsPerChannel = 8 * pcmASBD.mBytesPerFrame; AudioStreamBasicDescription aacASBD = {0}; aacASBD.mFormatID = kAudioFormatMPEG4AAC; aacASBD.mSampleRate = pcmASBD.mSampleRate; aacASBD.mChannelsPerFrame = pcmASBD.mChannelsPerFrame; size = sizeof(aacASBD); AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &aacASBD); AudioConverterNew(&pcmASBD, &aacASBD, &_converter); // ... } 

Cela semble assez simple seulement le IT NE FONCTIONNE PAS . Une fois AVCaptureSession en cours d'exécution, le convertisseur audio (en particulier AudioConverterFillComplexBuffer) renvoie une erreur «hwiu» (matériel en cours d'utilisation). La conversion fonctionne bien si la session est arrêtée mais je ne peux rien capturer …

Je me demandais s'il y avait un moyen d'get un stream AAC sur AVCaptureSession. Les options que je considère sont:

  1. D'une manière ou d'une autre, en utilisant AVAssetWriterInput pour encoder des échantillons audio dans AAC, puis get les packages codés en quelque sorte (pas via AVAssetWriter, qui écrirait seulement dans un file).

  2. Réorganisation de mon application pour qu'elle utilise AVCaptureSession uniquement du côté video et utilise des files d' attente audio du côté audio. Cela rendra le contrôle du stream (démarrage et arrêt de l'logging, réponse aux interruptions) plus compliqué et je crains que cela ne provoque des problèmes de synchronisation entre l'audio et la video. En outre, cela ne semble pas être un bon design.

Est-ce que quelqu'un sait si l'obtention de l'AAC sur AVCaptureSession est possible? Dois-je utiliser des files d'attente audio ici? Cela pourrait-il m'amener à des problèmes de synchronisation ou de contrôle?

J'ai fini par requestr conseil à Apple (il s'avère que vous pouvez le faire si vous avez un count de développeur payant).

Il semble que AVCaptureSession saisisse une partie de l'encodeur matériel AAC mais vous permet seulement de l'utiliser pour écrire directement dans un file.

Vous pouvez utiliser l'encodeur logiciel mais vous devez le requestr spécifiquement au lieu d'utiliser AudioConverterNew:

 AudioClassDescription *description = [self getAudioClassDescriptionWithType:kAudioFormatMPEG4AAC fromManufacturer:kAppleSoftwareAudioCodecManufacturer]; if (!description) { return false; } // see the question as for setting up pcmASBD and arc ASBD OSStatus st = AudioConverterNewSpecific(&pcmASBD, &aacASBD, 1, description, &_converter); if (st) { NSLog(@"error creating audio converter: %s", OSSTATUS(st)); return false; } 

avec

 - (AudioClassDescription *)getAudioClassDescriptionWithType:(UInt32)type fromManufacturer:(UInt32)manufacturer { static AudioClassDescription desc; UInt32 encoderSpecifier = type; OSStatus st; UInt32 size; st = AudioFormatGetPropertyInfo(kAudioFormatProperty_Encoders, sizeof(encoderSpecifier), &encoderSpecifier, &size); if (st) { NSLog(@"error getting audio format propery info: %s", OSSTATUS(st)); return nil; } unsigned int count = size / sizeof(AudioClassDescription); AudioClassDescription descriptions[count]; st = AudioFormatGetProperty(kAudioFormatProperty_Encoders, sizeof(encoderSpecifier), &encoderSpecifier, &size, descriptions); if (st) { NSLog(@"error getting audio format propery: %s", OSSTATUS(st)); return nil; } for (unsigned int i = 0; i < count; i++) { if ((type == descriptions[i].mSubType) && (manufacturer == descriptions[i].mManufacturer)) { memcpy(&desc, &(descriptions[i]), sizeof(desc)); return &desc; } } return nil; } 

L'encodeur logiciel va utiliser les ressources du CPU, bien sûr, mais fera le travail.