В настоящее время я в первый раз связываюсь с 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
.
(я добавляю код как изображение, так как я не могу правильно его отформатировать ...)
![enter image description here](https://i.stack.imgur.com/QgZ4l.png)
Затем я запускаю 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](https://i.stack.imgur.com/uC1SD.png)
Шаги с 5 по 7 повторяются до тех пор, пока соединение tcp не будет закрыто.
Каждый шаг выполняется без какой-либо заметной ошибки (я знаю, что я мог бы включить сюда лучшую обработку ошибок), и я делаю этополучить данные из шагов 3 и 7. Однако, похоже, что в конце не получится AAC.
Поскольку я довольно новичок во всем этом, я действительно не уверен, в чем заключается моя ошибка, я уверен, чтоЕсть несколько вещей, которые я сделал неправильно.Трудно найти подходящий пример кода того, что я пытаюсь сделать, и вышеизложенное - лучшее, что я мог придумать до сих пор со всем, что я нашел, в паре с документацией Apple Dev.
Я надеюсь, что кто-то может занять некоторое время, чтобы объяснить мне, что я сделал не так и как я могу заставить это работать.Спасибо за чтение, пока здесь!