iOS AVCaptureSession потоковый микро-аудио Objective-C ++ - PullRequest
0 голосов
/ 20 декабря 2018

В настоящее время я в первый раз связываюсь с iOS и Objective-C ++.Я пришел из C / C ++, поэтому, пожалуйста, извините за плохое кодирование в следующих примерах.

Я пытаюсь транслировать в реальном времени звук с микрофона моего устройства iOS через tcp, устройство iOS работаетв качестве сервера и отправляет данные всем клиентам, которые подключаются.

  1. Для этого я сначала использую AVCaptureDevice и requestAccessForMediaType:AVMediaTypeAudio для запроса доступа к микрофону (пос необходимой записью в Info.plist).

  2. Затем я создаю AVCaptureSession*, используя следующую функцию:

    AVCaptureSession* createBasicARecordingSession(aReceiver* ObjectReceivingAudioFrames){
    
      AVCaptureSession* s = [[AVCaptureSession alloc] init];
    
      AVCaptureDevice* aDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
      AVCaptureDeviceInput* aInput = NULL;
      if([aDevice lockForConfiguration:NULL] == YES && aDevice){
          aInput = [AVCaptureDeviceInput deviceInputWithDevice:aDevice error:nil];
          [aDevice unlockForConfiguration];
      }
      else if(!aDevice){
          fprintf(stderr, "[d] could not create device. (%p)\n", aDevice);
          return NULL;
      }
      else{
          fprintf(stderr, "[d] could not lock device.\n");
          return NULL;
      }
    
      if(!aInput){
          fprintf(stderr, "[d] could not create input.\n");
          return NULL;
      }
    
      AVCaptureAudioDataOutput* aOutput = [[AVCaptureAudioDataOutput alloc] init];
      dispatch_queue_t aQueue = dispatch_queue_create("aQueue", NULL);
    
      if(!aOutput){
          fprintf(stderr, "[d] could not create output.\n");
          return NULL;
      }
    
      [aOutput setSampleBufferDelegate:ObjectReceivingAudioFrames queue:aQueue];
      // the below line does only work on macOS
      //aOutput.audioSettings = settings;
    
      [s beginConfiguration];
      if([s canAddInput:aInput]){
          [s addInput:aInput];
      }
      else{
          fprintf(stderr, "[d] could not add input.\n");
          return NULL;
      }
      if([s canAddOutput:aOutput]){
          [s addOutput:aOutput];
      }
      else{
          fprintf(stderr, "[d] could not add output.\n");
          return NULL;
      }
      [s commitConfiguration];
    
      return s;
    
    }
    
  3. Класс aReceiver* (?) Определен ниже и принимает аудиокадры, предоставленные объектом AVCaptureAudioDataOutput*.Кадры хранятся внутри std::vector.

(я добавляю код как изображение, так как я не могу правильно его отформатировать ...) enter image description here enter image description here

Затем я запускаю AVCaptureSession*, используя [audioSession start]

Когда клиент TCP подключается, я сначала создаю AudioConverterRef и два AudioStreamBasicDescription для преобразования аудиокадры в AAC, см. ниже:

AudioStreamBasicDescription asbdIn, asbdOut;
AudioConverterRef converter;

asbdIn.mFormatID = kAudioFormatLinearPCM;
//asbdIn.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
asbdIn.mFormatFlags = 12;
asbdIn.mSampleRate = 44100;
asbdIn.mChannelsPerFrame = 1;
asbdIn.mFramesPerPacket = 1;
asbdIn.mBitsPerChannel = 16;
//asbdIn.mBytesPerFrame = (asbdIn.mBitsPerChannel / 8) * asbdIn.mBitsPerChannel;
asbdIn.mBytesPerFrame = 2;
asbdIn.mBytesPerPacket = asbdIn.mBytesPerFrame;
asbdIn.mReserved = 0;

