AudioConverterFillComplexBuffer
không thực sự có nghĩa là "lấp đầy bộ mã hóa bằng bộ đệm đầu vào của tôi mà tôi có ở đây". Nó có nghĩa là "lấp đầy bộ đệm đầu ra đầu ra này tại đây với dữ liệu được mã hóa từ bộ mã hóa". Với quan điểm này, gọi lại đột nhiên có ý nghĩa - nó được sử dụng để lấy dữ liệu nguồn để đáp ứng yêu cầu "điền vào bộ đệm đầu ra này cho tôi". Có lẽ điều này là hiển nhiên đối với những người khác, nhưng tôi mất thời gian dài để hiểu điều này (và từ tất cả mã mẫu AudioConverter tôi thấy nổi xung quanh nơi mọi người gửi dữ liệu đầu vào qua inInputDataProcUserData
, tôi đoán mình không phải là người duy nhất).
Cuộc gọi AudioConverterFillComplexBuffer
đang chặn và mong bạn phân phối dữ liệu đến đồng bộ từ cuộc gọi lại. Nếu bạn mã hóa theo thời gian thực, bạn sẽ cần gọi số FillComplexBuffer
trên một chuỗi riêng biệt mà bạn tự thiết lập. Trong cuộc gọi lại, sau đó bạn có thể kiểm tra dữ liệu đầu vào có sẵn và nếu nó không có sẵn, bạn cần phải chặn trên một semaphore. Sử dụng một NSCondition, thread encoder sau đó sẽ giống như thế này:
- (void)startEncoder
{
OSStatus creationStatus = AudioConverterNew(&_fromFormat, &_toFormat, &_converter);
_running = YES;
_condition = [[NSCondition alloc] init];
[self performSelectorInBackground:@selector(_encoderThread) withObject:nil];
}
- (void)_encoderThread
{
while(_running) {
// Make quarter-second buffers.
size_t bufferSize = (_outputBitrate/8) * 0.25;
NSMutableData *outAudioBuffer = [NSMutableData dataWithLength:bufferSize];
AudioBufferList outAudioBufferList;
outAudioBufferList.mNumberBuffers = 1;
outAudioBufferList.mBuffers[0].mNumberChannels = _toFormat.mChannelsPerFrame;
outAudioBufferList.mBuffers[0].mDataByteSize = (UInt32)bufferSize;
outAudioBufferList.mBuffers[0].mData = [outAudioBuffer mutableBytes];
UInt32 ioOutputDataPacketSize = 1;
_currentPresentationTime = kCMTimeInvalid; // you need to fill this in during FillComplexBuffer
const OSStatus conversionResult = AudioConverterFillComplexBuffer(_converter, FillBufferTrampoline, (__bridge void*)self, &ioOutputDataPacketSize, &outAudioBufferList, NULL);
// here I convert the AudioBufferList into a CMSampleBuffer, which I've omitted for brevity.
// Ping me if you need it.
[self.delegate encoder:self encodedSampleBuffer:outSampleBuffer];
}
}
Và gọi lại có thể trông như thế này: (lưu ý rằng tôi thường sử dụng tấm bạt lò xo này ngay lập tức về phía trước đến một phương pháp trên dụ của tôi (bằng cách chuyển tiếp của tôi Ví dụ trong inUserData
; bước này được bỏ qua cho ngắn gọn)):
static OSStatus FillBufferTrampoline(AudioConverterRef inAudioConverter,
UInt32* ioNumberDataPackets,
AudioBufferList* ioData,
AudioStreamPacketDescription** outDataPacketDescription,
void* inUserData)
{
[_condition lock];
UInt32 countOfPacketsWritten = 0;
while (true) {
// If the condition fires and we have shut down the encoder, just pretend like we have written 0 bytes and are done.
if(!_running) break;
// Out of input data? Wait on the condition.
if(_inputBuffer.length == 0) {
[_condition wait];
continue;
}
// We have data! Fill ioData from your _inputBuffer here.
// Also save the input buffer's start presentationTime here.
// Exit out of the loop, since we're done waiting for data
break;
}
[_condition unlock];
// 2. Set ioNumberDataPackets to the amount of data remaining
// if running is false, this will be 0, indicating EndOfStream
*ioNumberDataPackets = countOfPacketsWritten;
return noErr;
}
và cho đầy đủ, đây là cách bạn sau đó sẽ ăn encoder này với dữ liệu, và làm thế nào để tắt nó đúng cách:
- (void)appendSampleBuffer:(CMSampleBufferRef)sampleBuffer
{
[_condition lock];
// Convert sampleBuffer and put it into _inputBuffer here
[_condition broadcast];
[_condition unlock];
}
- (void)stopEncoding
{
[_condition lock];
_running = NO;
[_condition broadcast];
[_condition unlock];
}
Tôi có một số rắc rối với việc điền iodate với _inputBuffer và đặt ioNumberDataPackets, bạn có thể vui lòng điền mã không? Một số câu hỏi: Chúng ta có cần đặt ioData.mNumberBuffers thành 1 không? Chúng ta có cần điền tất cả dữ liệu từ _inputBuffer vào ioData.mBuffers [0] không? Làm thế nào chúng ta có thể tính toán ioNumberDataPackets? hoặc chỉ cần đặt nó là 1? Ý bạn là "đặt ioNumberDataPackets thành lượng dữ liệu còn lại?" trong khi tài liệu nói "khi thoát, số gói dữ liệu âm thanh thực sự được cung cấp cho đầu vào"? – lancy