2011-02-10 3 views
9

iOS에서 WAVE 파일을 AAC로 인코딩 된 M4A 파일로 변환해야합니다. 저는 AAC 인코딩이 오래된 장치 나 시뮬레이터에서 지원되지 않는다는 것을 알고 있습니다. 코드를 실행하기 전에 테스트하고 있습니다. 그러나 나는 아직도 그것을 작동시킬 수 없다.iOS의 ExtAudioFile과 함께 AAC 인코딩을 작동 시키려면 어떻게해야합니까?

나는 애플 자신의 iPhoneExtAudioFileConvertTest 예를 들여다 보았다. 나는 그것을 정확하게 따라 갔다고 생각했지만 여전히 운이 없다.

현재 대상 파일에서 클라이언트 형식을 설정하는 중 -50 (사용자 매개 변수 목록에 오류가 있음)이 표시됩니다. 소스 파일에서 작동합니다.

아래 코드는 제 코드입니다. 어떤 도움이라도 대단히 감사합니다.

UInt32 size; 

// Open a source audio file. 
ExtAudioFileRef sourceAudioFile; 
ExtAudioFileOpenURL((CFURLRef)sourceURL, &sourceAudioFile); 

// Get the source data format 
AudioStreamBasicDescription sourceFormat; 
size = sizeof(sourceFormat); 
result = ExtAudioFileGetProperty(sourceAudioFile, kExtAudioFileProperty_FileDataFormat, &size, &sourceFormat); 

// Define the output format (AAC). 
AudioStreamBasicDescription outputFormat; 
outputFormat.mFormatID = kAudioFormatMPEG4AAC; 
outputFormat.mSampleRate = 44100; 
outputFormat.mChannelsPerFrame = 2; 

// Use AudioFormat API to fill out the rest of the description. 
size = sizeof(outputFormat); 
AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &outputFormat); 

// Make a destination audio file with this output format. 
ExtAudioFileRef destAudioFile; 
ExtAudioFileCreateWithURL((CFURLRef)destURL, kAudioFileM4AType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &destAudioFile); 

// Create canonical PCM client format. 
AudioStreamBasicDescription clientFormat; 
clientFormat.mSampleRate = sourceFormat.mSampleRate; 
clientFormat.mFormatID = kAudioFormatLinearPCM; 
clientFormat.mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger; 
clientFormat.mChannelsPerFrame = 2; 
clientFormat.mBitsPerChannel = 16; 
clientFormat.mBytesPerFrame = 4; 
clientFormat.mBytesPerPacket = 4; 
clientFormat.mFramesPerPacket = 1; 

// Set the client format in source and destination file. 
size = sizeof(clientFormat); 
ExtAudioFileSetProperty(sourceAudioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat); 
size = sizeof(clientFormat); 
ExtAudioFileSetProperty(destAudioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat); 

// Make a buffer 
int bufferSizeInFrames = 8000; 
int bufferSize = (bufferSizeInFrames * sourceFormat.mBytesPerFrame); 
UInt8 * buffer = (UInt8 *)malloc(bufferSize); 
AudioBufferList bufferList; 
bufferList.mNumberBuffers = 1; 
bufferList.mBuffers[0].mNumberChannels = clientFormat.mChannelsPerFrame; 
bufferList.mBuffers[0].mData = buffer; 
bufferList.mBuffers[0].mDataByteSize = (bufferSize); 

while(TRUE) 
{ 
    // Try to fill the buffer to capacity. 
    UInt32 framesRead = bufferSizeInFrames; 
    ExtAudioFileRead(sourceAudioFile, &framesRead, &bufferList); 

    // 0 frames read means EOF. 
    if(framesRead == 0) 
     break; 

    // Write. 
    ExtAudioFileWrite(destAudioFile, framesRead, &bufferList); 
} 

free(buffer); 

// Close the files. 
ExtAudioFileDispose(sourceAudioFile); 
ExtAudioFileDispose(destAudioFile); 
+0

이제까지이 기능을 사용할 수 있습니까? .wav를 .acc로 변환하려고합니다. – RyanG

+0

안녕 라이언, 아래 내 자신의 대답을 확인하십시오. – Sebastian

답변

0

샘플 속도가 맞습니까? 오류가 발생하는 시점에 clientFormatoutputFormat에 대한 값을 인쇄 할 수 있습니까? 그렇지 않으면 AudioConverter이 필요할 수도 있습니다.

11

내 질문에 답했습니다. 동료에게이 문제를 전달해야만했습니다. 나는 원래의 문제를 분석 할 기회가 없었지만 나는 완전성을 위해 여기에 게시 할 것이라고 생각했다. 다음 메소드는 NSThread 내에서 호출됩니다.

