0

因此,我想获取一个声音文件并将其转换为数据包,然后将其发送到另一台计算机。我希望另一台计算机能够在数据包到达时播放它们。

我正在使用 AVAudioPlayer 尝试播放此数据包,但我找不到正确的方法来序列化 peer2 可以播放的 peer1 上的数据。

场景是,peer1 有一个音频文件,将音频文件拆分为许多小数据包,将它们放在一个 NSData 上并将它们发送给 peer2。对等方 2 接收数据包并在它们到达时一一播放。

有谁知道该怎么做?或者即使有可能?

编辑:

这是一些代码来说明我想要实现的目标。


// This code is part of the peer1, the one who sends the data
- (void)sendData
{
    int packetId = 0;
    NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"myAudioFile" ofType:@"wav"];

    NSData *soundData = [[NSData alloc] initWithContentsOfFile:soundFilePath];
    NSMutableArray *arraySoundData = [[NSMutableArray alloc] init];

    // Spliting the audio in 2 pieces
    // This is only an illustration
    // The idea is to split the data into multiple pieces
    // dependin on the size of the file to be sent
    NSRange soundRange;
    soundRange.length = [soundData length]/2;
    soundRange.location = 0;
    [arraySoundData addObject:[soundData subdataWithRange:soundRange]];
    soundRange.length = [soundData length]/2;
    soundRange.location = [soundData length]/2;
    [arraySoundData addObject:[soundData subdataWithRange:soundRange]];

    for (int i=0; i<[arraySoundData count]; i++)
    {
        NSData *soundPacket = [arraySoundData objectAtIndex:i];

        if(soundPacket == nil)
        {
            NSLog(@"soundData is nil");
            return;
        }       

        NSMutableData* message = [[NSMutableData alloc] init];
        NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:message];
        [archiver encodeInt:packetId++ forKey:PACKET_ID];
        [archiver encodeObject:soundPacket forKey:PACKET_SOUND_DATA];
        [archiver finishEncoding];      

        NSError* error = nil;
        [connectionManager sendMessage:message error:&error];
        if (error) NSLog (@"send greeting failed: %@" , [error localizedDescription]);

        [message release];
        [archiver release];
    }

    [soundData release];
    [arraySoundData release];
}

// This is the code on peer2 that would receive and play the piece of audio on each packet

- (void) receiveData:(NSData *)data
{

    NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

    if ([unarchiver containsValueForKey:PACKET_ID])
        NSLog(@"DECODED PACKET_ID: %i", [unarchiver decodeIntForKey:PACKET_ID]);

    if ([unarchiver containsValueForKey:PACKET_SOUND_DATA])
    {
        NSLog(@"DECODED sound");
        NSData *sound = (NSData *)[unarchiver decodeObjectForKey:PACKET_SOUND_DATA];

        if (sound == nil)
        {
            NSLog(@"sound is nil!");

        }
        else
        {
            NSLog(@"sound is not nil!");

            AVAudioPlayer *audioPlayer = [AVAudioPlayer alloc];

            if ([audioPlayer initWithData:sound error:nil])
            {
                [audioPlayer prepareToPlay];
                [audioPlayer play];
            } else {
                [audioPlayer release];
                NSLog(@"Player couldn't load data");
            }   
        }
    }

    [unarchiver release];
}

所以,这就是我想要实现的......所以,我真正需要知道的是如何创建数据包,以便 peer2 可以播放音频。

这将是一种流媒体。是的,现在我不担心接收或播放数据包的顺序......我只需要对声音进行切片,他们就可以播放每个片段,每个片段,而无需等待整个文件由 peer2 接收。

谢谢!

4

3 回答 3

8

这是使用 AQ 播放文件的最简单类注意,您可以从任何点播放它(只需设置 currentPacketNumber)

#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>

@interface AudioFile : NSObject {
    AudioFileID                     fileID;     // the identifier for the audio file to play
    AudioStreamBasicDescription     format;
    UInt64                          packetsCount;           
    UInt32                          maxPacketSize;  
}

@property (readwrite)           AudioFileID                 fileID;
@property (readwrite)           UInt64                      packetsCount;
@property (readwrite)           UInt32                      maxPacketSize;

- (id) initWithURL: (CFURLRef) url;
- (AudioStreamBasicDescription *)audioFormatRef;

@end


//  AudioFile.m

#import "AudioFile.h"


@implementation AudioFile

@synthesize fileID;
@synthesize format;
@synthesize maxPacketSize;
@synthesize packetsCount;

- (id)initWithURL:(CFURLRef)url{
    if (self = [super init]){       
        AudioFileOpenURL(
                         url,
                         0x01, //fsRdPerm, read only
                         0, //no hint
                         &fileID
                         );

        UInt32 sizeOfPlaybackFormatASBDStruct = sizeof format;
        AudioFileGetProperty (
                              fileID, 
                              kAudioFilePropertyDataFormat,
                              &sizeOfPlaybackFormatASBDStruct,
                              &format
                              );

        UInt32 propertySize = sizeof (maxPacketSize);

        AudioFileGetProperty (
                              fileID, 
                              kAudioFilePropertyMaximumPacketSize,
                              &propertySize,
                              &maxPacketSize
                              );

        propertySize = sizeof(packetsCount);
        AudioFileGetProperty(fileID, kAudioFilePropertyAudioDataPacketCount, &propertySize, &packetsCount);
    }
    return self;
} 

-(AudioStreamBasicDescription *)audioFormatRef{
    return &format;
}

- (void) dealloc {
    AudioFileClose(fileID);
    [super dealloc];
}



//  AQPlayer.h

#import <Foundation/Foundation.h>
#import "AudioFile.h"

#define AUDIOBUFFERS_NUMBER     3
#define MAX_PACKET_COUNT    4096

