美文网首页
iOS自定义音视频采集问题汇总

iOS自定义音视频采集问题汇总

作者: jedrek | 来源:发表于2018-12-10 10:51 被阅读0次

公司的直播项目一直采用的网易云的直播SDK,后来产品需求需要我们使用手机端作为音视频采集设备,使用电视端作为传输与显示媒介,把视频数据展示在电视和Windows端,音频只展示Windows端,这样一个需求,这样我这边需要做到的:
1.音视频采集已经考虑的传输数据大小问题需要对音视频进行压缩编码
2.数据传输,因为电视和手机在同一个局域网所以,我们使用socket进行数据传输
以上两点看似简单其实里面内容挺多,各种问题的出现让我在这个项目中跌了很多跟头,对于遇到的问题做以下记录

音视频采集

iOS这边处理音视频采集并不难,网上教程一大把,特别是视频采集很简单,只需有一点要注意就是像素的大小设置,苹果有两种方案:一种是你自定义画布大小,第二个你使用硬件支持的大小

自定义大小
pixW = 320;pixH = 240; //画布宽高自定义
self.captureSession.sessionPreset = AVCaptureSessionPresetHigh; //画质 采用最高 数据也比较大
使用硬件支持

这个需要你使用系统像素采集的宽高

if ([self.captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) { //判断当前采集的设备 是否支持 这个像素
 self.captureSession.sessionPreset = AVCaptureSessionPreset1280x720;

音频这边的就有多个选择, 比如:AVCaptureDevice, AudioQueue以及Audio Unit。其中 Audio Unit是最底层的接口,它的优点是功能强大,延迟低; 而缺点是学习成本高,难度大。对于一般的iOS应用程序,AVCaptureDevice和AudioQueue完全够用了。但对于音视频直播,最好还是使用 Audio Unit 进行处理,这样可以达到最佳的效果。
因为我这边做的是直播的所以为了追求低延迟,最好的效果,就采用了Audio Unit进行处理,因为都是C语言的代码而且还要和OC进行结合所以学习起来确实很费劲 所以对里面的关键代码做了注释

 // AVAudioSession的接口比较简单。APP启动的时候会自动帮激活AVAudioSession,我们可以手动 设置 开始的时候b可以播放也可以录音
AVAudioSession* session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:&error];
[session setActive:YES error:nil];   
//启用录音功能
UInt32 inputEnableFlag = 1;
CheckError(AudioUnitSetProperty(_remoteIOUnit,
                                kAudioOutputUnitProperty_EnableIO,
                                kAudioUnitScope_Input,
                                1,
                                &inputEnableFlag,
                                sizeof(inputEnableFlag)),
           "Open input of bus 1 failed");
//    Open output of bus 0(output speaker)//禁用播放功能
UInt32 outputEnableFlag = 1;
CheckError(AudioUnitSetProperty(_remoteIOUnit,
                                kAudioOutputUnitProperty_EnableIO,
                                kAudioUnitScope_Output,
                                0,
                                &outputEnableFlag,
                                sizeof(outputEnableFlag)),
           "Open output of bus 0 failed");
//Set up stream format for input and output
/**
 AudioStreamBasicDescription:
 mSampleRate;       采样率, eg. 44100
 mFormatID;         格式, eg. kAudioFormatLinearPCM
 mFormatFlags;      标签格式, eg. kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked
 mBytesPerPacket;   每个Packet的Bytes数量, eg. 2
 mFramesPerPacket;  每个Packet的帧数量, eg. 1
 mBytesPerFrame;    (mBitsPerChannel / 8 * mChannelsPerFrame) 每帧的Byte数, eg. 2
 mChannelsPerFrame; 1:单声道;2:立体声, eg. 1
 mBitsPerChannel;   语音每采样点占用位数[8/16/24/32], eg. 16
 mReserved;         保留
 */
_streamFormat.mFormatID = kAudioFormatLinearPCM;
_streamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
_streamFormat.mSampleRate = kRate;
_streamFormat.mFramesPerPacket = 1;
_streamFormat.mBytesPerFrame = 2;
_streamFormat.mBytesPerPacket = 2;
_streamFormat.mBitsPerChannel = kBits;
_streamFormat.mChannelsPerFrame = kChannels;

CheckError(AudioUnitSetProperty(_remoteIOUnit,
                                kAudioUnitProperty_StreamFormat,
                                kAudioUnitScope_Input,
                                0,
                                &_streamFormat,
                                sizeof(_streamFormat)),
           "kAudioUnitProperty_StreamFormat of bus 0 failed");
CheckError(AudioUnitSetProperty(_remoteIOUnit,
                                kAudioUnitProperty_StreamFormat,
                                kAudioUnitScope_Output,
                                1,
                                &_streamFormat,
                                sizeof(_streamFormat)),
           "kAudioUnitProperty_StreamFormat of bus 1 failed");

//音频采集结果回调
AURenderCallbackStruct recordCallback;
recordCallback.inputProc = recordCallback_xb;
recordCallback.inputProcRefCon = (__bridge void *)(self);
CheckError(AudioUnitSetProperty(_remoteIOUnit,
                            kAudioOutputUnitProperty_SetInputCallback,
                                kAudioUnitScope_Output,
                                1,
                                &recordCallback,
                                sizeof(recordCallback)),
           "couldnt set remote i/o render callback for output");

音视频编码

视频我采用的是常用的H264编码,之前说了视频采集很简单,但视频264编码也是C语言写里面的设置还是有很多需要注意的

OSStatus status = VTCompressionSessionCreate(NULL,//分配器,如果使用NULL的话,就使用默认的. CFAllocatorRef  _Nullable allocator
                                                 width,//视频帧的象素宽 int32_t width
                                                 height,//视频帧的象素高 int32_t height
                                                 kCMVideoCodecType_H264,//编码器的类型 CMVideoCodecType codecType
                                                 NULL,//如果用指定的视频编码器,就要设置这个.使用NULL就是videoToolbox自己选择一个 CFDictionaryRef  _Nullable encoderSpecification
                                                 NULL,//元象素缓存,如果你不要videoToolbox给你创建,就传NULL.使用非VTB分配的缓存,可以让你有机会拷贝图片 CFDictionaryRef  _Nullable sourceImageBufferAttributes,
                                                 NULL,//压缩数据分配器.传NULL可以使用默认的. CFAllocatorRef  _Nullable compressedDataAllocator
                                                 didCompressH264,//回调,这个方法会在另一个线程上被异步的VTCompressionSessionEncodeFrame调用.只有在你要使VTCompressionSessionEncodeFrameWithOutputHandler去编码帧时,才可以设置为NULL. VTCompressionOutputCallback  _Nullable outputCallback
                                                 (__bridge void *)(self),//回调方法所在的实例,回调方法是全局的可以设置为NULL void * _Nullable outputCallbackRefCon
                                                 &EncodingSession);//用来接收新的compression session VTCompressionSessionRef  _Nullable * _Nonnull compressionSessionOut
    NSLog(@"H264: VTCompressionSessionCreate %d", (int)status);
// Set the properties
    VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue);//实时运行
    VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_AllowFrameReordering, kCFBooleanFalse);
    
    //设置码率,均值,单位是byte
    SInt32 bitRate = width*height*3 * 4 * 8 * 2;  //越高效果越屌  帧数据越大
    CFNumberRef ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bitRate);
    VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_AverageBitRate, ref);
    CFRelease(ref);
    //设置码率上限,均值,单位是byte
    int bitRateLimit = width * height * 3 * 4 * 2;
    CFNumberRef bitRateLimitRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bitRateLimit);
    VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_DataRateLimits, bitRateLimitRef);
    
    int frameInterval = 10; //关键帧间隔 越低效果越屌 帧数据越大
    CFNumberRef  frameIntervalRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &frameInterval);
    VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_MaxKeyFrameInterval,frameIntervalRef);
    CFRelease(frameIntervalRef);

