本文介绍iOS实时语音双向对讲(语音通话)功能:
(一)实时采集PCM并编码AAC
(二)RTSP+RTP协议实时传输
(三)FFmpeg实时解码AAC并播放PCM
第二篇介绍使用基于
CocoaAsyncSocket
的第三方开源库GCDAsyncSocket
进行RTSP协议的Socket网络通讯。
GitHub下载地址:https://github.com/robbiehanson/CocoaAsyncSocket
具体过程如下:
创建socket并建立一个连接
- (int)connectServer:(NSString *)hostIP port:(int)hostPort {
if (_socket == nil) {
_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *err = nil;
int t = [_socket connectToHost:hostIP onPort:hostPort error:&err];
if (!t) {
return 0;
}else{
return 1;
}
}else {
[_socket readDataWithTimeout:-1 tag:0];
return 1;
}
}
GCDAsyncSocketDelegate代理方法
//连接成功
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
BOOL state = [self.socket isConnected];
if (state) {
[self sendCmd];
}
}
//断开连接
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
BOOL state = [_socket isConnected];
NSLog(@"disconnect,state=%d",state);
self.socket = nil;
}
发送RTSP命令
- (void)sendCmd
{
[self doSetup:self.url];
}
- (void)doSetup:(NSString *)url {
NSMutableString *dataString = [NSMutableString string];
[dataString appendString:[NSString stringWithFormat:@"SETUP %@ RTSP/1.0\r\n", url]];
[dataString appendString:@"Content-Length: 0\r\n"];
[dataString appendFormat:@"CSeq: 0\r\n"];
[dataString appendString:@"Transport: RTP/AVP/DHTP;unicast\r\n"];
[dataString appendString:@"\r\n"];
NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
[self.socket writeData:data withTimeout:-1 tag:0];
[self.socket readDataWithTimeout:-1 tag:0];
}
- (void)doPlay:(NSString *)url {
NSMutableString *dataString = [NSMutableString string];
[dataString appendString:[NSString stringWithFormat:@"PLAY %@ RTSP/1.0\r\n", url]];
[dataString appendString:@"Content-Length: 0\r\n"];
[dataString appendFormat:@"CSeq: 1\r\n"];
[dataString appendString:@"\r\n"];
NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
[self.socket writeData:data withTimeout:-1 tag:1];
[self.socket readDataWithTimeout:-1 tag:1];
}
- (void)doTeardown:(NSString *)url {
NSMutableString *dataString = [NSMutableString string];
[dataString appendString:[NSString stringWithFormat:@"TEARDOWN %@ RTSP/1.0\r\n", url]];
[dataString appendString:@"Content-Length: 0\r\n"];
[dataString appendString:@"CSeq: 2\r\n"];
[dataString appendString:@"\r\n"];
NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
[self.socket writeData:data withTimeout:-1 tag:2];
}
读取数据
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
switch (tag) {
case 0:
[self doPlay:self.url];
break;
case 1:
[self startCapture];
break;
case 200:
if (!dataString) {
[self getPayload:data];
}
break;
default:
break;
}
[sock readDataWithTimeout:-1 tag:200];
}
PS:标准的RTSP协议DESCRIBE、OPTIONS、SETUP、PLAY、TEARDOWN等方法一般都需要发送,作者这里为非标准RTSP,只需发送SETUP、PLAY、TEARDOWN。可参考RTSP百科:RTSP
其中readDataWithTimeout
表示需要读取发送后返回的数据,-1表示不会使用超时。
以上,则实现了RTSP的通讯,可将编码后的AAC以RTP的形式进行传输,且可以一边发送一边读取。
Demo地址:https://github.com/XuningZhai/TalkDemo
支持G711的Demo地址:https://github.com/XuningZhai/TalkDemo_G711_AAC