AVAssetReader проигрывает MPMediaItem в низком качестве? - PullRequest
4 голосов
/ 08 марта 2012

Мне удалось получить необработанные данные из MPMediaItem с помощью AVAssetReader после объединения ответов на пару вопросов SO, таких как этот и этот и приятный сообщение в блоге .Я также могу воспроизводить эти необработанные данные с помощью FMOD, но затем возникает проблема.

Получается, что получающееся в результате аудио имеет более низкое качество, чем исходная дорожка.Хотя AVAssetTrack formatDescription говорит мне, что в данных есть 2 канала, результат звучит моно.Это также звучит немного приглушенно (менее резким), как при снижении битрейта.

Я что-то не так делаю или качество данных MPMediaItem специально понижено AVAssetReader (из-за пиратства)?


#define OUTPUTRATE   44100

Инициализация AVAssetReader и 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];

Инициализация FMOD и звука 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);              /* 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);

Чтение из AVAssetReaderTrackOutput в кольцевой буфер

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

Ответы [ 2 ]

0 голосов
/ 29 марта 2014

Вы инициализируете FMOD для вывода монофонического звука. Попробуйте

result = system->setSoftwareFormat(OUTPUTRATE, FMOD_SOUND_FORMAT_PCM16, 2, 0, FMOD_DSP_RESAMPLER_LINEAR);
0 голосов
/ 28 марта 2014

Я не знаком с FMOD, поэтому я не могу комментировать там.AVAssetReader не выполняет никаких действий по защите от копирования, так что это не волнует.(Если вы можете получить AVAssetURL, дорожка свободна от DRM)

Поскольку вы используете не чередующиеся буферы, будет только один буфер, поэтому я предполагаю, что ваш последний бит кода может быть неправильным

Вот пример кода, который хорошо работает для меня.Кстати, ваш цикл for, вероятно, не будет очень производительным.Вы можете рассмотреть возможность использования memcpy или чего-то еще ... Если вы не ограничены существующим кольцевым буфером, попробуйте TPCircularBuffer (https://github.com/michaeltyson/TPCircularBuffer), это удивительно.

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

Надеюсь, это поможет

...