设置码率这个一定要 把宽高数据放大100倍左右不然图像会很模糊,当时因为这个问题困扰了我3天,其他倒是没有太大问题的

Socket 数据传输

socket我采用的是GCDAsyncSocket 这个框架,使用方法也很简单网上教程很多,在这不再详述,只是记录一下注意的问题
比如数据传输 常量数据进行大小端转换问题 ,还有因为数据都是采用二级制进行发送所以要对数据进行组合,但是音视频发送数据要单独写一个方法,不然NSMutableData 对象会造成内存泄露无法释放,原因是视频采集的代理方法执行速度太快导致此对象释放不及时

char sign[8] = "KOCLACMD";
uint16_t  data_type  = 0x02;
uint16_t  media_type = 0x06;
uint32_t  width  =  pixW;
uint32_t  height =  pixH;
uint32_t  len =  (uint32_t)data.length;
// 字节序转换 小端模式转换大端 匹配网络字节序
HTONS(data_type);
HTONS(media_type);
HTONL(len);
HTONL(width);
HTONL(height);

NSMutableData *muta = [NSMutableData data];
[muta appendBytes:&sign length:8];
[muta appendBytes:&data_type length:2];
[muta appendBytes:&media_type length:2];
[muta appendBytes:&len length:4];
[muta appendBytes:&width length:4];
[muta appendBytes:&height length:4];
[muta appendData:data];
//    NSLog(@"Video data (%lu): %@", (unsigned long)muta.length, muta.description);
[_clientSocket writeData:muta withTimeout:-1 tag:0];

因为你传输到其他端比如后台所以涉及到常量类型的数据要进行大小端转换,而且还要注意 short 类型转换和 long类型转换的方法不同
其他也没有什么了

相关文章

  • iOS自定义音视频采集问题汇总

    公司的直播项目一直采用的网易云的直播SDK,后来产品需求需要我们使用手机端作为音视频采集设备,使用电视端作为传输与...

  • iOS 摄像头,进行音视频的数据采集

    iOS 摄像头,进行音视频的数据采集,主要分为以下几个步骤: 1 音视频的采集,ios 中,利用 AVCaptur...

  • 直播APP流程

    音视频采集 *iOS采集音视频数据,导入AVFoundation框架,从CaptureSession会话的回调中获...

  • 2018年第二次面试题

    1、AFNetworking实现原理 2、iOS音频采集有几种方式 在iOS中有很多方法可以进行音视频采集。如 A...

  • iOS - 采集音视频及写入文件

    �音视频采集包括两部分:视频采集和音频采集。在iOS中可以同步采集视频与音频,通过系统框架AVFoundation...

  • iOS视频开发(一):视频采集

    前言 作为iOS音视频开发之视频开发的第一篇,本文介绍iOS视频采集的相关概念及视频采集的工作原理,后续将对采集后...

  • iOS视频采集以及写入二

    概述 音视频采集包括两部分:视频采集音频采集 在iOS开发中,是可以同步采集视频&音频的,使用方式也非常简单 相关...

  • AVFoundation 简介

    AVFoundation能做什么 AVFoundation 提供了 iOS 基本的音视频处理,包括播放,采集,编辑...

  • iOS 实时音频采集与播放Audio Unit使用

    前言 在iOS中有很多方法可以进行音视频采集。如 AVCaptureDevice, AudioQueue以及Aud...

  • iOS 实时音频采集与播放

    前言 在iOS中有很多方法可以进行音视频采集。如 AVCaptureDevice, AudioQueue以及Aud...

网友评论

      本文标题:iOS自定义音视频采集问题汇总

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