iOS 播放pcm文件 播放udp数据包传输过来的pcmData

2018-04-17  本文已影响276人  InterestingPDD

由于项目需要 自己用socket 加udp做一个小区物业和业主的视频通话功能。我们需要采集麦克风声音传输出去和播放接收到的udp传过来的pcm原始音频文件。因为我们这个功能很简单,只需要通话几秒钟  最多20秒。就没有做音频编码和解码的操作,直接传输的pcm原始数据。

但是我在网上找了很多播放pcm文件的代码,很少找到即时播放传过来的udp包的,一般都是播放本地的pcm或者解码过来的稳定的数据流。不能满足我udp经常丢包 导致播放停止的问题。

结合了很多别人的播放器和数据包的处理 自己改了一下 这个播放器能满足我的需要了 希望能帮到有同样需求的人。


AudioQueuePlay.h

//

//  AudioQueuePlay.h

//  udp

//

//  Created by 唐超 on 3/19/18.

//  Copyright © 2018 唐超. All rights reserved.

//

#import

#import

@interfaceAudioQueuePlay :NSObject

/**

 播放音频pcm数据 udp包传过来的

 @paramdata pcm data

 */

- (void)playWithData: (NSData*)data;

/**

 当播放不出声音的时候 重启一下播放器

 */

- (void)resetPlay;

/**

 开始播放

 */

- (void)startPlay;

/**

 停止播放

 */

- (void)stopPlay;

@end

我们的需求是通过socket 传输udp语音包(pcm)和图片实现视频通话 但由于udp的不可靠性 网速慢的时候 播放声音有问题 在网上找了好久的资料 终于找到能解决这个问题的代码  下面是我在网上看到的有意义的资料

 码农人生博客 比较详细了 网易云音乐的大神

http://msching.github.io/blog/categories/audio/

 从这里我找到了怎么填充空数据包的方法  赞

 https://my.oschina.net/xikan/blog/483929

 这个就是我现在的播放器来源 感谢  结合第二个加空数据包的 就是我完整的例子啦

https://segmentfault.com/a/1190000010177336

#import "AudioQueuePlay.h"

/*我们的需求是通过socket 传输udp语音包(pcm)和图片实现视频通话 但由于udp的不可靠性 网速慢的时候 播放声音有问题 在网上找了好久的资料 终于找到能解决这个问题的代码  下面是我在网上看到的有意义的资料

 码农人生博客 比较详细了 网易云音乐的大神

 http://msching.github.io/blog/categories/audio/

 从这里我找到了怎么填充空数据包的方法  赞

 https://my.oschina.net/xikan/blog/483929

 这个就是我现在的播放器来源 感谢  结合第二个加空数据包的 就是我完整的例子啦

 https://segmentfault.com/a/1190000010177336

 */

#define MIN_SIZE_PER_FRAME5000//缓冲区大小  如果一次性数据超出 会内存溢出崩溃

#define QUEUE_BUFFER_SIZE3      //队列缓冲个数

@interface AudioQueuePlay() {

    AudioQueueRefaudioQueue;                                //音频播放队列

    AudioStreamBasicDescription _audioDescription;

    AudioQueueBufferRefaudioQueueBuffers[QUEUE_BUFFER_SIZE];//音频缓存

    BOOLaudioQueueBufferUsed[QUEUE_BUFFER_SIZE];            //判断音频缓存是否在使用

    NSLock*sysnLock;

    NSMutableData*tempData;

    OSStatusosState;

}

@property (nonatomic, assign) BOOL isRuning;

@end

@implementation AudioQueuePlay

- (instancetype)init

