AudioConverterFillComplexBuffer doesn't really mean "populate the encoder with my input buffer , which I have." This means "fill this output buffer here with encoded data from the encoder." From this point of view, the callback suddenly makes sense - it is used to extract the source data to satisfy the request "fill this output buffer for me." This may be obvious to others, but it took me a long time to figure it out (and from the entire AudioConverter example code that I see, it floats around where people send input through inInputDataProcUserData , I assume I'm not the only one).
The AudioConverterFillComplexBuffer call is blocked and expects you to pass data to it synchronously from the callback. If you are coding in real time, you will need to call FillComplexBuffer in a separate thread that you configured yourself. In the callback, you can check the available input, and if it is not available, you need to lock the semaphore. Using NSCondition, the encoder stream will look something like this:
- (void)startEncoder { OSStatus creationStatus = AudioConverterNew(&_fromFormat, &_toFormat, &_converter); _running = YES; _condition = [[NSCondition alloc] init]; [self performSelectorInBackground:@selector(_encoderThread) withObject:nil]; } - (void)_encoderThread { while(_running) {
And the callback might look like this: (note that I usually use this trampoline to immediately jump to the method of my instance (by redirecting my instance to inUserData , this step is omitted for brevity)):
static OSStatus FillBufferTrampoline(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData) { [_condition lock]; UInt32 countOfPacketsWritten = 0; while (true) {
And for completeness, here is how you could serve this data encoder and how to disable it correctly:
- (void)appendSampleBuffer:(CMSampleBufferRef)sampleBuffer { [_condition lock];
nevyn
source share