1

我正在使用 AudioQueue 流式传输一些歌曲,我的问题是如何判断已排队缓冲区的播放长度?我想一次传输两秒的数据,我遇到的问题是我怎么知道有多少字节实际上对应于两秒的音乐(所以我总是可以领先两秒)。

谢谢

丹尼尔

4

2 回答 2

3

这是一个使用音频文件服务获取比特率/数据包/帧数据的类,以从音乐文件中获取对应于 x 秒的字节量,该示例已使用 mp3 和 m4a 文件进行测试

标题

#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
@interface MusicChunker : NSObject
{
    AudioFileID audioFile;
    int _sampleRate;
    int _totalFrames;
    UInt64 _framesPerPacket;
    UInt64 _totalPackets;
    UInt64 fileDataSize;
    AudioFilePacketTableInfo _packetInfo;
    int _fileLength;
    AudioStreamBasicDescription _fileDataFormat;
    NSFileHandle * _fileHandle;
    int _packetOffset;
    int _totalReadBytes;
    int _maxPacketSize;
    BOOL firstTime;
    BOOL _ism4a;
}
-(id)initWithURL:(NSURL*)url andFileType:(NSString*)ext;
//gets next chunk that corresponds to seconds of audio
-(NSData*)getNextDataChunk:(int)seconds;
@end

执行

#import "MusicChunker.h"
void ReportAudioError(OSStatus statusCode);
@implementation MusicChunker

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }

    return self;
}
void ReportAudioError(OSStatus statusCode) {
    switch (statusCode) {
        case noErr:
            break;
        case kAudioFileUnspecifiedError:
            [NSException raise:@"AudioFileUnspecifiedError" format:@"An unspecified error occured."];
            break;
        case kAudioFileUnsupportedDataFormatError:
            [NSException raise:@"AudioFileUnsupportedDataFormatError" format:@"The data format is not supported by the output file type."];
            break;
        case kAudioFileUnsupportedFileTypeError:
            [NSException raise:@"AudioFileUnsupportedFileTypeError" format:@"The file type is not supported."];
            break;
        case kAudioFileUnsupportedPropertyError:
            [NSException raise:@"AudioFileUnsupportedPropertyError" format:@"A file property is not supported."];
            break;
        case kAudioFilePermissionsError:
            [NSException raise:@"AudioFilePermissionsError" format:@"The operation violated the file permissions. For example, an attempt was made to write to a file opened with the kAudioFileReadPermission constant."];
            break;
        case kAudioFileNotOptimizedError:
            [NSException raise:@"AudioFileNotOptimizedError" format:@"The chunks following the audio data chunk are preventing the extension of the audio data chunk. To write more data, you must optimize the file."];
            break;
        case kAudioFileInvalidChunkError:
            [NSException raise:@"AudioFileInvalidChunkError" format:@"Either the chunk does not exist in the file or it is not supported by the file."];
            break;
        case kAudioFileDoesNotAllow64BitDataSizeError:
            [NSException raise:@"AudioFileDoesNotAllow64BitDataSizeError" format:@"The file offset was too large for the file type. The AIFF and WAVE file format types have 32-bit file size limits."];
            break;
        case kAudioFileInvalidPacketOffsetError:
            [NSException raise:@"AudioFileInvalidPacketOffsetError" format:@"A packet offset was past the end of the file, or not at the end of the file when a VBR format was written, or a corrupt packet size was read when the packet table was built."];
            break;
        case kAudioFileInvalidFileError:
            [NSException raise:@"AudioFileInvalidFileError" format:@"The file is malformed, or otherwise not a valid instance of an audio file of its type."];
            break;
        case kAudioFileOperationNotSupportedError:
            [NSException raise:@"AudioFileOperationNotSupportedError" format:@"The operation cannot be performed. For example, setting the kAudioFilePropertyAudioDataByteCount constant to increase the size of the audio data in a file is not a supported operation. Write the data instead."];
            break;
        case -50:
            [NSException raise:@"AudioFileBadParameter" format:@"An invalid parameter was passed, possibly the current packet and/or the inNumberOfPackets."];
            break;
        default:
            [NSException raise:@"AudioFileUknownError" format:@"An unknown error type %@ occured. [%s]", [NSNumber numberWithInteger:statusCode], (char*)&statusCode];
            break;
    }
}

+ (AudioFileTypeID)hintForFileExtension:(NSString *)fileExtension
{
    AudioFileTypeID fileTypeHint = kAudioFileAAC_ADTSType;
    if ([fileExtension isEqual:@"mp3"])
    {
        fileTypeHint = kAudioFileMP3Type;
    }
    else if ([fileExtension isEqual:@"wav"])
    {
        fileTypeHint = kAudioFileWAVEType;
    }
    else if ([fileExtension isEqual:@"aifc"])
    {
        fileTypeHint = kAudioFileAIFCType;
    }
    else if ([fileExtension isEqual:@"aiff"])
    {
        fileTypeHint = kAudioFileAIFFType;
    }
    else if ([fileExtension isEqual:@"m4a"])
    {
        fileTypeHint = kAudioFileM4AType;
    }
    else if ([fileExtension isEqual:@"mp4"])
    {
        fileTypeHint = kAudioFileMPEG4Type;
    }
    else if ([fileExtension isEqual:@"caf"])
    {
        fileTypeHint = kAudioFileCAFType;
    }
    else if ([fileExtension isEqual:@"aac"])
    {
        fileTypeHint = kAudioFileAAC_ADTSType;
    }
    return fileTypeHint;
}