{

    self= [superinit];

    if(self) {

        sysnLock= [[NSLockalloc]init];

        // 播放PCM使用

        //设置音频参数 这些参数根据传过来的音频参数自己设置 如果有偏差 可能会出现杂音或者卡顿的情况

        _audioDescription.mSampleRate = 22050;//采样率

        _audioDescription.mFormatID = kAudioFormatLinearPCM;

        // 下面这个是保存音频数据的方式的说明,如可以根据大端字节序或小端字节序,浮点数或整数以及不同体位去保存数据

        _audioDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;

        //1单声道 2双声道

        _audioDescription.mChannelsPerFrame = 1;

        //每一个packet一侦数据,每个数据包下的桢数,即每个数据包里面有多少桢

        _audioDescription.mFramesPerPacket = 1;

        //每个采样点16bit量化 语音每采样点占用位数

        _audioDescription.mBitsPerChannel = 16;

        _audioDescription.mBytesPerFrame = (_audioDescription.mBitsPerChannel / 8) * _audioDescription.mChannelsPerFrame;

        //每个数据包的bytes总数,每桢的bytes数*每个数据包的桢数

        _audioDescription.mBytesPerPacket = _audioDescription.mBytesPerFrame * _audioDescription.mFramesPerPacket;

        // 使用player的内部线程播放 新建输出

        AudioQueueNewOutput(&_audioDescription,AudioPlayerAQInputCallback, (__bridgevoid*_Nullable)(self),nil,0,0, &audioQueue);

        // 设置音量

        AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1.0);

        // 初始化需要的缓冲区

        for(inti =0; i

            audioQueueBufferUsed[i] = false;

            osState = AudioQueueAllocateBuffer(audioQueue, MIN_SIZE_PER_FRAME, &audioQueueBuffers[i]);

        }

    }

    return self;

}

//添加静音包  当来源音频数据不足的时候 往里面添加静音包

- (void)fillNullData{

            if(_isRuning) {

                BOOLisNull =YES;

                for(inti =0; i<3; i++) {

                   BOOLused =audioQueueBufferUsed[i];

                    if(used) {

                        isNull =NO;

                    }

                }

                if(isNull) {

                    //填空数据包

//                    NSLog(@"填空数据包");

                    NSMutableData*tmpData = [[NSMutableDataalloc]init];

                    for(inti=0; i<600; i++) {

                        [tmpDataappendBytes:"\x00"length:1];

                    }

                    [selfplayWithData:tmpData];

                }

            }

}

- (void)startPlay{

    [self resetPlay];

}

- (void)stopPlay{

    _isRuning = NO;

    if (audioQueue) {

        AudioQueueStop(audioQueue,true);

    }

}

- (void)resetPlay {

    _isRuning = NO;

    if(audioQueue!=nil) {

        AudioQueueReset(audioQueue);

        //延迟执行

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

            osState=AudioQueueStart(audioQueue,NULL);

            if(osState!=noErr) {

                NSLog(@"AudioQueueStart Error");

            }

            _isRuning=YES;

        });

    }

}

// 播放相关

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

    if(!_isRuning) {

        return;

    }

    [sysnLock lock];

    tempData = [NSMutableData new];

    [tempDataappendData:data];

    // 得到数据

    NSUInteger len = tempData.length;

    Byte*bytes = (Byte*)malloc(len);

    [tempDatagetBytes:byteslength: len];

    inti =0;

    while (true) {

        if (!audioQueueBufferUsed[i]) {

            audioQueueBufferUsed[i] = true;

            break;

        }else{

            i++;

            if(i >=QUEUE_BUFFER_SIZE) {

                i =0;

            }

        }

    }

    AudioQueueBufferRef buffer = audioQueueBuffers[i];

    buffer ->mAudioDataByteSize=  (unsignedint)len;

    // 把bytes的头地址开始的len字节给mAudioData

    memcpy(audioQueueBuffers[i] ->mAudioData, bytes, len);

    free(bytes);

    AudioQueueEnqueueBuffer(audioQueue, audioQueueBuffers[i], 0, NULL);

    printf("本次播放数据大小: %lu \n", len);

    [sysnLock unlock];

}

// 回调回来把buffer状态设为未使用

staticvoidAudioPlayerAQInputCallback(void* inUserData,AudioQueueRefaudioQueueRef,AudioQueueBufferRefaudioQueueBufferRef) {

    AudioQueuePlay* player = (__bridgeAudioQueuePlay*)inUserData;

    [playerresetBufferState:audioQueueRefand:audioQueueBufferRef];

//    NSLog(@"AudioPlayerAQInputCallback");

}

- (void)resetBufferState:(AudioQueueRef)audioQueueRef and:(AudioQueueBufferRef)audioQueueBufferRef {

    for(inti =0; i

        // 将这个buffer设为未使用

        if(audioQueueBufferRef ==audioQueueBuffers[i]) {

            audioQueueBufferUsed[i] = false;

        }

    }

    [self fillNullData];

}

// ************************** 内存回收 **********************************

- (void)dealloc {

    if(audioQueue!=nil) {

        AudioQueueStop(audioQueue,true);

    }

    audioQueue = nil;

    sysnLock = nil;

}

@end

上一篇下一篇

猜你喜欢

热点阅读