- (void)encodeToAAC 
{ 
    RXAudioEncoderStatusType encoderStatus; 
    OSStatus result = noErr; 
    BOOL success = NO; 
    BOOL cancelled = NO; 
    UInt32 size; 

    ExtAudioFileRef sourceAudioFile,destAudioFile; 
    AudioStreamBasicDescription sourceFormat,outputFormat, clientFormat; 

    SInt64 totalFrames; 
    unsigned long long encodedBytes, totalBytes; 

    int bufferSizeInFrames, bufferSize; 
    UInt8 * buffer; 
    AudioBufferList bufferList; 

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    NSFileManager * fileManager = [[[NSFileManager alloc] init] autorelease]; 

    NSMutableDictionary * threadDict = [[NSThread currentThread] threadDictionary]; 

    NSObject<RXAudioEncodingDelegate> * delegate = (NSObject<RXAudioEncodingDelegate> *)[threadDict objectForKey:@"Delegate"]; 

    NSString *sourcePath = (NSString *)[threadDict objectForKey:@"SourcePath"]; 
    NSString *destPath = (NSString *)[threadDict objectForKey:@"DestinationPath"]; 

    NSURL * sourceURL = [NSURL fileURLWithPath:sourcePath]; 
    NSURL * destURL = [NSURL fileURLWithPath:destPath]; 

    // Open a source audio file. 
    result = ExtAudioFileOpenURL((CFURLRef)sourceURL, &sourceAudioFile); 
    if(result != noErr) 
    { 
     DLog(@"Error in ExtAudioFileOpenURL: %ld", result); 
     goto bailout; 
    } 

    // Get the source data format 
    size = sizeof(sourceFormat); 
    result = ExtAudioFileGetProperty(sourceAudioFile, kExtAudioFileProperty_FileDataFormat, &size, &sourceFormat); 
    if(result != noErr) 
    { 
     DLog(@"Error in ExtAudioFileGetProperty: %ld", result); 
     goto bailout; 
    } 

    // Define the output format (AAC). 
    memset(&outputFormat, 0, sizeof(outputFormat)); 
    outputFormat.mFormatID = kAudioFormatMPEG4AAC; 
    outputFormat.mSampleRate = 44100; 
    outputFormat.mFormatFlags = kMPEG4Object_AAC_Main; 
    outputFormat.mChannelsPerFrame = 2; 
    outputFormat.mBitsPerChannel = 0; 
    outputFormat.mBytesPerFrame = 0; 
    outputFormat.mBytesPerPacket = 0; 
    outputFormat.mFramesPerPacket = 1024; 


    // Use AudioFormat API to fill out the rest of the description. 
    //size = sizeof(outputFormat); 
    //AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &outputFormat); 

    // Make a destination audio file with this output format. 
    result = ExtAudioFileCreateWithURL((CFURLRef)destURL, kAudioFileM4AType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &destAudioFile); 
    if(result != noErr) 
    { 
     DLog(@"Error creating destination file: %ld", result); 
     goto bailout; 
    } 

    // Create the canonical PCM client format. 
    memset(&clientFormat, 0, sizeof(clientFormat)); 
    clientFormat.mSampleRate = sourceFormat.mSampleRate; 
    clientFormat.mFormatID = kAudioFormatLinearPCM; 
    clientFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; //kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger; 
    clientFormat.mChannelsPerFrame = 2; 
    clientFormat.mBitsPerChannel = 16; 
    clientFormat.mBytesPerFrame = 4; 
    clientFormat.mBytesPerPacket = 4; 
    clientFormat.mFramesPerPacket = 1; 

    // Set the client format in source and destination file. 
    size = sizeof(clientFormat); 
    result = ExtAudioFileSetProperty(sourceAudioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat); 
    if(result != noErr) 
    { 
     DLog(@"Error while setting client format in source file: %ld", result); 
     goto bailout; 
    } 
    size = sizeof(clientFormat); 
    result = ExtAudioFileSetProperty(destAudioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat); 
    if(result != noErr) 
    { 
     DLog(@"Error while setting client format in destination file: %ld", result); 
     goto bailout; 
    } 

    // Make a buffer 
    bufferSizeInFrames = 8000; 
    bufferSize = (bufferSizeInFrames * sourceFormat.mBytesPerFrame); 
    buffer = (UInt8 *)malloc(bufferSize); 

    bufferList.mNumberBuffers = 1; 
    bufferList.mBuffers[0].mNumberChannels = clientFormat.mChannelsPerFrame; 
    bufferList.mBuffers[0].mData = buffer; 
    bufferList.mBuffers[0].mDataByteSize = (bufferSize); 

    // Obtain total number of audio frames to encode 
    size = sizeof(totalFrames); 
    result = ExtAudioFileGetProperty(sourceAudioFile, kExtAudioFileProperty_FileLengthFrames, &size, &totalFrames); 
    if(result != noErr) 
    { 
     DLog(@"Error in ExtAudioFileGetProperty, could not get kExtAudioFileProperty_FileLengthFrames from sourceFile: %ld", result); 
     goto bailout; 
    } 

    encodedBytes = 0; 
    totalBytes = totalFrames * sourceFormat.mBytesPerFrame; 
    [threadDict setValue:[NSValue value:&totalBytes withObjCType:@encode(unsigned long long)] forKey:@"TotalBytes"]; 

    if (delegate != nil) 
     [self performSelectorOnMainThread:@selector(didStartEncoding) withObject:nil waitUntilDone:NO]; 

    while(TRUE) 
    { 
     // Try to fill the buffer to capacity. 
     UInt32 framesRead = bufferSizeInFrames; 
     result = ExtAudioFileRead(sourceAudioFile, &framesRead, &bufferList); 
     if(result != noErr) 
     { 
      DLog(@"Error in ExtAudioFileRead: %ld", result); 
      success = NO; 
      break; 
     } 

     // 0 frames read means EOF. 
     if(framesRead == 0) { 
      success = YES; 
      break; 
     } 

     // Write. 
     result = ExtAudioFileWrite(destAudioFile, framesRead, &bufferList); 
     if(result != noErr) 
     { 
      DLog(@"Error in ExtAudioFileWrite: %ld", result); 
      success = NO; 
      break; 
     } 

     encodedBytes += framesRead * sourceFormat.mBytesPerFrame; 

     if (delegate != nil) 
      [self performSelectorOnMainThread:@selector(didEncodeBytes:) withObject:[NSValue value:&encodedBytes withObjCType:@encode(unsigned long long)] waitUntilDone:NO]; 

     if ([[NSThread currentThread] isCancelled]) { 
      cancelled = YES; 
      DLog(@"Encoding was cancelled."); 
      success = NO; 
      break; 
     } 
    } 

    free(buffer); 

    // Close the files. 
    ExtAudioFileDispose(sourceAudioFile); 
    ExtAudioFileDispose(destAudioFile); 

bailout: 
    encoderStatus.result = result; 
    [threadDict setValue:[NSValue value:&encoderStatus withObjCType:@encode(RXAudioEncoderStatusType)] forKey:@"EncodingError"]; 

    // Report to the delegate if one exists 
    if (delegate != nil) 
     if (success) 
      [self performSelectorOnMainThread:@selector(didEncodeFile) withObject:nil waitUntilDone:YES]; 
     else if (cancelled) 
      [self performSelectorOnMainThread:@selector(encodingCancelled) withObject:nil waitUntilDone:YES]; 
     else 
      [self performSelectorOnMainThread:@selector(failedToEncodeFile) withObject:nil waitUntilDone:YES]; 

    // Clear the partially encoded file if encoding failed or is cancelled midway 
    if ((cancelled || !success) && [fileManager fileExistsAtPath:destPath]) 
     [fileManager removeItemAtURL:destURL error:NULL]; 

    [threadDict setValue:[NSNumber numberWithBool:NO] forKey:@"isEncoding"]; 

    [pool release]; 
} 
+0