asbdOut.mFormatID = kAudioFormatMPEG4AAC;
asbdOut.mFormatFlags = 0;
asbdOut.mSampleRate = 44100;
asbdOut.mChannelsPerFrame = 1;
asbdOut.mFramesPerPacket = 1024;
asbdOut.mBitsPerChannel = 0;
//asbdOut.mBytesPerFrame = (asbdOut.mBitsPerChannel / 8) * asbdOut.mBitsPerChannel;
asbdOut.mBytesPerFrame = 0;
asbdOut.mBytesPerPacket = asbdOut.mBytesPerFrame;
asbdOut.mReserved = 0;

OSStatus err = AudioConverterNew(&asbdIn, &asbdOut, &converter);

Затем я создаю AudioBufferList* для хранения закодированных кадров:

while(audioInput.locked){ // audioInput is my aReceiver*
    usleep(0.2 * 1000000);
}
audioInput.locked = true;

UInt32 RequestedPackets = 8192;
//AudioBufferList* aBufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList));
AudioBufferList* aBufferList = static_cast<AudioBufferList*>(calloc(1, offsetof(AudioBufferList, mBuffers) + (sizeof(AudioBuffer) * 1)));
aBufferList->mNumberBuffers = 1;

aBufferList->mBuffers[0].mNumberChannels = asbdIn.mChannelsPerFrame;
aBufferList->mBuffers[0].mData = static_cast<void*>(calloc(RequestedPackets, asbdIn.mBytesPerFrame));
aBufferList->mBuffers[0].mDataByteSize = asbdIn.mBytesPerFrame * RequestedPackets;

Затем я просматриваю кадры, хранящиеся в std::vector, упомянутом ранее, и передаю их AudioConverterFillComplexBuffer().После преобразования я объединяю все закодированные кадры в один NSMutableData, который затем write(), в сокет, подключенный к клиенту.

long aBufferListSize = audioInput.aBufferList.size();

while(aBufferListSize > 0){

   err = AudioConverterFillComplexBuffer(converter, feedAFrames, static_cast<void*>(&audioInput.aBufferList[audioInput.aBufferList.size() - aBufferListSize]), &RequestedPackets, aBufferList, NULL);

   NSMutableData* encodedData = [[NSMutableData alloc] init];
   long encodedDataLen = 0;

   for(int i = 0; i < aBufferList->mNumberBuffers; i++){

       Float32* frame = (Float32*)aBufferList->mBuffers[i].mData;
       [encodedData appendBytes:frame length:aBufferList->mBuffers[i].mDataByteSize];
       encodedDataLen += aBufferList->mBuffers[i].mDataByteSize;

   }

   unsigned char* encodedDataBytes = (unsigned char*)[encodedData bytes];

   fprintf(stderr, "[d] got %li encoded bytes to send...\n", encodedDataLen);
   long bytes = write(Client->GetFD(), encodedDataBytes, encodedDataLen);
   fprintf(stderr, "[d] written %li of %li bytes.\n", bytes, encodedDataLen);

   usleep(0.2 * 1000000);

   aBufferListSize--;

}

audioInput.aBufferList.clear();

audioInput.locked = false;

Ниже используется обратный вызов feedAFrames()в AudioConverterFillComplexBuffer() вызове:

(опять же, это изображение кода, та же причина, что и выше) enter image description here

Шаги с 5 по 7 повторяются до тех пор, пока соединение tcp не будет закрыто.


Каждый шаг выполняется без какой-либо заметной ошибки (я знаю, что я мог бы включить сюда лучшую обработку ошибок), и я делаю этополучить данные из шагов 3 и 7. Однако, похоже, что в конце не получится AAC.

Поскольку я довольно новичок во всем этом, я действительно не уверен, в чем заключается моя ошибка, я уверен, чтоЕсть несколько вещей, которые я сделал неправильно.Трудно найти подходящий пример кода того, что я пытаюсь сделать, и вышеизложенное - лучшее, что я мог придумать до сих пор со всем, что я нашел, в паре с документацией Apple Dev.

Я надеюсь, что кто-то может занять некоторое время, чтобы объяснить мне, что я сделал не так и как я могу заставить это работать.Спасибо за чтение, пока здесь!

...