Воспроизведение голоса из серверного потока nsdata с помощью AudioUnit IOS

Я пытаюсь создать какое-то приложение VoIP в iOS. До сих пор мне удавалось успешно отправлять данные микрофона в качестве буфера с микрофона на сервер, используя GCDAsyncSocket. Теперь мне нужно воспроизвести полученные данные, что меня очень смущает. Я посмотрел в Интернете, но все, что я вижу, это либо воспроизведение аудиофайла с удаленного компьютера, либо аудиопоток с URL-адреса. На самом деле я регулярно получаю NSData, и мне нужно выяснить, как использовать эти NSData для заполнения списка буферов аудиоустройств. Я новичок в C и мне трудно с этим справиться. Здесь я получаю NSData с сервера.

- (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag
{
    if (tag == 1 ){
       //this is where I read password and stuff to authenticate

    }
    else{

        [self setUpAQOutput:data];//this should somehow initialize AU and fill the buffer
}

и в моем AudioUnitProcessor вот как я настроил AUnit, используя коды Стефана Поппа:

  //
//  AudioProcessor.m
//  MicInput
//
//  Created by Stefan Popp on 21.09.11.

//

#import "AudioProcessor.h"
#import "PTTClient.h"
#pragma mark Recording callback

static OSStatus recordingCallback(void *inRefCon,
                                  AudioUnitRenderActionFlags *ioActionFlags,
                                  const AudioTimeStamp *inTimeStamp,
                                  UInt32 inBusNumber,
                                  UInt32 inNumberFrames,
                                  AudioBufferList *ioData) {

    // the data gets rendered here
    AudioBuffer buffer;

    // a variable where we check the status
    OSStatus status;

    /**
     This is the reference to the object who owns the callback.
     */
    AudioProcessor *audioProcessor = (AudioProcessor*) inRefCon;

    /**
     on this point we define the number of channels, which is mono
     for the iphone. the number of frames is usally 512 or 1024.
     */
    buffer.mDataByteSize = inNumberFrames * 2; // sample size
    buffer.mNumberChannels = 1; // one channel
    buffer.mData = malloc( inNumberFrames * 2 ); // buffer size

    // we put our buffer into a bufferlist array for rendering
    AudioBufferList bufferList;
    bufferList.mNumberBuffers = 1;
    bufferList.mBuffers[0] = buffer;

    // render input and check for error
    status = AudioUnitRender([audioProcessor audioUnit], ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList);


    // process the bufferlist in the audio processor
    [audioProcessor processBuffer:&bufferList];

    // clean up the buffer
    free(bufferList.mBuffers[0].mData);

    return noErr;
}

#pragma mark Playback callback

static OSStatus playbackCallback(void *inRefCon,
                                 AudioUnitRenderActionFlags *ioActionFlags,
                                 const AudioTimeStamp *inTimeStamp,
                                 UInt32 inBusNumber,
                                 UInt32 inNumberFrames,
                                 AudioBufferList *ioData) {

//does nothing
    return noErr;
}


#pragma mark objective-c class

@implementation AudioProcessor
@synthesize audioUnit, inAudioBuffer;

-(AudioProcessor*)init
{
    self = [super init];
    if (self) {
        [self initializeAudio];
    }
    return self;
}

+ (OSStatus) playBytes:(NSArray*) byteArray {

    /**
     This is the reference to the object who owns the callback.
     */
  //  NSArray * byteArray = nil;
    AudioProcessor *audioProcessor = [[AudioProcessor alloc] init];

    // iterate over incoming stream an copy to output stream
    for (int i=0; i < [byteArray count]; i++) {
    //  AudioBuffer buffer = ioData->mBuffers[i];

        // find minimum size

        UInt32 size =  [audioProcessor inAudioBuffer].mDataByteSize;

        // copy buffer to audio buffer which gets played after function return
        memcpy(byteArray[i], [audioProcessor inAudioBuffer].mData, size);

        // set data size
        //buffer.mDataByteSize = size;
    }
    return noErr;
}

-(void)initializeAudio
{
    OSStatus status;

    // We define the audio component
    AudioComponentDescription desc;
    desc.componentType = kAudioUnitType_Output; // we want to ouput
    desc.componentSubType = kAudioUnitSubType_RemoteIO; // we want in and ouput
    desc.componentFlags = 0; // must be zero
    desc.componentFlagsMask = 0; // must be zero
    desc.componentManufacturer = kAudioUnitManufacturer_Apple; // select provider

    // find the AU component by description
    AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);

    // create audio unit by component
    status = AudioComponentInstanceNew(inputComponent, &audioUnit);

        // define that we want record io on the input bus
    UInt32 flag = 1;
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioOutputUnitProperty_EnableIO, // use io
                                  kAudioUnitScope_Input, // scope to input
                                  kInputBus, // select input bus (1)
                                  &flag, // set flag
                                  sizeof(flag));
        // define that we want play on io on the output bus
    UInt32 stopFlag = 0;//stop flag 0 because we dont want to play audio back in device
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioOutputUnitProperty_EnableIO, // use io
                                  kAudioUnitScope_Output, // scope to output
                                  kOutputBus, // select output bus (0)
                                  &stopFlag, // set flag
                                  sizeof(stopFlag));

    /*
     We need to specify our format on which we want to work.
     We use Linear PCM cause its uncompressed and we work on raw data.
     for more informations check.

     We want 16 bits, 2 bytes per packet/frames at 44khz
     */
    AudioStreamBasicDescription audioFormat;
    audioFormat.mSampleRate         = SAMPLE_RATE;
    audioFormat.mFormatID           = kAudioFormatLinearPCM;
    audioFormat.mFormatFlags        = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
    audioFormat.mFramesPerPacket    = 1;
    audioFormat.mChannelsPerFrame   = 1;
    audioFormat.mBitsPerChannel     = 16;
    audioFormat.mBytesPerPacket     = audioFormat.mChannelsPerFrame * sizeof( SInt16);
    audioFormat.mBytesPerFrame      = audioFormat.mChannelsPerFrame * sizeof( SInt16);



    // set the format on the output stream
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioUnitProperty_StreamFormat,
                                  kAudioUnitScope_Output,
                                  kInputBus,
                                  &audioFormat,
                                  sizeof(audioFormat));


    // set the format on the input stream
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioUnitProperty_StreamFormat,
                                  kAudioUnitScope_Input,
                                  kOutputBus,
                                  &audioFormat,
                                  sizeof(audioFormat));


    /**
     We need to define a callback structure which holds
     a pointer to the recordingCallback and a reference to
     the audio processor object
     */
    AURenderCallbackStruct callbackStruct;

    // set recording callback
    callbackStruct.inputProc = recordingCallback; // recordingCallback pointer
    callbackStruct.inputProcRefCon = self;

    // set input callback to recording callback on the input bus
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioOutputUnitProperty_SetInputCallback,
                                  kAudioUnitScope_Global,
                                  kInputBus,
                                  &callbackStruct,
                                  sizeof(callbackStruct));

     /*
     We do the same on the output stream to hear what is coming
     from the input stream
     */
    callbackStruct.inputProc = playbackCallback;
    callbackStruct.inputProcRefCon = self;

    // set playbackCallback as callback on our renderer for the output bus
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioUnitProperty_SetRenderCallback,
                                  kAudioUnitScope_Global,
                                  kOutputBus,
                                  &callbackStruct,
                                  sizeof(callbackStruct));

    // reset flag to 0
    flag = 0;

    /*
     we need to tell the audio unit to allocate the render buffer,
     that we can directly write into it.
     */
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioUnitProperty_ShouldAllocateBuffer,
                                  kAudioUnitScope_Output,
                                  kInputBus,
                                  &flag,
                                  sizeof(flag));


    /*
     we set the number of channels to mono and allocate our block size to
     1024 bytes.
     */
    inAudioBuffer.mNumberChannels = 1;
    inAudioBuffer.mDataByteSize = 512 * 2;
    inAudioBuffer.mData = malloc( 512 * 2 );

    // Initialize the Audio Unit and cross fingers =)
    status = AudioUnitInitialize(audioUnit);

    NSLog(@"Started");

}

