¿AVAssetReader reproduce MPMediaItem en baja calidad?

Logré get los datos brutos de un MPMediaItem utilizando un AVAssetReader después de combinar las respuestas de un par de preguntas SO como éste y esta y una buena publicación de blog . También puedo reproducir esta información en bruto usando FMOD, pero luego surge un problema.

Parece que el audio resultante es de menor calidad que la pista original. Aunque AVAssetTrack formatDescription me dice que hay 2 canales en los datos, el resultado suena mono. También suena un poco humedecido (less crujiente), como la tasa de bits baja.

¿Estoy haciendo algo mal o es la calidad de los datos MPMediaItem bajada a propósito por el AVAssetReader (debido a la piratería)?


#define OUTPUTRATE 44100 

Inicializando AVAssetReader y AVAssetReaderTrackOutput

 // prepare AVAsset and AVAssetReaderOutput etc MPMediaItem* mediaItem = ...; NSURL* ipodAudioUrl = [mediaItem valueForProperty:MPMediaItemPropertyAssetURL]; AVURLAsset * asset = [[AVURLAsset alloc] initWithURL:ipodAudioUrl options:nil]; NSError * error = nil; assetReader = [[AVAssetReader alloc] initWithAsset:asset error:&error]; if(error) NSLog(@"error creating reader: %@", [error debugDescription]); AVAssetTrack* songTrack = [asset.tracks objectAtIndex:0]; NSArray* trackDescriptions = songTrack.formatDescriptions; numChannels = 2; for(unsigned int i = 0; i < [trackDescriptions count]; ++i) { CMAudioFormatDescriptionRef item = (CMAudioFormatDescriptionRef)[trackDescriptions objectAtIndex:i]; const AudioStreamBasicDescription* bobTheDesc = CMAudioFormatDescriptionGetStreamBasicDescription (item); if(bobTheDesc && bobTheDesc->mChannelsPerFrame == 1) { numChannels = 1; } } NSDictionary* outputSettingsDict = [[[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey, [NSNumber numberWithInt:OUTPUTRATE],AVSampleRateKey, [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey, [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey, [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey, [NSNumber numberWithBool:NO],AVLinearPCMIsNonInterleaved, nil] autorelease]; AVAssetReaderTrackOutput * output = [[[AVAssetReaderTrackOutput alloc] initWithTrack:songTrack outputSettings:outputSettingsDict] autorelease]; [assetReader addOutput:output]; [assetReader startReading]; 

Inicializando el FMOD y el sonido de FMOD

 // Init FMOD FMOD_RESULT result = FMOD_OK; unsigned int version = 0; /* Create a System object and initialize */ result = FMOD::System_Create(&system); ERRCHECK(result); result = system->getVersion(&version); ERRCHECK(result); if (version < FMOD_VERSION) { fprintf(stderr, "You are using an old version of FMOD %08x. This program requires %08x\n", version, FMOD_VERSION); exit(-1); } result = system->setSoftwareFormat(OUTPUTRATE, FMOD_SOUND_FORMAT_PCM16, 1, 0, FMOD_DSP_RESAMPLER_LINEAR); ERRCHECK(result); result = system->init(32, FMOD_INIT_NORMAL | FMOD_INIT_ENABLE_PROFILE, NULL); ERRCHECK(result); // Init FMOD sound stream CMTimeRange timeRange = [songTrack timeRange]; float durationInSeconds = timeRange.duration.value / timeRange.duration.timescale; FMOD_CREATESOUNDEXINFO exinfo = {0}; memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); /* requinetworking. */ exinfo.decodebuffersize = OUTPUTRATE; /* Chunk size of stream update in samples. This will be the amount of data passed to the user callback. */ exinfo.length = OUTPUTRATE * numChannels * sizeof(signed short) * durationInSeconds; /* Length of PCM data in bytes of whole song (for Sound::getLength) */ exinfo.numchannels = numChannels; /* Number of channels in the sound. */ exinfo.defaultfrequency = OUTPUTRATE; /* Default playback rate of sound. */ exinfo.format = FMOD_SOUND_FORMAT_PCM16; /* Data format of sound. */ exinfo.pcmreadcallback = pcmreadcallback; /* User callback for reading. */ exinfo.pcmsetposcallback = pcmsetposcallback; /* User callback for seeking. */ result = system->createStream(NULL, FMOD_OPENUSER, &exinfo, &sound); ERRCHECK(result); result = system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel); ERRCHECK(result); 

Lectura desde AVAssetReaderTrackOutput en un buffer de anillo

 AVAssetReaderTrackOutput * trackOutput = (AVAssetReaderTrackOutput *)[assetReader.outputs objectAtIndex:0]; CMSampleBufferRef sampleBufferRef = [trackOutput copyNextSampleBuffer]; if (sampleBufferRef) { AudioBufferList audioBufferList; CMBlockBufferRef blockBuffer; CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBufferRef, NULL, &audioBufferList, sizeof(audioBufferList), NULL, NULL, 0, &blockBuffer); if(blockBuffer == NULL) { stopLoading = YES; continue; } if(&audioBufferList == NULL) { stopLoading = YES; continue; } if(audioBufferList.mNumberBuffers != 1) NSLog(@"numBuffers = %lu", audioBufferList.mNumberBuffers); for( int y=0; y<audioBufferList.mNumberBuffers; y++ ) { AudioBuffer audioBuffer = audioBufferList.mBuffers[y]; SInt8 *frame = (SInt8*)audioBuffer.mData; for(int i=0; i<audioBufferList.mBuffers[y].mDataByteSize; i++) { ringBuffer->push_back(frame[i]); } } CMSampleBufferInvalidate(sampleBufferRef); CFRelease(sampleBufferRef); } 

    No estoy familiarizado con FMOD, así que no puedo comentar allí. AVAssetReader no hace ningún tipo de "protección contra copy", por lo que no es una preocupación. (Si puede get AVAssetURL, la pista está libre de DRM)

    Dado que está utilizando búferes no intercalados, solo habrá un búfer, así que supongo que su último bit de código podría estar mal

    Aquí hay un ejemplo de código que funciona bien para mí. Por cierto, tu ciclo for probablemente no va a ser muy eficaz. Puede considerar usar memcpy o algo así … Si no está restringido a su búfer de anillo existente, intente TPCircularBuffer ( https://github.com/michaeltyson/TPCircularBuffer ) es increíble.

     CMSampleBufferRef nextBuffer = NULL; if(_reader.status == AVAssetReaderStatusReading) { nextBuffer = [_readerOutput copyNextSampleBuffer]; } if (nextBuffer) { AudioBufferList abl; CMBlockBufferRef blockBuffer; CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer( nextBuffer, NULL, &abl, sizeof(abl), NULL, NULL, kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBuffer); // the correct way to get the number of bytes in the buffer size_t size = CMSampleBufferGetTotalSampleSize(nextBuffer); memcpy(ringBufferTail, abl.mBuffers[0].mData, size); CFRelease(nextBuffer); CFRelease(blockBuffer); } 

    Espero que esto ayude

    Está iniciando FMOD para producir audio mono. Tratar

     result = system->setSoftwareFormat(OUTPUTRATE, FMOD_SOUND_FORMAT_PCM16, 2, 0, FMOD_DSP_RESAMPLER_LINEAR);