@interface AQPlayer : NSObject {
@public
    AudioQueueRef                   queue;
    AudioQueueBufferRef             buffers[AUDIOBUFFERS_NUMBER];
    NSInteger                       bufferByteSize;
    AudioStreamPacketDescription    packetDescriptions[MAX_PACKET_COUNT];

    AudioFile * audioFile;
    SInt64  currentPacketNumber;
    UInt32  numPacketsToRead;
}

@property (nonatomic)               SInt64          currentPacketNumber;
@property (nonatomic, retain)       AudioFile       * audioFile;

-(id)initWithFile:(NSString *)file;
-(NSInteger)fillBuffer:(AudioQueueBufferRef)buffer;
-(void)play;

@end 

//  AQPlayer.m

#import "AQPlayer.h"

static void AQOutputCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
    AQPlayer * aqp = (AQPlayer *)inUserData;
    [aqp fillBuffer:(AudioQueueBufferRef)inBuffer];
}

@implementation AQPlayer

@synthesize currentPacketNumber;
@synthesize audioFile;

-(id)initWithFile:(NSString *)file{
    if ([self init]){
        audioFile = [[AudioFile alloc] initWithURL:[NSURL fileURLWithPath:file]];
        currentPacketNumber = 0;
        AudioQueueNewOutput ([audioFile audioFormatRef], AQOutputCallback, self, CFRunLoopGetCurrent (), kCFRunLoopCommonModes, 0, &queue);
        bufferByteSize = 4096;
        if (bufferByteSize < audioFile.maxPacketSize) bufferByteSize = audioFile.maxPacketSize; 
        numPacketsToRead = bufferByteSize/audioFile.maxPacketSize;
        for(int i=0; i<AUDIOBUFFERS_NUMBER; i++){
            AudioQueueAllocateBuffer (queue, bufferByteSize, &buffers[i]);
        }
    }
    return self;
}

-(void) dealloc{
    [audioFile release];
    if (queue){
        AudioQueueDispose(queue, YES);
        queue = nil;
    }
    [super dealloc];
}

- (void)play{
    for (int bufferIndex = 0; bufferIndex < AUDIOBUFFERS_NUMBER; ++bufferIndex){
        [self fillBuffer:buffers[bufferIndex]];
    }
    AudioQueueStart (queue, NULL);

}

-(NSInteger)fillBuffer:(AudioQueueBufferRef)buffer{
    UInt32 numBytes;
    UInt32 numPackets = numPacketsToRead;
    BOOL isVBR = [audioFile audioFormatRef]->mBytesPerPacket == 0 ? YES : NO;
    AudioFileReadPackets(
                         audioFile.fileID,
                         NO,
                         &numBytes,
                         isVBR ? packetDescriptions : 0,
                         currentPacketNumber,
                         &numPackets, 
                         buffer->mAudioData
                         );

    if (numPackets > 0) {
        buffer->mAudioDataByteSize = numBytes;      
        AudioQueueEnqueueBuffer (
                                 queue,
                                 buffer,
                                 isVBR ? numPackets : 0,
                                 isVBR ? packetDescriptions : 0
                                 );


    } 
    else{
        // end of present data, check if all packets are played
        // if yes, stop play and dispose queue
        // if no, pause queue till new data arrive then start it again
    }
    return  numPackets;
}
于 2010-03-25T10:49:26.070 回答
5

看来您正在解决错误的任务,因为AVAudioPlayer只能播放整个音频文件。您应该改用AudioToolbox框架中的音频队列服务,以逐个数据包的方式播放音频。事实上,您不需要将音频文件划分为真正的声音数据包,您可以使用任何数据块,就像上面您自己的示例一样,但是您应该使用音频文件服务音频文件流服务功能(来自AudioToolbox)读取接收到的数据块并将它们提供给音频队列缓冲区。

如果您仍然想将音频文件分成声音包,您可以使用 Audiofile Service 功能轻松完成。Audiofile 由标头和原始声音数据组成,其中存储了数据包数、采样率、通道数等属性。

使用AudioFileOpenURL打开音频文件并使用AudioFileGetProperty函数获取其所有属性。基本上你只需要 kAudioFilePropertyDataFormat 和 kAudioFilePropertyAudioDataPacketCount 属性:

AudioFileID  fileID;    // the identifier for the audio file
CFURLRef     fileURL = ...; // file URL
AudioStreamBasicDescription format; // structure containing audio header info
    UInt64  packetsCount;

AudioFileOpenURL(fileURL, 
    0x01, //fsRdPerm,                       // read only
    0, //no hint
    &fileID
);

UInt32 sizeOfPlaybackFormatASBDStruct = sizeof format;
AudioFileGetProperty (
    fileID, 
    kAudioFilePropertyDataFormat,
    &sizeOfPlaybackFormatASBDStruct,
    &format
);

propertySize = sizeof(packetsCount);
AudioFileGetProperty(fileID, kAudioFilePropertyAudioDataPacketCount, &propertySize, &packetsCount);

然后,您可以使用任何范围的音频包数据:

   OSStatus AudioFileReadPackets (
       AudioFileID                  inAudioFile,
       Boolean                      inUseCache,
       UInt32                       *outNumBytes,
       AudioStreamPacketDescription *outPacketDescriptions,
       SInt64                       inStartingPacket,
       UInt32                       *ioNumPackets,
       void                         *outBuffer
    );
于 2010-03-10T14:23:09.307 回答
0

Apple 已经编写了可以做到这一点的东西:AUNetSend 和 AUNetReceive。AUNetSend 是一个效果 AudioUnit,它将音频发送到另一台计算机上的 AUNetReceive AudioUnit。

不幸的是,这些 AU 在 iPhone 上不可用。

于 2010-03-11T07:51:53.597 回答