2011-02-10 17 views
9

Potrzebuję przekonwertować plik WAVE do pliku M4A zakodowanego AAC na iOS. Mam świadomość, że kodowanie AAC nie jest obsługiwane na starszych urządzeniach ani w symulatorze. Testuję to zanim uruchomię kod. Ale nadal nie mogę zmusić go do działania.Jak mogę uzyskać kodowanie AAC za pomocą ExtAudioFile na iOS, aby działał?

Spojrzałem na własny przykład Apple iPhoneExtAudioFileConvertTest i pomyślałem, że poszedłem dokładnie, ale wciąż nie ma szczęścia!

Obecnie otrzymuję -50 (= błąd na liście parametrów użytkownika) podczas próby ustawienia formatu klienta w pliku docelowym. W pliku źródłowym działa.

Poniżej znajduje się mój kod. Każda pomoc jest bardzo doceniana, dzięki!

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

Czy to działa? Chciałbym również przekonwertować plik .wav na .acc. – RyanG

+0

Hej Ryan, sprawdź moją własną odpowiedź na to poniżej. – Sebastian

Odpowiedz

0

Czy jesteś pewien, że mecz próbkowania? Czy możesz wydrukować wartości clientFormat i outputFormat w miejscu, w którym pojawia się błąd? W przeciwnym razie myślę, że możesz potrzebować AudioConverter.

11

Odpowiedziałem na moje własne pytanie: Musiałem przekazać ten problem mojemu koledze i on to zrobił! Nigdy nie miałem szansy na przeanalizowanie mojego pierwotnego problemu, ale pomyślałem, że zamieściłbym go tutaj ze względu na kompletność. Następująca metoda jest wywoływana z poziomu NSThread. Parametry są ustawione przez „threadDictionary” i stworzył własny delegata do przekazywania informacji zwrotnej o postępach (przepraszam, SO nie rozumie formatowanie odpowiednio, następujące ma być jeden blok realizacji metody):

- (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

Wygląda dobrze, czemu nie oznaczyć tego jako prawidłowej odpowiedzi? – newenglander

+0

Dzięki. Wspaniale! – John

0

Wypróbowałem kod w odpowiedzi Sebastiana i podczas gdy pracował dla nieskompresowanych plików (aif, wav, caf), nie działał na stratny skompresowany plik (mp3). Miałem także kod błędu -50, ale w ExtAudioFileRead zamiast ExtAudioFileSetProperty. Z tego question dowiedziałem się, że ten błąd oznacza problem z parametrami funkcji. Okazuje się, że bufor do odczytu pliku audio miało rozmiar 0 bajtów, a wynik tej linii:

int bufferSize = (bufferSizeInFrames * sourceFormat.mBytesPerFrame); 

włączeniem użyć bajtów na ramkę z clientFormat zamiast (sourceFormat jest wartość 0) pracował dla mnie:

int bufferSize = (bufferSizeInFrames * clientFormat.mBytesPerFrame); 

linia ta była również w kodzie zapytania, ale nie sądzę, że był problem (ale nie miałem zbyt dużo tekstu do komentarza).

+0

iOS nie ma kompresora mp3 –