Я в настоящее время возиться с iOS и Objective-C ++. Я пришел из C / C ++, поэтому, пожалуйста, извините за плохое кодирование в следующих примерах.
Я пытаюсь транслировать звук с микрофона моего устройства iOS через tcp, устройство iOS действует как сервер и отправляет данные всем подключенным клиентам.
Для этого я сначала использую AVCaptureDevice
а также requestAccessForMediaType:AVMediaTypeAudio
запросить доступ к микрофону (вместе с необходимой записью в Info.plist).
Затем я создаю 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;
}
aReceiver*
класс (?) определен ниже и получает аудио кадры, предоставленные AVCaptureAudioDataOutput*
объект. Кадры хранятся внутри std::vector
,
(Я добавляю код в виде изображения, так как я не смог правильно его отформатировать …)
Тогда я начинаю 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()
вызов:
(опять же это изображение кода, та же причина, что и выше)
Шаги с 5 по 7 повторяются до тех пор, пока соединение TCP не будет закрыто.
Каждый шаг выполняется без какой-либо заметной ошибки (я знаю, что я мог бы включить сюда более эффективную обработку ошибок), и я получаю данные из шагов 3 и 7. Однако, похоже, что в конце не получается AAC.
Поскольку я довольно новичок во всем этом, я действительно не уверен, в чем моя ошибка, я уверен, что есть несколько вещей, которые я сделал неправильно. Довольно сложно найти подходящий пример кода того, что я пытаюсь сделать, и вышеизложенное — лучшее, что я мог придумать до сих пор со всем, что я нашел, в паре с документацией на Apple Dev.
Я надеюсь, что кто-то может занять некоторое время, чтобы объяснить мне, что я сделал неправильно и как я могу заставить это работать. Спасибо за чтение, пока здесь!
Задача ещё не решена.
Других решений пока нет …