音视频前处理
更新时间: 2024/08/23 10:24:52
本章节主要介绍网易云信提供的各种音视频前处理功能。前处理介于采集和编码之间,按照数据类型分类,可以分为音频前处理和视频前处理,音频前处理包括降噪、回音消除、人声检测、自动增益、耳返等等,视频前处理包括美颜、磨皮、设置对比度、镜像、水印等。网易云信移动端SDK中内置了基础款的美颜滤镜。 同时,网易云信还提供了音视频采集数据的回调功能,所以除了SDK自带的一些音视频的前处理功能,开发者可以利用音视频采集数据的回调功能,实现自定义的音视频数据前处理,包括接入第三方变声、美颜算法。
音频前处理
音频采集数据回调与发送
- API介绍
当用户开始外部语音处理后,采集到的语音数据通过次回调通知。 用户可以对语音数据做相应的变声等不同的处理。需要通过setParameters
开启语音数据处理。
- API原型
java /**
* 语音数据处理接口, 不要改变数据的长度. 需要设置参数 {@link AVChatParameters#KEY_AUDIO_FRAME_FILTER}.
*
* @param frame 语音帧
* @return 返回false 失败
* @see AVChatParameters#KEY_AUDIO_FRAME_FILTER
* @see AVChatManager#setParameter(AVChatParameters.Key, Object)
*/
boolean onAudioFrameFilter(AVChatAudioFrame frame);
- 参数说明
参数 | 说明 |
---|---|
frame | 语音帧,参考AVChatAudioFrame。 |
- 示例
java@Override
public boolean onAudioFrameFilter(AVChatAudioFrame frame) {}
音频耳返
- API介绍
当主播想要从耳机中听到自己的声音时,可以开启耳返。一般使用在主播开启伴音,主播戴上耳机,随着伴奏说话唱歌,可以从耳机中实时听到融合了音乐和自己人声的声音。
- API原型
java /**
* <p>开启耳返</p>
* <p>
* <p>在通话建立后可以打开耳返功能,打开后可以从耳机中实时听到自己的声音。</p>
*
* @return {@code true} 方法调用成功,{@code false} 方法调用失败
*/
public abstract boolean startPlayCapturedAudio();
/**
* <p>关闭耳返</p>
* <p>
* <p>在成功开启耳返功能后, 可以随时关闭耳返效果。</p>
*
* @return {@code true} 方法调用成功,{@code false} 方法调用失败
*/
public abstract boolean stopPlayCapturedAudio();
/**
* <p>设置耳返音量</p>
* <p>
* <p>在成功打开耳返功能后,可以实时调整耳返音量。</p>
*
* @param volume 播放耳返音量 [0.0f - 1.0f]
* @return {@code true} 方法调用成功,{@code false} 方法调用失败
*/
public abstract boolean setPlayCapturedAudioVolume(float volume);
- 参数说明
setPlayCapturedAudioVolume参数说明:
参数 | 说明 |
---|---|
volume | 播放耳返数据音量[0.0f - 1.0f] |
视频前处理
视频采集数据回调与发送
- API介绍
当用户开始外部视频处理后,采集到的视频数据通过此回调通知。 用户可以对视频数据做相应的美颜等不同的处理。 需要通过setParameters
开启视频数据处理,根据情况选择开启新的或者老的数据回调,推荐使用新的数据回调方式。
parameters.setBoolean(AVChatParameters.KEY_VIDEO_FRAME_FILTER, on);//开启老的视频数据回调
parameters.setBoolean(AVChatParameters.KEY_VIDEO_FRAME_FILTER_NEW, on);//开启新的视频数据回调
- API原型1(弃用)
/**
* 视频数据外部处理接口, 此接口需要同步执行. 操作运行在视频数据发送线程上,处理速度过慢会导致帧率过低
*
* @param frame 待处理数据
* @param maybeDualInput 如果为 {@code true} 则代表需要外部输入两路数据,
* {@link AVChatVideoFrame#data} 处理后的原始数据,{@link AVChatVideoFrame#dataMirror} 处理后的镜像数据。
* 如果为 {@code false} 则代表仅需要外部输入一路数据,仅支持 {@link AVChatVideoFrame#data}。
* 在实际使用过程中,用户需要根据自己需求来决定是否真正需要输入镜像数据,一般在使用到水印等外部处理时才会需要真正输入两路数据,其他情况可以忽略此参数。
* @return 返回true成功
*/
boolean onVideoFrameFilter(AVChatVideoFrame frame, boolean maybeDualInput);
- 参数说明
参数 | 说明 |
---|---|
frame | 待处理数据,参考AVChatVideoFrame。 |
maybeDualInput | 是否需要外部输入两路数据,如果为 true 则代表需要外部输入两路数据。 |
- 示例
java@Override
public boolean onVideoFrameFilter(AVChatVideoFrame frame, boolean maybeDualInput)) {}
- API原型2(推荐)
java/**
* 视频数据外部处理接口, 此接口需要同步执行. 操作运行在视频数据发送线程上,处理速度过慢会导致帧率过低
* @param input 待处理数据
* @param outputFrames {@link com.netease.nrtc.sdk.video.VideoFrame[0]} 处理后的数据,{@link com.netease.nrtc.sdk.video.VideoFrame[1]} 处理后的镜像数据。
* 在实际使用过程中,用户需要根据自己需求来决定是否真正需要输入镜像数据,一般在使用到水印等外部处理时才会需要真正输入两路数据,其他情况可以忽略此参数。
* @param filterParameter 待处理数据的参数
* @return 返回true成功
*/
boolean onVideoFrameFilter(final com.netease.nrtc.sdk.video.VideoFrame input, com.netease.nrtc.sdk.video.VideoFrame[] outputFrames, VideoFilterParameter filterParameter);
- 参数说明
参数 | 说明 |
---|---|
input | 待处理数据,统一用VideoFrame封装,可能是I420/NV/Texture格式 |
outputFrames | outputFrames[0]处理后的数据,outputFrames[1]处理后的镜像数据 |
filterParameter | 待处理参数 ,参考 VideoFilterParameter |
- 示例
java@Override
boolean onVideoFrameFilter(final VideoFrame input, VideoFrame[] outputFrames, VideoFilterParameter filterParameter){
...
if(format == VideoFrameFormat.kVideoTexture) {
innerVideoEffect.init(getApplicationContext(), true, true);
}else{
innerVideoEffect.init(getApplicationContext(), true, false);
}
...
if (frame.getBuffer().getFormat() == VideoFrameFormat.kVideoTexture) {
return filterTextureFrame(frame,outputFrames,filterParameter);
} else {
return filterByteBufferFrame(frame, outputFrames, filterParameter);
}
...
}
滤镜模块初始化
- API介绍
SDK的发布包中打包有滤镜SDK,开发者可以根据需要接入滤镜功能,轻松实现磨皮、滤镜、静态水印和动态水印功能。滤镜模块的接口文档打开查阅API文档
滤镜模块需要运行在GLES 3.0及以上版本的系统中,对应API版本为API >= 18。
滤镜模块可以通过gradle进行依赖,如
javadependencies {
implementation 'com.netease.nrtc:videoeffect:1.0.3'
}
如果在API < 18工程中依赖,需要在AndroidManifest.xml
中申明此库支持低版本依赖,运行时需增加API >= 18才使用滤镜库逻辑
xml<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!--添加以下内容-->
<uses-sdk tools:overrideLibrary="com.netease.nrtc.libvideoeffect"/>
<application>...<application/>
</manifest>
接入时,需要在采集视频数据的回调方法中自定义滤镜功能,该回调需要设置对应参数才会触发。
java boolean onVideoFrameFilter(final AVChatVideoFrame frame, final boolean maybeDualInput);
滤镜模块的功能实现由VideoEffect
类提供,使用前需要通过VideoEffectFactory.getVCloudEffect
创建一个对应实例。
- API原型
java/**
* 滤镜模块初始化前处理
*/
public void init(Context context, boolean useFilter, boolean hasGLContext);
- 参数说明
参数 | 说明 |
---|---|
context | 应用上下文,建议使用ApplicationContext。 |
useFilter | 是否使用滤镜功能。 |
hasGLContext | 调用线程是否具有EGLContext。如果当前线程没有EGLContext,那么该接口会创建一个。后续所有的滤镜API必须保证在同一个具有GL Context的线程中调用。 |
- 示例
javamVideoEffect = VideoEffectFactory.getVCloudEffect();
mVideoEffect.init(getApplicationContext(), true, false);
- 特殊说明
滤镜的功能需要在
onVideoFrameFilter
方法中同步处理,因此该初始化方法也需要在该方法中进行完成调用,开发者也可以在onVideoFrameFilter
中使用第三方滤镜 SDK 进行视频处理。
对视频美颜
- API介绍
对视频美颜需要调用 VideoEffect
的设置滤镜类型和滤镜强度接口。
- API原型
java //设置滤镜类型,可设置为`FilterType`枚举类中的一种
public abstract void setFilterType(FilterType type);
//设置滤镜强度,有效值为(0-1)
public abstract void setFilterLevel(float level);
- 参数说明
参数 | 说明 |
---|---|
FilterType | 滤镜类型。 |
level | 滤镜强度,有效值为(0-1)。 |
- 示例
javamVideoEffect.setFilterType(VideoEffect.FilterType.nature);
mVideoEffect.setFilterLevel(0.5f);
设置磨皮强度
- API介绍
对视频进行磨皮需要调用 VideoEffect
的设置磨皮强度接口。
- API原型
java/** 设置磨皮强度
* 参数:
* level - (0-5)*/
public abstract void setBeautyLevel(int level)
- 参数说明
参数 | 说明 |
---|---|
level | 磨皮强度,有效值为(0-5)。 |
- 示例
javamVideoEffect.setBeautyLevel(5);
设置静态水印
- API介绍
设置静态水印需要调用 VideoEffect
的设置静态水印接口。
- API原型
java/**添加水印(只需要调用一次)
* 参数:
* bitmap - 水印图片
* rect - 水印具体位置(上下左右中四个基本位置)
* x - 距离 rect 的 x 坐标
* y - 距离 rect 的 y 坐标 */
public void addWaterMark(android.graphics.Bitmap bitmap,
VideoEffect.Rect rect,
int x,
int y)
- 参数说明
参数 | 说明 |
---|---|
bitmap | 水印图片。 |
rect | 水印具体位置(上下左右中四个基本位置)。 |
x | 距离 rect 的 x 坐标。 |
y | 距离 rect 的 y 坐标。 |
- 示例
javamVideoEffect.addWaterMark(mWaterMaskBitmapStatic, frame.width / 2, frame.height / 2);
设置动态水印
- API介绍
设置动态水印需要调用 VideoEffect
的设置动态水印接口。
- API原型
java/**添加水印(只需要调用一次)
* 添加动态水印(只需要调用一次)
* 参数:
* bitmapArray - 水印图片
* rect - 水印具体位置(上下左右中四个基本位置)
* x - 距离 rect 的 x 坐标
* y - 距离 rect 的 y 坐标
* fps - 动态水印的帧率
* cameraFps - 相机采集的帧率
* looped - 是否循环播放 */
public void addDynamicWaterMark(android.graphics.Bitmap[] bitmapArray,
VideoEffect.Rect rect,
int x,
int y,
int fps,
int cameraFps,
boolean looped)
- 参数说明
参数 | 说明 |
---|---|
bitmapArray | 水印图片。 |
rect | 水印具体位置(上下左右中四个基本位置)。 |
x | 距离 rect 的 x 坐标。 |
y | 距离 rect 的 y 坐标。 |
fps | 动态水印的帧率。 |
cameraFps | 相机采集的帧率。 |
looped | 是否循环播放。 |
- 示例
java mVideoEffect.closeDynamicWaterMark(false);
mVideoEffect.addDynamicWaterMark(null, frame.width / 2, frame.height / 2, 23, AVChatVideoFrameRate.FRAME_RATE_15, true);
mVideoEffect.addDynamicWaterMark(mWaterMaskBitmapDynamic, frame.width / 2, frame.height / 2, 23, AVChatVideoFrameRate.FRAME_RATE_15, true);
滤镜处理
- ByteBuffer的滤镜API介绍
对图片原始数据进行滤镜处理,并返回RGBA格式的数据,如果开启了滤镜,则每一帧都需要调用该方法.采用GPU对buffer数据进行滤镜处理,必须在具有EGLContext的线程上进行调用。
- API原型
java/**采用GPU对buffer数据进行滤镜处理(必须在具有EGLContext的线程上进行调用)
* 参数:
* format - 数据类型
* data - 原始数据
* width - 图像宽
* height - 图像高
* 返回:
* RGBA图像数组 */
public abstract byte[] filterBufferToRGBA(VideoEffect.DataFormat format,
byte[] data,
int width,
int height)
- 参数说明
参数 | 说明 |
---|---|
format | 数据类型。 |
data | 原始数据。 |
width | 图像宽。 |
height | 图像高。 |
- 示例
java byte[] intermediate = mVideoEffect.filterBufferToRGBA(format, frame.data, frame.width, frame.height);
- Texture的滤镜API介绍
对图片原始数据进行滤镜处理,并2D的纹理ID,如果开启了滤镜,则每一帧都需要调用该方法.采用GPU对纹理数据进行滤镜处理,必须在具有EGLContext的线程上进行调用。
- API原型
java/**
* 采用GPU对Camera采集的OES Texture进行滤镜处理 (必须在具有EGLContext的线程上进行调用)
*
* @param texture OES Texture
* @param width 采集图像宽
* @param height 采集图像高
* @return 2D texture纹理,可以对这个texture做后续操作,如硬编、预览
*/
public abstract int filterTexture(int texture, int width, int height);
- 参数说明
参数 | 说明 |
---|---|
texture | 数据类型。 |
height | 图像宽。 |
height | 图像高。 |
- 示例
java int filtedTextureId = mVideoEffect.filterTexture(textureId, width, height);
水印和涂鸦处理
- ByteBuffer API介绍
该方法将输入的原始视频数据转为YUV格式数据,同时会根据autoEffect
配置是否为true
自动进行水印、涂鸦、旋转等操作。
方法会返回两路数据,但只有第一路数据是始终有效的,并需要将之拷贝至AVChatVideoFrame.data
中去。
仅当needMirrorData
为true
时,第二路数据有效,并需要将第二路数据拷贝至AVChatVideoFrame.dataMirror
中,并设置AVChatVideoFrame.dualInput
为true
。
- API原型
java/**原始视频数据转YUV,同时会根据配置进行水印、涂鸦、旋转等操作,保证输出的是正的YUV数据
* 参数:
* src - 原始视频数据
* dataFormat - 原始视频数据的色彩空间类型
* inWidth - 输入视频宽
* inHeight - 输入视频高
* cameraRotation - 相机拍摄调度
* displayOrientation - 计算后的显示角度
* outWidth - 输出视频宽
* outHeight - 输出视频高
* needMirrorData - 是否需要镜像后的数据(主要用于前置摄像头下水印、涂鸦等本地镜像显示和编码数据的区别)
* autoEffect - 是否自动添加上水印、涂鸦等
* 返回:
* 具有水印、涂鸦等正的YUV数据*/
public VideoEffect.YUVData[] TOYUV420(byte[] src,
VideoEffect.DataFormat dataFormat,
int inWidth,
int inHeight,
int cameraRotation,
int displayOrientation,
int outWidth,
int outHeight,
boolean needMirrorData,
boolean autoEffect)
- 参数说明
参数 | 说明 |
---|---|
src | 原始视频数据。 |
dataFormat | 原始视频数据的色彩空间类型。 |
width | 视频宽。 |
height | 视频高。 |
cameraRotation | 相机拍摄调度。 |
displayOrientation | 计算后的显示角度。 |
outWidth | 输出视频宽。 |
outHeight | 输出视频高。 |
needMirrorData | 是否需要镜像后的数据。 |
autoEffect | 是否自动添加上水印、涂鸦等。 |
- 示例
java result = mVideoEffect.TOYUV420(intermediate, VideoEffect.DataFormat.RGBA, frame.width, frame.height, frame.rotation, 90, frame.width, frame.height, needMirrorData, true);
-
Texture API介绍
添加水印前,先将纹理做完滤镜,通过旋转或者镜像等处理,将纹理处理成正的方向。不然后续再做旋转,水印方向就会有问题。
方法会返回两路数据,但只有第一路数据是始终有效的,并需要将之拷贝至AVChatVideoFrame.data
中去。
仅当needMirrorData
为true
时,第二路数据有效,并需要将第二路数据拷贝至AVChatVideoFrame.dataMirror
中,并设置AVChatVideoFrame.dualInput
为true
。
- 纹理添加水印 API原型
java /**
* 手动水印
* @param data - 纹理格式的数据,包括纹理ID,纹理的byte[]数据,纹理长,纹理宽等。
* @param needI420Data - 是否需要I420数据。
* @param waterCount - 需要添加水印的纹理数。比如镜像过和未镜像过的两个纹理。
* @return TextureData - 包括纹理ID,纹理的byte[]数据,纹理长,纹理宽等。
*/
public TextureData effectWater(TextureData data,boolean needI420Data, int waterCount){}
- 参数说明
参数 | 说明 |
---|---|
data | 纹理格式的数据,包括纹理ID,纹理长,纹理宽等 |
needI420Data | 是否需要I420数据。 |
waterCount | 需要添加水印的纹理数。比如镜像过和未镜像过的两个纹理。 |
- 示例
java //////给未镜像数据加水印
VideoEffect.TextureData textureDataW = mVideoEffect.effectWater(textureData, false,1);
- 纹理旋转 API原型
java /**
* 原始纹理数据进行旋转
* @param textureType 输入纹理类型 GLES11Ext.GL_TEXTURE_EXTERNAL_OES、GLES20.GL_TEXTURE_2D
* @param textureId 输入纹理ID
* @param srcWidth 输入宽
* @param srcHeight 输入高
* @param rotation 对纹理的旋转角度
* @return 旋转后的纹理信息
*/
public TextureData rotateTexture(int textureType,int textureId, int srcWidth, int srcHeight, int rotation){}
- 参数说明
参数 | 说明 |
---|---|
textureType | 输入纹理类型 GLES11Ext.GL_TEXTURE_EXTERNAL_OES、GLES20.GL_TEXTURE_2D |
textureId | 输入纹理ID |
srcWidth | 输入宽 |
srcHeight | 输入高 |
rotation | 对纹理的旋转角度 |
- 示例
java VideoEffect.TextureData textureDataR = mVideoEffect.rotateTexture(GLES20.GL_TEXTURE_2D, filtedTextureId, width, height, rotation);
- 纹理镜像 API原型
java /**
* 原始纹理数据镜像处理
*
* @param textureType 输入纹理类型 GLES11Ext.GL_TEXTURE_EXTERNAL_OES、GLES20.GL_TEXTURE_2D
* @param textureId 输入纹理ID
* @param srcWidth 输入宽
* @param srcHeight 输入高
* @return 镜像后的纹理信息
*/
public TextureData mirrorTexture(int textureType,int textureId, int srcWidth, int srcHeight){}
- 参数说明
参数 | 说明 |
---|---|
textureType | 输入纹理类型 GLES11Ext.GL_TEXTURE_EXTERNAL_OES、GLES20.GL_TEXTURE_2D |
textureId | 输入纹理ID |
srcWidth | 输入宽 |
srcHeight | 输入高 |
- 示例
javaVideoEffect.TextureDatatextureData = mVideoEffect.mirrorTexture(GLES20.GL_TEXTURE_2D, textureDataR.textureId, textureDataR.width, textureDataR.height);
销毁滤镜模块
- API介绍
销毁滤镜模块,需要在init
方法调用的线程中调用
- API原型
java//销毁滤镜
public void unInit()
- 示例
javamVideoEffect.unInit();
mVideoEffect = null;
设置视频预览镜像
- API介绍
设置视频预览镜像是通过setParameters
或者setParameter
接口进行设置的,参数名称在AVChatParameters
中的KEY_VIDEO_LOCAL_PREVIEW_MIRROR
。
- API原型
java /**
* 前置摄像头本地预览镜像
*
* >当使用前置摄像头时,本地预览画面是否镜像。 默认前置摄像头画面镜像处理。
*/
public static final Key<Boolean> KEY_VIDEO_LOCAL_PREVIEW_MIRROR = new Key<>(RtcParameters.KEY_VIDEO_LOCAL_PREVIEW_MIRROR, Boolean.class);
- 参数说明
参数 | 说明 |
---|---|
value | true 镜像,false 不镜像。 |
- 示例
javaboolean mirror = AVChatManager.getInstance().getParameter(AVChatParameters.KEY_VIDEO_LOCAL_PREVIEW_MIRROR);
AVChatManager.getInstance().setParameter(AVChatParameters.KEY_VIDEO_LOCAL_PREVIEW_MIRROR, !mirror);
设置视频编码镜像
- API介绍
设置视频预览镜像是通过setParameters
或者setParameter
接口进行设置的,参数名称在AVChatParameters
中的KEY_VIDEO_TRANSPORT_MIRROR
。
- API原型
java /**
* 前置摄像头发送数据预览镜像
*
* 当使用前置摄像头时,本地发送画面是否镜像。 默认前置摄像头发送画面不镜像处理。
*
public static final Key<Boolean> KEY_VIDEO_TRANSPORT_MIRROR = new Key<>(RtcParameters.KEY_VIDEO_TRANSPORT_MIRROR, Boolean.class);
- 参数说明
参数 | 说明 |
---|---|
value | true 镜像,false 不镜像。 |
- 示例
javaboolean mirror = AVChatManager.getInstance().getParameter(AVChatParameters.KEY_VIDEO_TRANSPORT_MIRROR);
AVChatManager.getInstance().setParameter(AVChatParameters.KEY_VIDEO_TRANSPORT_MIRROR, !mirror);