Does AVAssetReader play MPMediaItem in low quality?

I managed to get the raw data from MPMediaItem using AVAssetReader after combining the answers to several SO questions like this and this and a nice post. I can also play this raw data using FMOD, but then a problem arises.

It seems that the resulting sound has lower quality than the original track. Although the AVAssetTrackDescription format tells me that there are 2 channels in the data, the result sounds monophonic. It also sounds slightly moist (less crunchy), like the bitrate.

Am I doing something wrong or is it the quality of MPMediaItem data specifically designed for AVAssetReader (due to piracy)?


#define OUTPUTRATE 44100 

Initializing AVAssetReader and 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]; 

Initializing FMOD and FMOD Sound

 // 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); /* required. */ 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); 

Reading from AVAssetReaderTrackOutput to the ring buffer

 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); } 
+4
source share
2 answers

I am not familiar with FMOD, so I can not comment there. AVAssetReader does not make any "copy protection" so as not to worry. (If you can get AVAssetURL, a track without DRM)

Since you are using unmoved buffers, there will only be one buffer, so I think your last bit of code may be wrong

Here is sample code that works well for me. Btw, your for loop probably won't be very productive. You can use memcpy or something else ... If you are not limited to the existing ring buffer, try TPCircularBuffer ( https://github.com/michaeltyson/TPCircularBuffer ), this is awesome.

 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); } 

Hope this helps

0
source

You initialize the FMOD to output monaural sound. Try

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

Source: https://habr.com/ru/post/1400370/


All Articles