【问题标题】:AVAssetReader playing MPMediaItem in low quality?AVAssetReader 以低质量播放 MPMediaItem?
【发布时间】:2012-03-08 11:19:31
【问题描述】:

在结合了几个 SO 问题(如 this onethis one 以及一个不错的 blog post 等问题的答案后,我设法使用 AVAssetReader 从 MPMediaItem 获取原始数据。我也可以使用 FMOD 播放这些原始数据,但是出现了问题。

生成的音频质量似乎低于原始音轨。虽然 AVAssetTrack formatDescription 告诉我数据中有 2 个通道,但结果听起来是单声道的。它也听起来有点阻尼(不那么脆),就像比特率降低了一样。

是我做错了什么还是 AVAssetReader 故意降低了 MPMediaItem 数据的质量(因为盗版)?


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

【问题讨论】:

    标签: ios audio avfoundation fmod


    【解决方案1】:

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

    希望对你有帮助

    【讨论】:

      【解决方案2】:

      您正在初始化 FMOD 以输出单声道音频。试试

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

      【讨论】:

        猜你喜欢
        • 2014-02-17
        • 2016-11-15
        • 2018-08-19
        • 1970-01-01
        • 2011-11-16
        • 1970-01-01
        • 2020-12-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多