좋아 보이는데, 정답으로 표시하지 않으시겠습니까? – newenglander

+0

감사합니다. 대단 하네! – John

0

: 매개 변수가 'threadDictionary'로 설정하고 그 진행 상황 피드백을 전송하는 사용자 정의 대리자를 만들어 (죄송합니다, SO 제대로 형식을 이해하지 않습니다, 다음은 메소드 구현의 한 블록 있어야한다) 나는 Sebastian의 답변에서 코드를 시험해 보았고 압축되지 않은 파일 (aif, wav, caf)에서 작동했지만 손실이 많은 압축 파일 (mp3)에서는 작동하지 않았습니다. 또한 -50의 오류 코드가 있지만 ExtAudioFileSetProperty이 아닌 ExtAudioFileRead에 있습니다. 이 question에서이 오류는 함수 매개 변수의 문제점을 나타냅니다.

int bufferSize = (bufferSizeInFrames * sourceFormat.mBytesPerFrame); 

대신 clientFormat에서 프레임 당 바이트를 사용하도록 전환 (sourceFormat의 값은 0) 일 : 오디오 파일을 읽기 위해 버퍼를 밝혀 0 바이트 크기,이 라인의 결과가 있었다 나를 위해 :

int bufferSize = (bufferSizeInFrames * clientFormat.mBytesPerFrame); 

이 줄은 질문 코드도했지만, 나는 문제가 있었다 생각하지 않습니다 (그러나 나는 코멘트에 대한 너무 많은 텍스트를했다).

+0

iOS에는 mp3 압축기가 없습니다. –

관련 문제