-(id)initWithURL:(NSURL*)url andFileType:(NSString*)ext
{
    self = [super init];
    if (self) {
        // Initialization code here.
        //OSStatus theErr = noErr;
        if([ext isEqualToString:@"mp3"])
        {
            _ism4a=FALSE;
        }
        else
            _ism4a=TRUE;
        firstTime=TRUE;
        _packetOffset=0;
        AudioFileTypeID hint=[MusicChunker hintForFileExtension:ext];
        OSStatus theErr = AudioFileOpenURL((CFURLRef)url, kAudioFileReadPermission, hint, &audioFile);
        if(theErr)
        {
            ReportAudioError(theErr);

        }

        UInt32 thePropertySize;// = sizeof(theFileFormat);

        thePropertySize = sizeof(fileDataSize);
        theErr = AudioFileGetProperty(audioFile, kAudioFilePropertyAudioDataByteCount, &thePropertySize, &fileDataSize);
        if(theErr)
        {
            ReportAudioError(theErr);

        }

        theErr = AudioFileGetProperty(audioFile,    kAudioFilePropertyAudioDataPacketCount, &thePropertySize, &_totalPackets);
        if(theErr)
        {
            ReportAudioError(theErr);

        }
        /*
        UInt32 size;

        size= sizeof(_packetInfo);
        theErr= AudioFileGetProperty(audioFile, kAudioFilePropertyPacketTableInfo, &size, &_packetInfo);
  g(@"Key %@", key );
         }

        if(theErr)
        {
            ReportAudioError(theErr);
        }
        */
        UInt32 size;
        size=sizeof(_maxPacketSize);
        theErr=AudioFileGetProperty(audioFile,  kAudioFilePropertyMaximumPacketSize , &size, &_maxPacketSize);

        size = sizeof( _fileDataFormat );
        theErr=AudioFileGetProperty( audioFile, kAudioFilePropertyDataFormat, &size, &_fileDataFormat );
        _framesPerPacket=_fileDataFormat.mFramesPerPacket;
        _totalFrames=_fileDataFormat.mFramesPerPacket*_totalPackets;

        _fileHandle=[[NSFileHandle fileHandleForReadingFromURL:url error:nil] retain];    
        _fileLength=[_fileHandle seekToEndOfFile];
        _sampleRate=_fileDataFormat.mSampleRate;
        _totalReadBytes=0;
        /*
         AudioFramePacketTranslation tran;//= .mFrame = 0, .mPacket = packetCount - 1, .mFrameOffsetInPacket = 0 };
         tran.mFrame=0;
         tran.mFrameOffsetInPacket=0;
         tran.mPacket=1;
         UInt32 size=sizeof(tran);
         theErr=AudioFileGetProperty(audioFile, kAudioFilePropertyPacketToFrame, &size, &tran);
         */
        /*
         AudioBytePacketTranslation bt;
         bt.mPacket=4;
         bt.mByteOffsetInPacket=0;
         size=sizeof(bt);
         theErr=AudioFileGetProperty(audioFile, kAudioFilePropertyPacketToByte, &size, &bt);
         */


    }

    return self;
}
//gets next chunk that corresponds to seconds of audio
-(NSData*)getNextDataChunk:(int)seconds
{

    //NSLog(@"%d, total packets",_totalPackets);

    if(_packetOffset>=_totalPackets)
        return nil;

    //sampleRate * seconds = number of wanted frames
    int framesWanted= _sampleRate*seconds;
    NSData *header=nil;
    int wantedPackets=  framesWanted/_framesPerPacket;
    if(firstTime && _ism4a)
    {
        firstTime=false;
        //when we have a header that was stripped off, we grab it from the original file
        int totallen= [_fileHandle seekToEndOfFile];
        int dif=totallen-fileDataSize;
        [_fileHandle seekToFileOffset:0];
        header= [_fileHandle readDataOfLength:dif];
     }



    int packetOffset=_packetOffset+wantedPackets;

    //bound condition
    if(packetOffset>_totalPackets)
    {
        packetOffset=_totalPackets;
    }


    UInt32 outBytes;

    UInt32 packetCount = wantedPackets;
    int x=packetCount * _maxPacketSize;
    void *data = (void *)malloc(x);

    OSStatus theErr=AudioFileReadPackets(audioFile, false, &outBytes, NULL, _packetOffset, &packetCount, data);

    if(theErr)
    {
        ReportAudioError(theErr);
    }
    //calculate bytes to read

    int bytesRead=outBytes;

    //update read bytes
    _totalReadBytes+=bytesRead;
   // NSLog(@"total bytes read %d", _totalReadBytes);
    _packetOffset=packetOffset;



    NSData *subdata=[[NSData dataWithBytes:data length:outBytes] retain];    

    free(data);

    if(header)
    {
        NSMutableData *data=[[NSMutableData alloc]init];
        [data appendData:header];
        [data appendData:subdata];
        [subdata release];
        return [data autorelease];
    }

    return [subdata autorelease];
}

@end
于 2011-09-11T20:47:30.647 回答
1

如果歌曲采用任意压缩格式,并且您想要精确的 2 秒剪辑,您可能必须首先将歌曲转换为原始 PCM 样本或 WAV 数据(AVAssetReader 等)。然后,您可以以已知的采样率对样本进行计数。例如,44.1k 采样率的 88200 帧将是 2 秒的价值。

于 2011-09-11T00:04:44.293 回答