经过麦克风摄像头采集音视频数据
复制代码
AVAudioSession *session = [AVAudioSession sharedInstance];
// 监听声音路线改变
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(handleRouteChange:)
name: AVAudioSessionRouteChangeNotification
object: session];
// 监听声音被打断
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(handleInterruption:)
name: AVAudioSessionInterruptionNotification
object: session];
/// 建立AudioComponent
AudioComponentDescription acd;
acd.componentType = kAudioUnitType_Output;
//acd.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
acd.componentSubType = kAudioUnitSubType_RemoteIO;
acd.componentManufacturer = kAudioUnitManufacturer_Apple; // 厂商 直接写kAudioUnitManufacturer_Apple
acd.componentFlags = 0; // 没有明确值时必须设为0
acd.componentFlagsMask = 0; // 没有明确值时必须设为0
self.component = AudioComponentFindNext(NULL, &acd);
OSStatus status = noErr;
status = AudioComponentInstanceNew(self.component, &_componetInstance);
if (noErr != status) {
[self handleAudioComponentCreationFailure];
}
/// 链接麦克风
UInt32 flagOne = 1;
AudioUnitSetProperty(self.componetInstance, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &flagOne, sizeof(flagOne));
AudioStreamBasicDescription desc = {0};
desc.mSampleRate = _configuration.audioSampleRate; // 采样率
desc.mFormatID = kAudioFormatLinearPCM;
desc.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
desc.mChannelsPerFrame = (UInt32)_configuration.numberOfChannels;
desc.mFramesPerPacket = 1;
desc.mBitsPerChannel = 16; // 表示每一个声道的音频数据要多少位,一个字节是8位,因此用8 * 每一个采样的字节数
desc.mBytesPerFrame = desc.mBitsPerChannel / 8 * desc.mChannelsPerFrame;
desc.mBytesPerPacket = desc.mBytesPerFrame * desc.mFramesPerPacket; // 根据mFormatFlags指定的Float类型非交错存储,就设置为bytesPerSample表示每一个采样的字节数。但若是是Interleaved交错存储的,就应该设置为bytesPerSample * mChannelsPerFrame 由于左右声道数据是交错存在一块儿的。
// 链接扬声器
AudioUnitSetProperty(self.componetInstance, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &desc, sizeof(desc));
// 设置声音回调
AURenderCallbackStruct cb;
cb.inputProcRefCon = (__bridge void *)(self);
cb.inputProc = handleInputBuffer;
AudioUnitSetProperty(self.componetInstance, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 1, &cb, sizeof(cb));
// 初始化AudioComponentInstance
status = AudioUnitInitialize(self.componetInstance);
if (noErr != status) {
[self handleAudioComponentCreationFailure];
}
[session setPreferredSampleRate:_configuration.audioSampleRate error:nil];
/// AVAudioSessionCategoryPlayAndRecord 支持音频播放和录音、打断其余不支持混音APP、不会被静音键或锁屏键静音
/// AVAudioSessionCategoryOptionDefaultToSpeaker 系统会自动选择最佳的内置麦克风组合支持视频聊天。
/// AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers 支持和其余APP音频混合
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers error:nil];
[session setActive:YES withOptions:kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation error:nil];
[session setActive:YES error:nil];
复制代码
音频配置:码率(128)和采样率(44100HZ)
视频配置:视频分辨率(720P )、码率(2000KB/S)和帧率(30FPS)
复制代码
苹果默认是 PCMweb
aac 是为了取代 MP3,压缩了原始文件,可是取决于比特率,合适的比特率人耳分辨不出算法
将数据采集的输入流进行实时滤镜, 美颜
复制代码
特效滤镜处理缓存
GPUImage (开源) 只有iOS 小型的能够作 封装和思惟基于 OpenGL ES服务器
Metal 苹果 新开发的markdown
OpenGL PCsession
OpenGL ES 线路 手机框架
AudioToolBox FFmpeg AACide
软解码:使用 fdk_aac 将 PCM 转为 AAC
复制代码
人耳掩盖效应:去除冗余信息ui
目前使用有损压缩比较多编码
有损压缩:解压后的数据彻底能够复原
有损压缩:解压后的数据不能彻底复原,会丢失一部分信息,压缩比越大,丢失的信息越多,信号还原的失真就好越大
VideoToolBox FFmpeg H264
软编码: FFmpeg X264
用到CPU
硬编码 VideoToolBox AudioToolBox
商业项目 通用 硬编码
GPU (运算大于CPU)
硬件加速器
视频编码 VideoToolBox FFmpeg H264
音频编码 AudioToolBox FFmpeg AAC
复制代码
YUV 而不是用 rgba
1s 内 60张照片
10张为一组 进行压缩
取第一帧为I帧,后面称之为 P帧,只保留和前一帧的不一样点
B 帧 是保留和后一帧的差别。
为了追求高压缩(如小视频),可以使用 I P B
可是直播是为了追求高实时性,所以使用I P,而不使用B帧
复制代码
VTCompressionSessionCreate
复制代码
经过 AVFoundation 获取捕获结果回调
原始数据封装为 CVPixelBuffers,
CVPixelBuffers 为主内存存储的全部像素点数据的对象
复制代码
encode 为 CMSampleBuffers
六、将数据写入 H264 文件 获取到 SPS/PPS, 第一帧写入 SPS/PPS VideoToolBox 在没一个关键帧前面都会输出 SPS/PPS信息,若是本帧为关键帧,则能够取出对应的 PPS / SPS 信息。
将采集的音视频信息经过流媒体协议发送到流媒体服务器
复制代码
数据分发 CDN
截屏
录制
实时转码
负责拉流、解码和播放
复制代码
拉流:
音视频解码
相对于 HLS 来讲,采用 RTMP 协议时,从采集推流端到流媒体服务器再到播放端是一条数据流,所以在服务器不会有落地文件。这样 RTMP 相对来讲就有这些优势:
延时较小,一般为 1-3s。
基于 TCP 长链接,不须要屡次建连。
所以业界大部分直播业务都会选择用 RTMP 做为流媒体协议。一般会将数据流封装成 FLV 经过 HTTP 提供出去。可是这样也有一些问题须要解决:
iOS 平台没有提供原生支持 RTMP 或 HTTP-FLV 的播放器,这就须要开发支持相关协议的播放器。
复制代码
HLS 的基本原理就是当采集推流端将视频流推送到流媒体服务器时,服务器将收到的流信息每缓存一段时间就封包成一个新的 ts 文件,同时服务器会创建一个 m3u8 的索引文件来维护最新几个 ts 片断的索引。当播放端获取直播时,它是从 m3u8 索引文件获取最新的 ts 视频文件片断来播放,从而保证用户在任什么时候候链接进来时都会看到较新的内容,实现近似直播的体验。相对于常见的流媒体直播协议,例如 RTMP 协议、RTSP 协议等,HLS 最大的不一样在于直播客户端获取到的并非一个完整的数据流,而是连续的、短时长的媒体文件,客户端不断的下载并播放这些小文件。这种方式的理论最小延时为一个 ts 文件的时长,通常状况为 2-3 个 ts 文件的时长。HLS 的分段策略,基本上推荐是 10 秒一个分片,这就看出了 HLS 的缺点:
一般 HLS 直播延时会达到 20-30s,而高延时对于须要实时互动体验的直播来讲是不可接受的。
HLS 基于短链接 HTTP,HTTP 是基于 TCP 的,这就意味着 HLS 须要不断地与服务器创建链接,TCP 每次创建链接时的三次握手、慢启动过程、断开链接时的四次挥手都会产生消耗。
不过 HLS 也有它的优势:
数据经过 HTTP 协议传输,因此采用 HLS 时不用考虑防火墙或者代理的问题。
使用短时长的分片文件来播放,客户端能够平滑的切换码率,以适应不一样带宽条件下的播放。
HLS 是苹果推出的流媒体协议,在 iOS 平台上能够得到自然的支持,采用系统提供的 AVPlayer 就能直接播放,不用本身开发播放器。
image
复制代码
先经过服务器将FLV下载到本地缓存,而后再经过NetConnection的本地链接来播放这个FLV,这种方法是播放本地的视频,并非播放服务器的视频。所以在本地缓存里能够找到这个FLV。其优势就是服务器下载完这个FLV,服务器就没有消耗了,节省服务器消耗。其缺点就是FLV会缓存在客户端,对FLV的保密性很差。
是一种将直播流模拟成FLV文件,经过HTTP协议进行下载的模式来实现流媒体传输的协议,端口号80
通常建议使用HTTP FLV,实时性和RTMP相等。
优势:HTTP相比于RTMP省去了一些协议交互时间,首屏时间更短。HTTP可拓展的功能更多。
复制代码