#pragma mark controll stream

-(void)start;
{
    // start the audio unit. You should hear something, hopefully :)
    OSStatus status = AudioOutputUnitStart(audioUnit);
   }
-(void)stop;
{
    // stop the audio unit
    OSStatus status = AudioOutputUnitStop(audioUnit);

}

#pragma mark processing

-(void)processBuffer: (AudioBufferList*) audioBufferList
{
    AudioBuffer sourceBuffer = audioBufferList->mBuffers[0];

    // we check here if the input data byte size has changed
    if (inAudioBuffer.mDataByteSize != sourceBuffer.mDataByteSize) {
        // clear old buffer
        free(inAudioBuffer.mData);
        // assing new byte size and allocate them on mData
        inAudioBuffer.mDataByteSize = sourceBuffer.mDataByteSize;
        inAudioBuffer.mData = malloc(sourceBuffer.mDataByteSize);
    }
    int currentBuffer =0;
    int maxBuf = 800;

    NSMutableData *data=[[NSMutableData alloc] init];
    // CMBlockBufferRef blockBuffer;
    // CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(ref, NULL, &audioBufferList, sizeof(audioBufferList), NULL, NULL, 0, &blockBuffer);
    // NSLog(@"%@",blockBuffer);


    // audioBufferList->mBuffers[0].mData, audioBufferList->mBuffers[0].mDataByteSize

    for( int y=0; y<audioBufferList->mNumberBuffers; y++ )
    {
        if (currentBuffer < maxBuf){
            AudioBuffer audioBuff = audioBufferList->mBuffers[y];
            Float32 *frame = (Float32*)audioBuff.mData;


            [data appendBytes:frame length:inAudioBuffer.mDataByteSize];
            currentBuffer += audioBuff.mDataByteSize;
        }
        else{
            break;
        }

    }

    [[PTTClient getDefaultInstance] setAudioBufferData: data];//This is call to send buffer data to the server

    // copy incoming audio data to the audio buffer (no need since we are not using playback)
    //memcpy(inAudioBuffer.mData, audioBufferList->mBuffers[0].mData, audioBufferList->mBuffers[0].mDataByteSize);
}




