本文介紹iOS實(shí)時(shí)語(yǔ)音雙向?qū)χv(語(yǔ)音通話)功能:
(一)實(shí)時(shí)采集PCM并編碼AAC
(二)RTSP+RTP協(xié)議實(shí)時(shí)傳輸
(三)FFmpeg實(shí)時(shí)解碼AAC并播放PCM
第二篇介紹使用基于
CocoaAsyncSocket
的第三方開(kāi)源庫(kù)GCDAsyncSocket
進(jìn)行RTSP協(xié)議的Socket網(wǎng)絡(luò)通訊。
GitHub下載地址:https://github.com/robbiehanson/CocoaAsyncSocket
具體過(guò)程如下:
創(chuàng)建socket并建立一個(gè)連接
- (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];
}
}
//斷開(kāi)連接
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
BOOL state = [_socket isConnected];
NSLog(@"disconnect,state=%d",state);
self.socket = nil;
}
發(fā)送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];
}
讀取數(shù)據(jù)
- (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:標(biāo)準(zhǔn)的RTSP協(xié)議DESCRIBE结执、OPTIONS、SETUP、PLAY、TEARDOWN等方法一般都需要發(fā)送闸英,作者這里為非標(biāo)準(zhǔn)RTSP枚驻,只需發(fā)送SETUP、PLAY蒿辙、TEARDOWN”醢停可參考RTSP百科:RTSP
其中readDataWithTimeout
表示需要讀取發(fā)送后返回的數(shù)據(jù)思灌,-1表示不會(huì)使用超時(shí)。
以上恭取,則實(shí)現(xiàn)了RTSP的通訊泰偿,可將編碼后的AAC以RTP的形式進(jìn)行傳輸,且可以一邊發(fā)送一邊讀取蜈垮。
Demo地址:https://github.com/XuningZhai/TalkDemo
支持G711的Demo地址:https://github.com/XuningZhai/TalkDemo_G711_AAC