webrtc ios 噪音怎么处理
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了webrtc ios 噪音怎么处理相关的知识,希望对你有一定的参考价值。
参考技术A webrtc ios 噪音处理推荐使用ZEGO即构科技。ZEGO即构科技毫秒级音视频互动,千万级高并发,70%丢包下仍能保障稳定流畅的观看体验。 【点击免费试用,0成本启动】方法/步骤
1 ,右击桌面右下角的“喇叭”图标,点击“录音设备”。
2 ,这是出现一个“麦克风”设备,右击“麦克风”,点击“属性”。
3 ,“麦克风属性”在“侦听”中,找到“侦听此设备”,若打钩,把钩去掉
4 ,再点击“级别”,把“麦克风加强”调为0.0dB。
可以适度调节麦克风音量。
5 ,在“增强”中,选择“禁用所有声音效果”。
6 ,最后再在“高级”中,找到“独占模式”,在它下边有两个选项,全部打钩。
分别是“允许应用程序独占控制该设备”“给与独占模式应用程序优先”。还可以调下“默认格式”。
想要了解更多关于webrtc的相关信息,推荐咨询ZEGO即构科技。公司自成立伊始,就专注自研音视频引擎,在音频前处理、网络自适应和跨平台兼容性等方面,达到国际一流水平,同时充分利用基础云服务商的能力,构建了MSDN海量有序自学习数据网络,服务覆盖全球,涵盖上百个音视频互动业务场景。官网佳佳_airkp
2016-07-09·TA获得超过297个赞知道小有建树答主回答量:采纳率:100%帮助的人:191万我也去答题访问个人页关注 "Blacker,您好,本人一直从事音视频算法的处理与研究,包括H264视频,语音抑制,回音消除,噪音处理等分支。最近已经转向webrtc了,对webrtc也算是相对熟悉了。不过我在利用webrtc模块来开发时,遇到了一个音频采集的问题。不知道你是否遇到了,你们是否有相关的处理方法呢。
webrtc在pc和ios手机端,效果可以说是非常的好,但是在android手机端,效果就不怎么样了,语音断断续续的,效果很差,造成这个的其中一个因素就是AECM和AGC,噪音消除等这些模块造成的,我也仔细研究过这些算法的底层罗辑,发现下面的算法很多地方的初始值就是取一个经验值,这些值的大小影响最终效果,难怪苹果和pc设备的效果那么好,因为这些经验值很可能就是针对pc和苹果手机的。由于android手机设备种类繁多,所以不同的设备喇叭和麦克风不同导致效果差异太大。
当然这些我还可以修改底层的语音算法来优化,但是android端,有一个问题我始终没有头绪,就是音频部分的采集问题。基本上所有android手机,采集出来的音频数据就是不完整的,偶尔断断续续,加上后续音频的处理,经过处理后的效果就更差了。底层的音频采集,用了opensles来实现,当然他还提供了回调上层java来实现采集的模块,用一个开关WEBRTC_ANDROID_OPENSLES就可以打开,这些底层采集出来的语音都是有问题的。
用webrtc自带的webrtcdemo就可以测试出来,特别是关掉视频后,只开音频的话,问题更明显.采集部分的代码我也看过,里面有一个缓冲大小设置,这个调大后也是作用不大的。当然我也写单独的demo来测试,如果我不调用StartReceive和StartPlayout,而只是调用StartSend,那个采集出来的音频就是非常完整的,效果也非常好的,前两者只是开启了接收监听和播放线程,它和startsend开启的采集线程根本就是毫不相关的,为什么就相互影响了呢。
所以我有时候怀疑webrtc的架构是不是有问题呢?
这个问题一直都没有头绪,你在webrtc接触比我久,经验比我丰富很多,接触的牛人也多得多,我想咨询一下你们是怎么处理底层的声音采集问题呢,只要把这个问题解决了,android手机端的音频效果一定会提高50%以上,那可是质的飞跃啊。盼望你能回复,一起探讨一下这个问题怎么解决,谢谢了。 "本回答被提问者采纳
IOS技术分享| 在iOS WebRTC 中添加美颜滤镜
在使用WebRTC的时候,对视频进行美颜处理一般有两种方式:替换WebRTC中的采集模块和对视频数据进行美颜。
一、替换WebRTC中的采集模块
替换WebRTC中的采集模块,相对比较简单,使用GPUImageVideoCamera替换WebRTC中的视频采集,得到经过GPUImage添加美颜处理后的图像,发送给WebRTC的OnFrame方法。
参考基于WebRTC框架开发的全平台推拉流SDK:Github
设置美颜
- (void)setBeautyFace:(BOOL)beautyFace{
if(_beautyFace == beautyFace) return;
_beautyFace = beautyFace;
[_emptyFilter removeAllTargets];
[_filter removeAllTargets];
[_videoCamera removeAllTargets];
if(_beautyFace){
_filter = [[GPUImageBeautifyFilter alloc] init];
_emptyFilter = [[GPUImageEmptyFilter alloc] init];
}else{
_filter = [[GPUImageEmptyFilter alloc] init];
}
__weak typeof(self) _self = self;
[_filter setFrameProcessingCompletionBlock:^(GPUImageOutput *output, CMTime time) {
[_self processVideo:output];
}];
[_videoCamera addTarget:_filter];
if (beautyFace) {
[_filter addTarget:_emptyFilter];
if(_gpuImageView) [_emptyFilter addTarget:_gpuImageView];
} else {
if(_gpuImageView) [_filter addTarget:_gpuImageView];
}
}
格式转换
GPUImage处理后的Pixel格式为BGRA,当处理完成后需要转换为I420格式,用于内部处理和渲染。
-(void) processVideo:(GPUImageOutput *)output{
rtc::CritScope cs(&cs_capture_);
if (!_isRunning) {
return;
}
@autoreleasepool {
GPUImageFramebuffer *imageFramebuffer = output.framebufferForOutput;
size_t width = imageFramebuffer.size.width;
size_t height = imageFramebuffer.size.height;
uint32_t size = width * height * 3 / 2;
if(self.nWidth != width || self.nHeight != height)
{
self.nWidth = width;
self.nHeight = height;
if(_dst)
delete[] _dst;
_dst = NULL;
}
if(_dst == NULL)
{
_dst = new uint8_t[size];
}
uint8_t* y_pointer = (uint8_t*)_dst;
uint8_t* u_pointer = (uint8_t*)y_pointer + width*height;
uint8_t* v_pointer = (uint8_t*)u_pointer + width*height/4;
int y_pitch = width;
int u_pitch = (width + 1) >> 1;
int v_pitch = (width + 1) >> 1;
libyuv::ARGBToI420([imageFramebuffer byteBuffer], width * 4, y_pointer, y_pitch, u_pointer, u_pitch, v_pointer, v_pitch, width, height);
if(self.bVideoEnable)
libyuv::I420Rect(y_pointer, y_pitch, u_pointer, u_pitch, v_pointer, v_pitch, 0, 0, width, height, 32, 128, 128);
if(_capturer != nil)
_capturer->CaptureYUVData(_dst, width, height, size);
}
}
美颜后的数据发送给WebRTC的OnFrame方法
GPUImageVideoCapturer 类为GPUImage 封装的摄像头类,跟WebRTC中的采集类功能保持一致,继承 cricket::VideoCapturer 类,便可以往WebRTC中塞入采集的音视频流。
namespace webrtc {
// 继承cricket::VideoCapturer
class GPUImageVideoCapturer : public cricket::VideoCapturer {
...
}
}
void GPUImageVideoCapturer::CaptureYUVData(const webrtc::VideoFrame& frame, int width, int height)
{
VideoCapturer::OnFrame(frame, width, height);
}
二、对视频数据进行美颜
对视频数据美颜的思路就是传统的第三方美颜SDK的做法,对内部采集的音视频数据进行处理:内部采集的数据(CVPixelBufferRef)-》转换为纹理(GLuint)-》对纹理进行音视频的美颜-》美颜的纹理转换为iOS的采集数据(CVPixelBufferRef)-》返回给WebRTC内部进行渲染编码和传输。
同步线程
内部处理的一般都是使用同步线程,这样能够保证数据线性流动,参阅GPUImage中的代码片段
runSynchronouslyOnVideoProcessingQueue(^{
// 美颜处理
});
把CVPixelBufferRef 数据转换为纹理(GLuint)
RGB格式类型的转换方式
-
CoreVideo
框架的方法:使用此方法可以创建CVOpenGLESTextureRef
纹理,并通过CVOpenGLESTextureGetName(texture)
获取纹理id。- (GLuint)convertRGBPixelBufferToTexture:(CVPixelBufferRef)pixelBuffer { if (!pixelBuffer) { return 0; } CGSize textureSize = CGSizeMake(CVPixelBufferGetWidth(pixelBuffer), CVPixelBufferGetHeight(pixelBuffer)); CVOpenGLESTextureRef texture = nil; CVReturn status = CVOpenGLESTextureCacheCreateTextureFromImage(nil, [[GPUImageContext sharedImageProcessingContext] coreVideoTextureCache], pixelBuffer, nil, GL_TEXTURE_2D, GL_RGBA, textureSize.width, textureSize.height, GL_BGRA, GL_UNSIGNED_BYTE, 0, &texture); if (status != kCVReturnSuccess) { NSLog(@"Can\'t create texture"); } self.renderTexture = texture; return CVOpenGLESTextureGetName(texture); }
-
OpenGL
的方法:创建纹理对象,使用glTexImage2D
方法上传CVPixelBufferRef
中图像数据data到纹理对象中。glBindTexture(GL_TEXTURE_2D, [outputFramebuffer texture]); glTexImage2D(GL_TEXTURE_2D, 0, _pixelFormat==GPUPixelFormatRGB ? GL_RGB : GL_RGBA, (int)uploadedImageSize.width, (int)uploadedImageSize.height, 0, (GLint)_pixelFormat, (GLenum)_pixelType, bytesToUpload);
YUV格式类型的转换方式
- (GLuint)convertYUVPixelBufferToTexture:(CVPixelBufferRef)pixelBuffer {
if (!pixelBuffer) {
return 0;
}
CGSize textureSize = CGSizeMake(CVPixelBufferGetWidth(pixelBuffer),
CVPixelBufferGetHeight(pixelBuffer));
[EAGLContext setCurrentContext:self.context];
GLuint frameBuffer;
GLuint textureID;
// FBO
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
// texture
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSize.width, textureSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
glViewport(0, 0, textureSize.width, textureSize.height);
// program
glUseProgram(self.yuvConversionProgram);
// texture
CVOpenGLESTextureRef luminanceTextureRef = nil;
CVOpenGLESTextureRef chrominanceTextureRef = nil;
CVReturn status = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
self.textureCache,
pixelBuffer,
nil,
GL_TEXTURE_2D,
GL_LUMINANCE,
textureSize.width,
textureSize.height,
GL_LUMINANCE,
GL_UNSIGNED_BYTE,
0,
&luminanceTextureRef);
if (status != kCVReturnSuccess) {
NSLog(@"Can\'t create luminanceTexture");
}
status = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
self.textureCache,
pixelBuffer,
nil,
GL_TEXTURE_2D,
GL_LUMINANCE_ALPHA,
textureSize.width / 2,
textureSize.height / 2,
GL_LUMINANCE_ALPHA,
GL_UNSIGNED_BYTE,
1,
&chrominanceTextureRef);
if (status != kCVReturnSuccess) {
NSLog(@"Can\'t create chrominanceTexture");
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(luminanceTextureRef));
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glUniform1i(glGetUniformLocation(self.yuvConversionProgram, "luminanceTexture"), 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, CVOpenGLESTextureGetName(chrominanceTextureRef));
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glUniform1i(glGetUniformLocation(self.yuvConversionProgram, "chrominanceTexture"), 1);
GLfloat kXDXPreViewColorConversion601FullRange[] = {
1.0, 1.0, 1.0,
0.0, -0.343, 1.765,
1.4, -0.711, 0.0,
};
GLuint yuvConversionMatrixUniform = glGetUniformLocation(self.yuvConversionProgram, "colorConversionMatrix");
glUniformMatrix3fv(yuvConversionMatrixUniform, 1, GL_FALSE, kXDXPreViewColorConversion601FullRange);
// VBO
glBindBuffer(GL_ARRAY_BUFFER, self.VBO);
GLuint positionSlot = glGetAttribLocation(self.yuvConversionProgram, "position");
glEnableVertexAttribArray(positionSlot);
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
GLuint textureSlot = glGetAttribLocation(self.yuvConversionProgram, "inputTextureCoordinate");
glEnableVertexAttribArray(textureSlot);
glVertexAttribPointer(textureSlot, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3* sizeof(float)));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDeleteFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glFlush();
self.luminanceTexture = luminanceTextureRef;
self.chrominanceTexture = chrominanceTextureRef;
if (luminanceTextureRef) {
CFRelease(luminanceTextureRef);
}
if (chrominanceTextureRef) {
CFRelease(chrominanceTextureRef);
}
return textureID;
}
使用GPUImageTextureInput 加载滤镜和使用GPUImageTextureOutput输出数据
[GPUImageContext setActiveShaderProgram:nil];
GPUImageTextureInput *textureInput = [[ARGPUImageTextureInput alloc] initWithTexture:textureID size:size];
GPUImageSmoothToonFilter *filter = [[GPUImageSmoothToonFilter alloc] init];
[textureInput addTarget:filter];
GPUImageTextureOutput *textureOutput = [[GPUImageTextureOutput alloc] init];
[filter addTarget:textureOutput];
[textureInput processTextureWithFrameTime:kCMTimeZero];
得到textureOutput,即得到输出的纹理。
GPUImageTextureOutput纹理转化为CVPixelBufferRef 数据
- (CVPixelBufferRef)convertTextureToPixelBuffer:(GLuint)texture
textureSize:(CGSize)textureSize {
[EAGLContext setCurrentContext:self.context];
CVPixelBufferRef pixelBuffer = [self createPixelBufferWithSize:textureSize];
GLuint targetTextureID = [self convertRGBPixelBufferToTexture:pixelBuffer];
GLuint frameBuffer;
// FBO
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
// texture
glBindTexture(GL_TEXTURE_2D, targetTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSize.width, textureSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, targetTextureID, 0);
glViewport(0, 0, textureSize.width, textureSize.height);
// program
glUseProgram(self.normalProgram);
// texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glUniform1i(glGetUniformLocation(self.normalProgram, "renderTexture"), 0);
// VBO
glBindBuffer(GL_ARRAY_BUFFER, self.VBO);
GLuint positionSlot = glGetAttribLocation(self.normalProgram, "position");
glEnableVertexAttribArray(positionSlot);
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
GLuint textureSlot = glGetAttribLocation(self.normalProgram, "inputTextureCoordinate");
glEnableVertexAttribArray(textureSlot);
glVertexAttribPointer(textureSlot, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3* sizeof(float)));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDeleteFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glFlush();
return pixelBuffer;
}
把美颜后的CVPixelBufferRef同步返回给SDK,进行渲染传输。
三、总结
对音视频的美颜,已经成为了音视频应用的常用功能,除了上述两种做法外,还可以使用第三方美颜,一般音视频厂商都有提供自采集功能,而第三方美颜功能则提供有采集美颜相机功能,二者正好可以无缝结合。如果自身的应用中对美颜要求不是很高,采用音视频SDK自带的美颜即可(美白、美颜、红润),如果用在娱乐场景,除了美颜,还要美型(廋脸,大眼)、贴纸(2D、3D)的,必须要集成第三方美颜SDK了。
以上是关于webrtc ios 噪音怎么处理的主要内容,如果未能解决你的问题,请参考以下文章