Запись в AAC из RemoteIO: данные записываются, но файл не воспроизводится
Я пытался записать с устройства RemoteIO непосредственно в AAC в renderCallback в iOS 5 на iPad 2. Я видел противоречивую информацию о том, что это невозможно и что это возможно (в комментариях здесь). Причина, по которой я хочу это сделать, заключается в том, что запись в PCM требует так много места на диске для записи любой длины - даже если она позже преобразуется в AAC.
я почти готов сдаться. Я соскреб через Google, так что Core Audio книга и Apple Core-Audio список рассылки & форумы и достигли точки, где я не получаю никаких ошибок-и записываю что-то на диск, но полученный файл не воспроизводится. Это относится как к симулятору, так и к устройству.
так... если у кого-то есть опыт в этом, я был бы очень признателен за толчок в правильном направлении. Настройка заключается в том, что RemoteIO воспроизводит вывод из AUSamplers и работает нормально.
здесь что я делаю в коде ниже
указать
AudioStreamBasicDescription
форматы для remoteIO блокkAudioFormatLinearPCM
создайте и укажите формат назначения для
ExtAudioFileRef
Укажите формат клиента, получив его из блока RemoteIOукажите renderCallback для модуля RemoteID
в renderCallback, напишите данные в
kAudioUnitRenderAction_PostRender
фаза
как я уже сказал, Я не получаю никаких ошибок, и результирующие размеры аудиофайлов показывают, что что-то записывается, но файл не воспроизводится. Возможно, у меня испорчены форматы?
в любом случае, это мое сообщение в бутылке и/или флаг "Be Here Dragons" для всех, кто бросает вызов темным водам Core-Audio.
/ / несчастный msg, который я получаю при попытке воспроизвести файл:
// части установка remoteIO
// Enable IO for recording
UInt32 flag = 1;
result = AudioUnitSetProperty(ioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
kInputBus, // == 1
&flag,
sizeof(flag));
if (noErr != result) {[self printErrorMessage: @"Enable IO for recording" withStatus: result]; return;}
// Describe format - - - - - - - - - -
size_t bytesPerSample = sizeof (AudioUnitSampleType);
AudioStreamBasicDescription audioFormat;
memset(&audioFormat, 0, sizeof(audioFormat));
audioFormat.mSampleRate = 44100.00;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 1;
audioFormat.mBitsPerChannel = 16;
audioFormat.mBytesPerPacket = 2;
audioFormat.mBytesPerFrame = 2;
result = AudioUnitSetProperty(ioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
kInputBus, // == 1
&audioFormat,
sizeof(audioFormat));
result = AudioUnitSetProperty(ioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kOutputBus, // == 0
&audioFormat,
sizeof(audioFormat));
// функция, которая устанавливает файл & rendercallback
- (void)startRecordingAAC
{
OSStatus result;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *recordFile = [documentsDirectory stringByAppendingPathComponent: @"audio.m4a"];
CFURLRef destinationURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
(__bridge CFStringRef)recordFile,
kCFURLPOSIXPathStyle,
false);
AudioStreamBasicDescription destinationFormat;
memset(&destinationFormat, 0, sizeof(destinationFormat));
destinationFormat.mChannelsPerFrame = 2;
destinationFormat.mFormatID = kAudioFormatMPEG4AAC;
UInt32 size = sizeof(destinationFormat);
result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destinationFormat);
if(result) printf("AudioFormatGetProperty %ld n", result);
result = ExtAudioFileCreateWithURL(destinationURL,
kAudioFileM4AType,
&destinationFormat,
NULL,
kAudioFileFlags_EraseFile,
&extAudioFileRef);
if(result) printf("ExtAudioFileCreateWithURL %ld n", result);
AudioStreamBasicDescription clientFormat;
memset(&clientFormat, 0, sizeof(clientFormat));
result = AudioUnitGetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, & clientFormat, &size);
if(result) printf("AudioUnitGetProperty %ld n", result);
result = ExtAudioFileSetProperty(extAudioFileRef,kExtAudioFileProperty_ClientDataFormat,sizeof(clientFormat),&clientFormat);
if(result) printf("ExtAudioFileSetProperty %ld n", result);
result = ExtAudioFileWriteAsync(extAudioFileRef, 0, NULL);
if (result) {[self printErrorMessage: @"ExtAudioFileWriteAsync error" withStatus: result];}
result = AudioUnitAddRenderNotify(ioUnit, renderCallback, (__bridge void*)self);
if (result) {[self printErrorMessage: @"AudioUnitAddRenderNotify" withStatus: result];}
}
// и, наконец, rendercallback
static OSStatus renderCallback (void * inRefCon,
AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData)
{
OSStatus result;
if (*ioActionFlags == kAudioUnitRenderAction_PostRender){
MusicPlayerController* THIS = (__bridge MusicPlayerController *)inRefCon;
result = ExtAudioFileWriteAsync(THIS->extAudioFileRef, inNumberFrames, ioData);
if(result) printf("ExtAudioFileWriteAsync %ld n", result);
}
return noErr;
}
3 ответов
Итак, я, наконец, разобрался с этим! Ох, что за охота за информацией.
во всяком случае, вот бит в документах для ExtAudioFile, который я пропустил (см. выделенный жирным шрифтом текст). Я не устанавливал это свойство. Данные были записаны на мой .файл m4a, но он был нечитаемым при воспроизведении. Итак, подводя итог: у меня есть куча AUSamplers -> AUMixer -> RemoteIO. Обратный вызов рендеринга на экземпляре RemoteIO записывает данные на диск в сжатом формате m4a. Таким образом, можно генерировать сжатые аудио на лету (iOS 5/iPad 2)
Кажется довольно надежным - у меня были некоторые операторы printf в rendercallback, и запись работала нормально.
Ура!
ExtAudioFileProperty_CodecManufacturer Производитель кодека, который будет использоваться объектом расширенного аудиофайла. Значение-uint32 для чтения / записи. это свойство необходимо указать перед установкой свойства kExtAudioFileProperty_ClientDataformat (стр. 20), которое, в свою очередь, запускает создание кодека. используйте это свойство в iOS для выбора между аппаратным или программным кодером, указав kAppleHardwareAudioCodecManufacturer или kAppleSoftwareAudioCodecManufacturer. Доступно в Mac OS X v10.7 и позже. Объявлено в ExtendedAudioFile.h.
// specify codec
UInt32 codec = kAppleHardwareAudioCodecManufacturer;
size = sizeof(codec);
result = ExtAudioFileSetProperty(extAudioFileRef,
kExtAudioFileProperty_CodecManufacturer,
size,
&codec);
if(result) printf("ExtAudioFileSetProperty %ld \n", result);
вы написали волшебный cookie, необходимый в начале аудиофайла mpg 4?
Вам также нужно сделать, по крайней мере, первый файл записи вне аудио блока рендеринга обратного вызова.
добавлено:
вы правильно смыли и закрыли аудиофайл в конце? (вне обратного вызова AU)
похоже, что это немного больше, чем просто указание кодека для идентификатора выходного аудиофайла.
по этой теме: ExtAudioFileWrite для M4A / AAC сбой на двухъядерных устройствах (ipad 2, iphone 4s) некоторые модели iPhone (4S), например, не играют хорошо с аппаратным декодером.
из моего опыта работы с 4S, пытаясь кодировать файл AAC с аппаратным кодеком либо a) иногда работает, b) терпит неудачу с ошибкой -66567 (kExtAudioFileError_MaxPacketSizeunknown) или c) записывает несколько образцов, а затем просто зависает без отслеживаемой ошибки.
программный кодек отлично работает на iPhone 4S за счет более низкой производительности.
Edit: некоторые утверждают, что аппаратный кодек не любит частоту дискретизации 44,1 кГц. Я еще не подтвердил это.