我正在尝试播放来自IP摄像机的音频流,它是API http://...<ip-camera..>/audio.cgi
目前我通过调整MJPEG客户端来播放流音频,获取音频块并将它们放入连续声音包的缓冲区(NSMutableArray
)。这里有一些接收数据的代码:
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(@"Data recv.");
NSLog(@"Data lenght:%i",[data length]);
if ([data length]>AUDIO_STREAM_HEADER_SIZE){
[recvData appendData:data]; //audio stream data - always 1024 bytes
NSLog(@"Data Append lenght:%i",[recvData length]);
}
else {
[_encabezado appendData:data]; //audio stream header - always 44 bytes
NSLog(@"Crea encabezado:%@",_encabezado);
NSLog(@"Largo encabezado:%i",[_encabezado length]);
}
if ([recvData length] >= PACKET_SIZE_BUFFER_SONIDO) //PACKET_SIZE_BUFFER_SONIDO = 81920
{
NSMutableData *soundData = [[NSMutableData alloc] initWithCapacity:([recvData length])];
NSMutableData *soundCut = [[NSMutableData alloc] initWithData:recvData];
[soundData appendData:_encabezado]; //audio stream header
[soundData appendData:soundCut]; //audio stream data
[_arrSonidos addObject:soundData];
if ([_arrSonidos count]>(BUFFER_SIZE_FOR_PLAY-1)) {
if (playerBuffer.isPlaying) {
[playerBuffer AgregaDataSound:soundData]; //addDataSound in Buffer
} else {
playerBuffer = [[SoundDataPLayer alloc]initWithFileNameQueue:_arrSonidos];
}
}
[recvData setLength:0];
[soundData release];
}
}
每次调用该函数(函数显示)时,数据字段的标头将获得(44字节),然后每次调用一次数据字节(1024字节)。我所做的是保持头部接收数据NSData
,然后接收数据直到定义的数据包大小,将包保持在缓冲区中各自的头数据。所以我迭代地将包添加到缓冲区。
另一方面,我创建了一个由上面的代码调用的“playerBuffer”(受网络上的例子启发),并且还负责控制缓冲区并播放缓冲区的每个数据包。此对象基于AVAudioPlayer
:
SoundDataPLayer.h
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@interface SoundDataPLayer : NSObject <AVAudioPlayerDelegate> {
AVAudioPlayer* myplayer;
NSMutableArray* dataSound;
int index;
}
@property (nonatomic, retain) AVAudioPlayer* myplayer;
@property (nonatomic, retain) NSMutableArray* dataSound;
- (id)initWithFileNameQueue:(NSArray*)datas;
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag;
- (void)playX:(int)i;
- (void)stop;
- (BOOL)isPlaying;
- (void)AgregaDataSound:(NSData *)data;
@end
SoundDataPLayer.m
#import "SoundDataPLayer.h"
#import "ExtAudioFileConvertUtil.h"
@implementation SoundDataPLayer
@synthesize myplayer;
@synthesize dataSound;
- (id)initWithFileNameQueue:(NSArray*)datas {
if ((self = [super init])) {
self.dataSound = [[NSMutableArray alloc]initWithArray:datas];
index = 0;
[self playX:index];
}
return self;
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
if (index < dataSound.count) {
[self playX:index];
} else {
//reached end of queue
}
}
- (void)playX:(int)i {
self.myplayer = [[AVAudioPlayer alloc] initWithData:[dataSound objectAtIndex:i] error:nil];
myplayer.delegate = self;
[myplayer prepareToPlay];
[myplayer play];
index++;
}
- (void)stop {
if (self.myplayer.playing) [myplayer stop];
}
- (BOOL)isPlaying{
return self.myplayer.playing;
}
- (void)AgregaDataSound:(NSData *)data {
[dataSound addObject:data]; //Add Sound Data to the buffer
}
@end
这就是工作和播放循环音频,但是当AVAudioPlayer
返回播放新数据包(NSData
)的缓冲区时,它会在每个循环中听到每个数据包之间发生恼人的故障。
我尝试使用SystemSound
并将声音包缓冲到数据文件中,以查看包含毛刺的数据,但显然毛刺是在数据的末尾。
我在一个论坛中发现了一篇似乎可以解释会发生什么的文章:here但显然在我的情况下没有完全解释因为是x-wav音频和PCM编解码器,所以我认为并理解是未压缩的。虽然我不知道如何解压缩来自NSData
数据包的音频。
来自IP摄像机的流音频的其他信息:
datos del audio en el header:
“Cache-Control”=“no-cache”; “Content-Type”=“audio / x-wav”;日期= “Tue Jan 5 01:17:04 2010”; Pragma =“no-cache”;服务器= “的GoAhead-腹板”;
Datos adicionales del audio Codec:PCM SampleRate:16 kHz
如果有人能给我一个如何解决这个问题的话,我将不胜感激......我试着解决它很多个小时......让我疯狂!
提前致谢,抱歉我的英语不好。
答案 0 :(得分:1)
您听到的“故障”是您接收“完成播放”事件所需的时间,处理它,为下一个数据包初始化AVAudioPlayer以及将数据发送到声卡的组合
这类似于等待一个CD播放器在开始播放之前完成播放 - 对于无故障播放,不可能做出足够快的反应。
您可能想要使用音频队列服务,并且您还需要一些方法来补偿相机和手机之间的音频时钟偏差。
答案 1 :(得分:1)
我用AVQueuePlayer解决了我的问题。
我希望它有所帮助。