2015-02-04 7 views
0

Мое приложение использует AVAssetReader для воспроизведения песен в библиотеке iPOD. Теперь я хочу добавить возможность записи звука.Использование AVAssetWriter и AVAssetReader для записи и воспроизведения звука

Я записал аудио с использованием AVAssetWriter. Я проверил полученный аудиофайл (формат MPEG4AAC), успешно воспроизведя его с помощью AVAudioPlayer. Моя цель - воспроизвести аудио с помощью AVAssetReader. Но когда я создаю AVURLAsset для файла, у него нет трека, и, следовательно, AVAssetReader не работает (код ошибки: -11828 Формат файла не распознан).

Что делать, чтобы AVAsset распознал формат файла? Есть ли какой-то специальный формат файла, необходимый для AVAsset?

Вот коды для записи:

void setup_ASBD(void *f, double fs, int sel, int numChannels); 
static AVAssetWriter *assetWriter = NULL; 
static AVAssetWriterInput *assetWriterInput = NULL; 
static CMAudioFormatDescriptionRef formatDesc; 
AVAssetWriter *newAssetWriter(NSURL *url) { 
    NSError *outError; 
    assetWriter = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeAppleM4A error:&outError]; 

    if(assetWriter == nil) { 
     NSLog(@"%s: asset=%x, %@\n", __FUNCTION__, (int)assetWriter, outError); 
     return assetWriter; 
    } 

    AudioChannelLayout audioChannelLayout = { 
     .mChannelLayoutTag = kAudioChannelLayoutTag_Mono, 
     .mChannelBitmap = 0, 
     .mNumberChannelDescriptions = 0 
    }; 

    // Convert the channel layout object to an NSData object. 
    NSData *channelLayoutAsData = [NSData dataWithBytes:&audioChannelLayout length:offsetof(AudioChannelLayout, mChannelDescriptions)]; 

    // Get the compression settings for 128 kbps AAC. 
    NSDictionary *compressionAudioSettings = @{ 
               AVFormatIDKey   : [NSNumber numberWithUnsignedInt:kAudioFormatMPEG4AAC], 
               AVEncoderBitRateKey : [NSNumber numberWithInteger:128000], 
               AVSampleRateKey  : [NSNumber numberWithInteger:44100], 
               AVChannelLayoutKey : channelLayoutAsData, 
               AVNumberOfChannelsKey : [NSNumber numberWithUnsignedInteger:1] 
               }; 

    // Create the asset writer input with the compression settings and specify the media type as audio. 
    assetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:compressionAudioSettings]; 
    assetWriterInput.expectsMediaDataInRealTime = YES; 

    // Add the input to the writer if possible. 
    if (assetWriterInput != NULL && [assetWriter canAddInput:assetWriterInput]) { 
     [assetWriter addInput:assetWriterInput]; 
    } 
    else { 
     NSLog(@"%s:assetWriteInput problem: %x\n", __FUNCTION__, (int)assetWriterInput); 
     return NULL; 
    } 

    [assetWriter startWriting]; 
    // Start a sample-writing session. 
    [assetWriter startSessionAtSourceTime:kCMTimeZero]; 

    if(assetWriter.status != AVAssetWriterStatusWriting) { 
     NSLog(@"%s: Bad writer status=%d\n", __FUNCTION__, (int)assetWriter.status); 
     return NULL; 
    } 

    AudioStreamBasicDescription ASBD; 
    setup_ASBD(&ASBD, 44100, 2, 1); 
    CMAudioFormatDescriptionCreate (NULL, &ASBD, sizeof(audioChannelLayout), &audioChannelLayout, 0, NULL, NULL, &formatDesc); 
    //CMAudioFormatDescriptionCreate (NULL, &ASBD, 0, NULL, 0, NULL, NULL, &formatDesc); 

    return assetWriter; 

} 

static int sampleCnt = 0; 
void writeNewSamples(void *buffer, int len) { 
    if(assetWriterInput == NULL) return; 
    if([assetWriterInput isReadyForMoreMediaData]) { 
     OSStatus result; 
     CMBlockBufferRef blockBuffer = NULL; 
     result = CMBlockBufferCreateWithMemoryBlock (NULL, buffer, len, NULL, NULL, 0, len, 0, &blockBuffer); 
     if(result == noErr) { 
      CMItemCount numSamples = len >> 1; 

      const CMSampleTimingInfo sampleTiming = {CMTimeMake(1, 44100), CMTimeMake(sampleCnt, 44100), kCMTimeInvalid}; 
      CMItemCount numSampleTimingEntries = 1; 

      const size_t sampleSize = 2; 
      CMItemCount numSampleSizeEntries = 1; 

      CMSampleBufferRef sampleBuffer; 
      result = CMSampleBufferCreate(NULL, blockBuffer, true, NULL, NULL, formatDesc, numSamples, numSampleTimingEntries, &sampleTiming, numSampleSizeEntries, &sampleSize, &sampleBuffer); 

      if(result == noErr) { 
       if([assetWriterInput appendSampleBuffer:sampleBuffer] == YES) sampleCnt += numSamples; 
       else { 
        NSLog(@"%s: ERROR\n", __FUNCTION__); 
       } 
       printf("sampleCnt = %d\n", sampleCnt); 
       CFRelease(sampleBuffer); 

      } 
     } 
    } 
    else { 
     NSLog(@"%s: AVAssetWriterInput not taking input data: status=%ld\n", __FUNCTION__, assetWriter.status); 
    } 
} 

void stopAssetWriter(AVAssetWriter *assetWriter) { 
    [assetWriterInput markAsFinished]; 
    [assetWriter finishWritingWithCompletionHandler:^{ 
     NSLog(@"%s: Done: %ld: %d samples\n", __FUNCTION__, assetWriter.status, sampleCnt); 
     sampleCnt = 0; 
    }]; 
    assetWriterInput = NULL; 
} 

ответ

1

Оказывается, что AVAsset ожидает "действительный" расширение файла. Поэтому, когда имя файла не имеет одного из этих общих расширений, таких как * .mp3, * .caf, * .m4a и т. Д., AVAsset отказывается рассматривать заголовок файла, чтобы определить формат носителя. С другой стороны, AVAudioPlay кажется совершенно безразличным к имени файла и самостоятельно определяет формат мультимедиа, просматривая заголовок файла.

Эта разница не появляется нигде в документе Apple. и я закончил тем, что потратил больше недели на это. Sigh ...