@end

И, наконец, это метод отправки аудиоданных на сервер.

-(void) setAudioBufferData: (NSData*) data{
   [gcdSocket writeData:data withTimeout:timeout tag:tag];
}

Все это работает нормально, и я могу слушать звук на своем сервере, работающем на Java. Теперь мне нужно выяснить, как настроить этот аудиоблок для воспроизведения NSData пакетов, которые я постоянно получаю от сервера (я просмотрел несколько примеров, которые воспроизводят удаленный файл, что мне не нужно. Мне нужно воспроизводить голос) . Источником является не файл, а кто-то говорит, так что я немного запутался.


person dynebuddha    schedule 11.08.2014    source источник
comment
Это не C. Objective-C сильно отличается от самого C.   -  person Neeku    schedule 11.08.2014
comment
Хорошо, можете ли вы предложить лучший способ добиться воспроизведения голоса.   -  person dynebuddha    schedule 11.08.2014
comment
Вы реализовали это, если да, поделитесь решением, у меня такая же проблема   -  person DURGESH    schedule 06.06.2017


Ответы (1)


1) Ну, предположительно, аудиоданные могут стать большими, поэтому я бы буферизовал их в файл. Не уверен, что это более элегантный способ, но эй... методы грубой силы в вашем коде, работающем на уровне логики C++, меня немного сдерживают...

Использует

[NSData writeToFile:atomically:];

...полезно вообще? Возможно, затем использовать этот файл в качестве источника звука, переданного в какой-то более удобный фреймворк Core?

2) Единственное, что приходит на ум, - это какая-то форма локальных сокетов. т. е. открыть соединение с самим собой и предоставить его как «удаленный источник»

Извините, я хотел бы знать больше, чтобы быть более полезным.

person jplego    schedule 12.08.2014
comment
поэтому каждый раз, когда я получаю NSData, должен ли я добавлять его в файл? Не могли бы вы предложить мне более удобную базовую аудиосистему с примером кода... я очень новичок во всем этом. Я уверен, что должен быть какой-то более элегантный способ, чем то, что я делал. - person dynebuddha; 12.08.2014
comment
ForPretty-Twilio, но с поминутной оплатой. Из личного опытаInCoreAudioPlayingSound as background/VoIP levelWillBeHairy.Какова ваша предполагаемая аудитория?Может ли вашJavaServerкодировать HLS? Это трудная проблема ... текущие SDK тоже сложны ... Поэтому ЧТО-ТО должно восполнить пробел - person jplego; 12.08.2014
comment
@jplego Я хочу воспроизвести звук, полученный из данных сокета, поэтому не могли бы вы предоставить мне какой-либо пример кода, который вы используете, потому что я провел много исследований, но не нашел хорошего решения. - person Parth Mehta; 30.07.2015