如何使用 AudioUnit 播放来自服务器的音频流?
How do I use the AudioUnit to play the audio stream from server?
- (void)openPlayThreadWithRtmpURL:(NSString *)rtmpURL {
spx_int16_t *input_buffer;
do {
if (self.rtmpDelegate) {
[self.rtmpDelegate evenCallbackWithEvent:2000];
}
//init speex decoder and config;
speex_bits_init(&dbits);
dec_state = speex_decoder_init(&speex_wb_mode);
speex_decoder_ctl(dec_state, SPEEX_GET_FRAME_SIZE, &dec_frame_size);
input_buffer = malloc(dec_frame_size * sizeof(short));
NSLog(@"Init Speex decoder success frame_size = %d",dec_frame_size);
//init rtmp
pPlayRtmp = RTMP_Alloc();
RTMP_Init(pPlayRtmp);
NSLog(@"Play RTMP_Init %@\n", rtmpURL);
if (!RTMP_SetupURL(pPlayRtmp, (char*)[rtmpURL UTF8String])) {
NSLog(@"Play RTMP_SetupURL error\n");
if(self.rtmpDelegate) {
[self.rtmpDelegate evenCallbackWithEvent:2002];
}
break;
}
if (!RTMP_Connect(pPlayRtmp, NULL) || !RTMP_ConnectStream(pPlayRtmp, 0)) {
NSLog(@"Play RTMP_Connect or RTMP_ConnectStream error\n");
if(self.rtmpDelegate) {
[self.rtmpDelegate evenCallbackWithEvent:2002];
}
break;
}
if(self.rtmpDelegate) {
[self.rtmpDelegate evenCallbackWithEvent:2001];
}
NSLog(@"Player RTMP_Connected \n");
RTMPPacket rtmp_pakt = {0};
isStartPlay = YES;
while (isStartPlay && RTMP_ReadPacket(pPlayRtmp, &rtmp_pakt)) {
if (RTMPPacket_IsReady(&rtmp_pakt)) {
if (!rtmp_pakt.m_nBodySize) {
continue;
}
if (rtmp_pakt.m_packetType == RTMP_PACKET_TYPE_AUDIO) {
NSLog(@"Audio size = %d head = %d time = %d", rtmp_pakt.m_nBodySize, rtmp_pakt.m_body[0], rtmp_pakt.m_nTimeStamp);
speex_bits_read_from(&dbits, rtmp_pakt.m_body + 1, rtmp_pakt.m_nBodySize - 1);
speex_decode_int(dec_state, &dbits, input_buffer); //audioData in the input_buffer
//do something...
} else if (rtmp_pakt.m_packetType == RTMP_PACKET_TYPE_VIDEO) {
// 处理视频数据包
} else if (rtmp_pakt.m_packetType == RTMP_PACKET_TYPE_INVOKE) {
// 处理invoke包
NSLog(@"RTMP_PACKET_TYPE_INVOKE");
RTMP_ClientPacket(pPlayRtmp,&rtmp_pakt);
} else if (rtmp_pakt.m_packetType == RTMP_PACKET_TYPE_INFO) {
// 处理信息包
//NSLog(@"RTMP_PACKET_TYPE_INFO");
} else if (rtmp_pakt.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO) {
// 其他数据
int index = 0;
while (1) {
int StreamType; //1-byte
int MediaSize; //3-byte
int TiMMER; //3-byte
int Reserve; //4-byte
char* MediaData; //MediaSize-byte
int TagLen; //4-byte
StreamType = rtmp_pakt.m_body[index];
index += 1;
MediaSize = bigThreeByteToInt(rtmp_pakt.m_body + index);
index += 3;
TiMMER = bigThreeByteToInt(rtmp_pakt.m_body + index);
index += 3;
Reserve = bigFourByteToInt(rtmp_pakt.m_body + index);
index += 4;
MediaData = rtmp_pakt.m_body + index;
index += MediaSize;
TagLen = bigFourByteToInt(rtmp_pakt.m_body + index);
index += 4;
//NSLog(@"bodySize:%d index:%d",rtmp_pakt.m_nBodySize,index);
//LOGI("StreamType:%d MediaSize:%d TiMMER:%d TagLen:%d\n", StreamType, MediaSize, TiMMER, TagLen);
if (StreamType == 0x08) {
//音频包
//int MediaSize = bigThreeByteToInt(rtmp_pakt.m_body+1);
// LOGI("FLASH audio size:%d head:%d time:%d\n", MediaSize, MediaData[0], TiMMER);
speex_bits_read_from(&dbits, MediaData + 1, MediaSize - 1);
speex_decode_int(dec_state, &dbits, input_buffer);
//[mAudioPlayer putAudioData:input_buffer];
// putAudioQueue(output_buffer,dec_frame_size);
} else if (StreamType == 0x09) {
//视频包
// LOGI( "video size:%d head:%d\n", MediaSize, MediaData[0]);
}
if (rtmp_pakt.m_nBodySize == index) {
break;
}
}
}
RTMPPacket_Free(&rtmp_pakt);
}
}
if (isStartPlay) {
if(self.rtmpDelegate) {
[self.rtmpDelegate evenCallbackWithEvent:2005];
}
isStartPlay = NO;
}
} while (0);
[mAudioPlayer stopPlay];
if (self.rtmpDelegate) {
[self.rtmpDelegate evenCallbackWithEvent:2004];
}
if (RTMP_IsConnected(pPlayRtmp)) {
RTMP_Close(pPlayRtmp);
}
RTMP_Free(pPlayRtmp);
free(input_buffer);
speex_bits_destroy(&dbits);
speex_decoder_destroy(dec_state);
}
这是我的自定义方法。 rtmpURL是一个NSString的对象,它是一个流服务器地址。使用这种方法,我可以从服务器获取音频流的编码,之后,我使用speex解码器解码我得到的数据,就像这样:
//init speex decoder and config;
speex_bits_init(&dbits);
dec_state = speex_decoder_init(&speex_wb_mode);
speex_decoder_ctl(dec_state, SPEEX_GET_FRAME_SIZE, &dec_frame_size);
input_buffer = malloc(dec_frame_size * sizeof(short));
NSLog(@"Init Speex decoder success frame_size = %d",dec_frame_size);
if (rtmp_pakt.m_packetType == RTMP_PACKET_TYPE_AUDIO) {
NSLog(@"Audio size = %d head = %d time = %d", rtmp_pakt.m_nBodySize, rtmp_pakt.m_body[0], rtmp_pakt.m_nTimeStamp);
speex_bits_read_from(&dbits, rtmp_pakt.m_body + 1, rtmp_pakt.m_nBodySize - 1);
speex_decode_int(dec_state, &dbits, input_buffer); //audioData in the input_buffer
//do something...
}
现在,解码后的音频数据存储在input_buffer中,这是我的困惑。如何使用AudioUnit播放音频data.And这是我的播放回调函数:
OSStatus playCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData){
AudioPlayer *THIS = (__bridge AudioPlayer *)inRefCon;
//How do I use the AudioUnit to play the audio stream from server?
return noErr;
}
希望有朋友解决我的困惑,如果你用过audioUnit,非常感谢!
这里有一些非常好的资源link
在您的 playCallback 中,您需要将音频复制到缓冲区 ioData
。
例如
memcpy (ioData->mBuffers[0].mData, input_buffer + offset, numBytes );
// increase offset based on how many frames it requests.
输入变量inNumberFrames
是准备好的帧数。这可能少于 input_buffer 中的帧数。所以你需要跟踪你的游戏位置。
我不知道你的音频流基本描述中指定的音频格式。您需要计算需要复制多少字节,考虑 mono/stereo、每个通道的字节数,当然还有 inNumberFrames
。
- (void)openPlayThreadWithRtmpURL:(NSString *)rtmpURL {
spx_int16_t *input_buffer;
do {
if (self.rtmpDelegate) {
[self.rtmpDelegate evenCallbackWithEvent:2000];
}
//init speex decoder and config;
speex_bits_init(&dbits);
dec_state = speex_decoder_init(&speex_wb_mode);
speex_decoder_ctl(dec_state, SPEEX_GET_FRAME_SIZE, &dec_frame_size);
input_buffer = malloc(dec_frame_size * sizeof(short));
NSLog(@"Init Speex decoder success frame_size = %d",dec_frame_size);
//init rtmp
pPlayRtmp = RTMP_Alloc();
RTMP_Init(pPlayRtmp);
NSLog(@"Play RTMP_Init %@\n", rtmpURL);
if (!RTMP_SetupURL(pPlayRtmp, (char*)[rtmpURL UTF8String])) {
NSLog(@"Play RTMP_SetupURL error\n");
if(self.rtmpDelegate) {
[self.rtmpDelegate evenCallbackWithEvent:2002];
}
break;
}
if (!RTMP_Connect(pPlayRtmp, NULL) || !RTMP_ConnectStream(pPlayRtmp, 0)) {
NSLog(@"Play RTMP_Connect or RTMP_ConnectStream error\n");
if(self.rtmpDelegate) {
[self.rtmpDelegate evenCallbackWithEvent:2002];
}
break;
}
if(self.rtmpDelegate) {
[self.rtmpDelegate evenCallbackWithEvent:2001];
}
NSLog(@"Player RTMP_Connected \n");
RTMPPacket rtmp_pakt = {0};
isStartPlay = YES;
while (isStartPlay && RTMP_ReadPacket(pPlayRtmp, &rtmp_pakt)) {
if (RTMPPacket_IsReady(&rtmp_pakt)) {
if (!rtmp_pakt.m_nBodySize) {
continue;
}
if (rtmp_pakt.m_packetType == RTMP_PACKET_TYPE_AUDIO) {
NSLog(@"Audio size = %d head = %d time = %d", rtmp_pakt.m_nBodySize, rtmp_pakt.m_body[0], rtmp_pakt.m_nTimeStamp);
speex_bits_read_from(&dbits, rtmp_pakt.m_body + 1, rtmp_pakt.m_nBodySize - 1);
speex_decode_int(dec_state, &dbits, input_buffer); //audioData in the input_buffer
//do something...
} else if (rtmp_pakt.m_packetType == RTMP_PACKET_TYPE_VIDEO) {
// 处理视频数据包
} else if (rtmp_pakt.m_packetType == RTMP_PACKET_TYPE_INVOKE) {
// 处理invoke包
NSLog(@"RTMP_PACKET_TYPE_INVOKE");
RTMP_ClientPacket(pPlayRtmp,&rtmp_pakt);
} else if (rtmp_pakt.m_packetType == RTMP_PACKET_TYPE_INFO) {
// 处理信息包
//NSLog(@"RTMP_PACKET_TYPE_INFO");
} else if (rtmp_pakt.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO) {
// 其他数据
int index = 0;
while (1) {
int StreamType; //1-byte
int MediaSize; //3-byte
int TiMMER; //3-byte
int Reserve; //4-byte
char* MediaData; //MediaSize-byte
int TagLen; //4-byte
StreamType = rtmp_pakt.m_body[index];
index += 1;
MediaSize = bigThreeByteToInt(rtmp_pakt.m_body + index);
index += 3;
TiMMER = bigThreeByteToInt(rtmp_pakt.m_body + index);
index += 3;
Reserve = bigFourByteToInt(rtmp_pakt.m_body + index);
index += 4;
MediaData = rtmp_pakt.m_body + index;
index += MediaSize;
TagLen = bigFourByteToInt(rtmp_pakt.m_body + index);
index += 4;
//NSLog(@"bodySize:%d index:%d",rtmp_pakt.m_nBodySize,index);
//LOGI("StreamType:%d MediaSize:%d TiMMER:%d TagLen:%d\n", StreamType, MediaSize, TiMMER, TagLen);
if (StreamType == 0x08) {
//音频包
//int MediaSize = bigThreeByteToInt(rtmp_pakt.m_body+1);
// LOGI("FLASH audio size:%d head:%d time:%d\n", MediaSize, MediaData[0], TiMMER);
speex_bits_read_from(&dbits, MediaData + 1, MediaSize - 1);
speex_decode_int(dec_state, &dbits, input_buffer);
//[mAudioPlayer putAudioData:input_buffer];
// putAudioQueue(output_buffer,dec_frame_size);
} else if (StreamType == 0x09) {
//视频包
// LOGI( "video size:%d head:%d\n", MediaSize, MediaData[0]);
}
if (rtmp_pakt.m_nBodySize == index) {
break;
}
}
}
RTMPPacket_Free(&rtmp_pakt);
}
}
if (isStartPlay) {
if(self.rtmpDelegate) {
[self.rtmpDelegate evenCallbackWithEvent:2005];
}
isStartPlay = NO;
}
} while (0);
[mAudioPlayer stopPlay];
if (self.rtmpDelegate) {
[self.rtmpDelegate evenCallbackWithEvent:2004];
}
if (RTMP_IsConnected(pPlayRtmp)) {
RTMP_Close(pPlayRtmp);
}
RTMP_Free(pPlayRtmp);
free(input_buffer);
speex_bits_destroy(&dbits);
speex_decoder_destroy(dec_state);
}
这是我的自定义方法。 rtmpURL是一个NSString的对象,它是一个流服务器地址。使用这种方法,我可以从服务器获取音频流的编码,之后,我使用speex解码器解码我得到的数据,就像这样:
//init speex decoder and config;
speex_bits_init(&dbits);
dec_state = speex_decoder_init(&speex_wb_mode);
speex_decoder_ctl(dec_state, SPEEX_GET_FRAME_SIZE, &dec_frame_size);
input_buffer = malloc(dec_frame_size * sizeof(short));
NSLog(@"Init Speex decoder success frame_size = %d",dec_frame_size);
if (rtmp_pakt.m_packetType == RTMP_PACKET_TYPE_AUDIO) {
NSLog(@"Audio size = %d head = %d time = %d", rtmp_pakt.m_nBodySize, rtmp_pakt.m_body[0], rtmp_pakt.m_nTimeStamp);
speex_bits_read_from(&dbits, rtmp_pakt.m_body + 1, rtmp_pakt.m_nBodySize - 1);
speex_decode_int(dec_state, &dbits, input_buffer); //audioData in the input_buffer
//do something...
}
现在,解码后的音频数据存储在input_buffer中,这是我的困惑。如何使用AudioUnit播放音频data.And这是我的播放回调函数:
OSStatus playCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData){
AudioPlayer *THIS = (__bridge AudioPlayer *)inRefCon;
//How do I use the AudioUnit to play the audio stream from server?
return noErr;
}
希望有朋友解决我的困惑,如果你用过audioUnit,非常感谢!
这里有一些非常好的资源link
在您的 playCallback 中,您需要将音频复制到缓冲区 ioData
。
例如
memcpy (ioData->mBuffers[0].mData, input_buffer + offset, numBytes );
// increase offset based on how many frames it requests.
输入变量inNumberFrames
是准备好的帧数。这可能少于 input_buffer 中的帧数。所以你需要跟踪你的游戏位置。
我不知道你的音频流基本描述中指定的音频格式。您需要计算需要复制多少字节,考虑 mono/stereo、每个通道的字节数,当然还有 inNumberFrames
。