gpt4 book ai didi

ios - AVAssetReader 以低质量播放 MPMediaItem?

转载 作者:可可西里 更新时间:2023-11-01 06:16:51 25 4
gpt4 key购买 nike

在结合了几个 SO 问题(如 this one)的答案后,我已经设法使用 AVAssetReader 从 MPMediaItem 获取原始数据。和 this one和一个不错的blog post .我也可以使用 FMOD 播放此原始数据,但问题出现了。

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

是我做错了什么还是 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);
}

最佳答案

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

希望对你有帮助

关于ios - AVAssetReader 以低质量播放 MPMediaItem?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9616828/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com