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

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

作者: InterestingPDD | 来源:发表于2018-04-17 14:45 被阅读276次

    由于项目需要 自己用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

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/hfaukftx.html