-
Notifications
You must be signed in to change notification settings - Fork 460
5 功能使用
- 5 功能使用
- 5.1 SDK 环境配置
- 5.1.1 初始化 SDK 环境
- 5.1.2 检查当前鉴权状态
- 5.1.3 日志的本地保存及上报
- 5.2 摄像头参数配置
- 5.2.1 前置/后置摄像头配置
- 5.2.2 Camera 对焦相关
- 5.2.3 Camera 预览 size
- 5.2.4 RecordingHint
- 5.2.5 Encoding Mirror
- 5.2.6 内置美颜
- 5.3 麦克风参数配置
- 5.3.1 是否开启蓝牙麦克风支持
- 5.4 推流参数设置
- 5.4.1 推流地址配置
- 5.4.1.1 StreamJson
- 5.4.1.2 推流 Url
- 5.4.2 VideoQuality
- 5.4.3 AudioQuality
- 5.4.4 AVProfile
- 5.4.5 HappyDns 支持
- 5.4.6 软编的 EncoderRCModes
- 5.4.7 Encoding size 的设定
- 5.4.8 StreamStatus 反馈配置
- 5.4.9 动态更改 Orientation
- 5.4.10 自适应码率
- 5.4.1 推流地址配置
- 5.5 水印设置
- 5.5.1 水印位置信息
- 5.5.1.1 WATERMARK_LOCATION
- 5.5.1.2 setCustomPosition
- 5.5.2 水印显示大小
- 5.5.3 构造 WatermarkSetting
- 5.5.1 水印位置信息
- 5.6 核心类 MediaStreamingManager
- 5.6.1 构造 MediaStreamingManager
- 5.6.2 设置 Listener
- 5.6.3 resume
- 5.6.4 开始推流
- 5.6.5 手动对焦
- 5.6.6 Zoom
- 5.6.7 闪光灯操作
- 5.6.8 切换摄像头
- 5.6.9 禁音推流
- 5.6.10 截帧
- 5.6.11 停止推流
- 5.6.12 Log 管理
- 5.6.13 pause
- 5.6.14 destroy
- 5.6.15 自定义音频数据处理
- 5.6.16 动态水印
- 5.6.17 发送 SEI 信息
- 5.6.18 动态改变预览镜像
- 5.6.19 动态改变推流镜像
- 5.6.20 推流时增加背景音乐
- 5.6.21 返听/耳返功能
- 5.6.22 QUIC 推流
- 5.6.23 图片推流
- 5.7 视频滤镜渲染
- 5.8 录屏
- 5.9 StreamingManager
- 5.1 SDK 环境配置
SDK 环境相关的配置,都在 StreamingEnv 类中进行。
/**
* 初始化 SDK 环境
* 必须在 Application 中调用此方法进行环境的初始化,否则在创建 MediaStreamingManager 的时候会抛异常
*
* @param context Application 上下文
* @param userId 用户唯一标识符,用于区分不同的用户
*/
public static void init(Context context, String userId)
/**
* 初始化 SDK 环境
* 必须在 Application 中调用此方法进行环境的初始化,否则在创建 MediaStreamingManager 的时候会抛异常
*
* @param context Application 上下文
*/
@Deprecated
public static void init(Context context)
/**
* 检查当前鉴权状态
*
* @param callback 鉴权结果回调
*/
public static void checkAuthentication(PLAuthenticationResultCallback callback)
其中,鉴权结果回调定义如下:
/**
* 回调当前的鉴权状态
*
* @param result 鉴权状态。包括:UnCheck(-1)、UnAuthorized(0)、Authorized(1)
*/
void onAuthorizationResult(int result);
/**
* 设置 SDK 日志等级
*
* @param level 日志等级。包括:VERBOSE、DEBUG、INFO、WARN、ERROR 以及 ASSERT
*/
public static void setLogLevel(int level)
/**
* 开启本地日志的保存,默认为关闭状态
*/
public static void setLogfileEnabled(boolean enabled);
/**
* 设置日志保存的最大文件个数,默认为 3
*/
public static void setLogFileMaxCount(int maxCount);
/**
* 获取当前的日志存储目录
*
* @return 日志存储目录
*/
public static String getLogFilePath();
/**
* 上报本地日志,上报结果会通过 FileLogHelper.LogReportCallback 返回
*/
public static void reportLogFiles(FileLogHelper.LogReportCallback callback);
注意: 开启日志的保存之前,需要通过调用 StreamingEnv.init(Context context, String userId)
接口进行 SDK 环境的初始化操作。
所有摄像头相关的配置,都在 CameraStreamingSetting 类中进行。
前后置摄像头配置:
mCameraStreamingSetting.setCameraId(Camera.CameraInfo.CAMERA_FACING_FRONT); // 前置摄像头
mCameraStreamingSetting.setCameraId(Camera.CameraInfo.CAMERA_FACING_BACK); // 后置摄像头
注:默认 CAMERA_FACING_BACK
若设定的摄像头打开失败,将会回调 StreamingState.OPEN_CAMERA_FAIL
.
-
setContinuousFocusModeEnabled
若希望关闭自动对焦功能,可以:
mCameraStreamingSetting.setContinuousFocusModeEnabled(false);
注:默认开启
- 通过
setFocusMode
设置对焦模式
您可以设置不同的对焦模式,目前 SDK 支持的对焦模式有:
- CameraStreamingSetting.FOCUS_MODE_CONTINUOUS_PICTURE // 自动对焦(Picture)
- CameraStreamingSetting.FOCUS_MODE_CONTINUOUS_VIDEO // 自动对焦(Video)
- CameraStreamingSetting.FOCUS_MODE_AUTO // 手动对焦
FOCUS_MODE_CONTINUOUS_PICTURE
与 FOCUS_MODE_CONTINUOUS_VIDEO
最大的区别在于,FOCUS_MODE_CONTINUOUS_PICTURE
对焦会比 FOCUS_MODE_CONTINUOUS_VIDEO
更加频繁,功耗会更高,建议使用 FOCUS_MODE_CONTINUOUS_VIDEO
。
可以通过 CameraStreamingSetting#setFocusMode
来设置不同的对焦模式:
mCameraStreamingSetting.setFocusMode(CameraStreamingSetting.FOCUS_MODE_CONTINUOUS_PICTURE);
mCameraStreamingSetting.setFocusMode(CameraStreamingSetting.FOCUS_MODE_CONTINUOUS_VIDEO);
mCameraStreamingSetting.setFocusMode(CameraStreamingSetting.FOCUS_MODE_AUTO);
注:默认值为 CameraStreamingSetting.FOCUS_MODE_CONTINUOUS_VIDEO
-
setResetTouchFocusDelayInMs
当对焦模式处理CameraStreamingSetting.FOCUS_MODE_AUTO
,并触发了手动对焦之后,若希望隔一段时间恢复到自动对焦模式,就可以调用:
mCameraStreamingSetting.setResetTouchFocusDelayInMs(3000); // 单位毫秒
注:默认值为 3000 ms
为了兼容更多的 Camera ,SDK 采用了相关的措施:
- 使用
PREVIEW_SIZE_LEVEL
和PREVIEW_SIZE_RATIO
共同确定一个 Preview Size. - 用户也可以通过
StreamingSessionListener#onPreviewSizeSelected
自定义选择一个合适的 Preview Size,onPreviewSizeSelected
的参数 list 是 Camera 系统支持的 preview size 列表(Camera.Parameters#getSupportedPreviewSizes()),SDK 内部会对 list 做从小到大的排序,可以安全地使用 list 中的 preview size。如果onPreviewSizeSelected
返回为 null,代表放弃自定义选择,那么 SDK 会使用前面的策略选择一个合适的 Preview Size,否则使用onPreviewSizeSelected
的返回值。
SDK 目前分别支持的 PREVIEW_SIZE_LEVEL
和 PREVIEW_SIZE_RATIO
分别是:
// PREVIEW_SIZE_LEVEL
- SMALL
- MEDIUM
- LARGE
// PREVIEW_SIZE_RATIO
- RATIO_4_3
- RATIO_16_9
需要注意的是:在某些机型上,list 可能为 null。
- SDK 默认会根据推流帧率自动选择一个合适的采集帧率.
- 用户也可以通过
StreamingSessionListener#onPreviewFpsSelected
自定义选择一个合适的预览 FPS,onPreviewFpsSelected
的参数 list 是 Camera 系统支持的预览 FPS 列表(Camera.Parameters#getSupportedPreviewFpsRange())。如果onPreviewFpsSelected
返回为 -1,代表放弃自定义选择,那么 SDK 会使用前面的策略选择一个合适的预览 FPS,否则使用onPreviewFpsSelected
的返回值。
可以通过 CameraStreamingSetting 对象设置 Recording hint,以此来提升数据源的帧率。
CameraStreamingSetting setting = new CameraStreamingSetting();
setting.setRecordingHint(false);
需要注意的是,在部分机型开启 Recording Hint 之后,会出现画面卡帧等风险,所以请慎用该 API。如果需要实现高 fps 推流,可以考虑开启并加入白名单机制。
对于有对前置摄像头进行 Mirror 操作的用户来说,只需通过 CameraStreamingSetting#setFrontCameraMirror
设置即可。该操作目前仅针对播放端有效。可以避免前置摄像头拍摄字体镜像反转问题。
boolean isMirror = xxx; // false as default
mCameraStreamingSetting.setFrontCameraMirror(isMirror);
内置美颜流程的开启通过 CameraStreamingSetting#setBuiltInFaceBeautyEnabled(boolean eanble)
进行,注意,若希望自定义美颜,需要 disable 该接口,否则行为未知。
在初始化 CameraStreamingSetting 的时候,可以初始化对应的美颜参数:
// FaceBeautySetting 中的参数依次为:beautyLevel,whiten,redden,即磨皮程度、美白程度以及红润程度,取值范围为[0.0f, 1.0f]
mCameraStreamingSetting.setFaceBeautySetting(new CameraStreamingSetting.FaceBeautySetting(1.0f, 1.0f, 0.8f))
.setVideoFilter(CameraStreamingSetting.VIDEO_FILTER_TYPE.VIDEO_FILTER_BEAUTY)
所有麦克风相关的配置,都在 MicrophoneStreamingSetting
类中进行。
若希望增加蓝牙麦克风的支持,需要:
mMicrophoneStreamingSetting.setBluetoothSCOEnabled(true);
注:默认值为 false
所有推流相关的参数配置,都在 StreamingProfile
类中进行。
从 v2.0.0 开始,在七牛加入推流域名白名单之后,可以使用如下接口直接推流:
mProfile.setPublishUrl("rtmp://xxx.xxx/xxx/xxx");
注:该方式当前不推荐,后续会逐渐被废弃
streamJsonStrFromServer 是由服务端返回的一段 JSON String,该 JSON String 描述了 Stream 的结构。通常,您可以使用 Pili 服务端 SDK 的 getStream(streamId) 方法来获取一个 Stream
对象,在服务端并将该对象以 JSON String 格式输出,该输出即是 streamJsonStrFromServer 变量的内容。从业务服务器获取对应的 Stream 可参考 MainActivity.java 中的 requestByHttpPost 方法。
String streamJsonStrFromServer = "stream json string from your server";
JSONObject streamJson = null;
try {
streamJson = new JSONObject(streamJsonStrFromServer);
} catch (JSONException e) {
e.printStackTrace();
}
mStreamingProfile.setStream(new Stream(streamJson));
设置 Stream 只需要保证在 MediaStreamingManager#startStreaming()
之前调用即可,SDK 接受以下调用流程:
mStreamingProfile.setStream(new Stream(mJSONObject1));
mMediaStreamingManager.setStreamingProfile(mProfile);
mMediaStreamingManager.startStreaming();
VideoQuality 是对视频质量相关参数的一个抽象,它的值对应的参数表如下:
Level | Fps | Video Bitrate(Kbps) |
---|---|---|
VIDEO_QUALITY_LOW1 | 12 | 150 |
VIDEO_QUALITY_LOW2 | 15 | 264 |
VIDEO_QUALITY_LOW3 | 15 | 350 |
VIDEO_QUALITY_MEDIUM1 | 30 | 512 |
VIDEO_QUALITY_MEDIUM2 | 30 | 800 |
VIDEO_QUALITY_MEDIUM3 | 30 | 1000 |
VIDEO_QUALITY_HIGH1 | 30 | 1200 |
VIDEO_QUALITY_HIGH2 | 30 | 1500 |
VIDEO_QUALITY_HIGH3 | 30 | 2000 |
你只需要通过 StreamingProfile#setVideoQuality
进行设置即可,如:
mStreamingProfile.setVideoQuality(StreamingProfile.VIDEO_QUALITY_MEDIUM1);
其含义为,设置视频的 fps 为 30,码率为 512 kbps
AudioQuality 是对音频质量相关参数的一个抽象,它的值对应的参数表如下:
Level | Audio Bitrate(Kbps) | Audio Sample Rate(Hz) |
---|---|---|
AUDIO_QUALITY_LOW1 | 18 | 44100 |
AUDIO_QUALITY_LOW2 | 24 | 44100 |
AUDIO_QUALITY_MEDIUM1 | 32 | 44100 |
AUDIO_QUALITY_MEDIUM2 | 48 | 44100 |
AUDIO_QUALITY_HIGH1 | 96 | 44100 |
AUDIO_QUALITY_HIGH2 | 128 | 44100 |
你只需要通过 StreamingProfile#setAudioQuality
进行设置即可,如:
mStreamingProfile.setAudioQuality(StreamingProfile.AUDIO_QUALITY_HIGH1);
其含义为,设置音频的采样率为 44100 HZ,码率为 96 kbps。
当需要自定义 video 的 fps/video bitrate/GOP size 或者 audio 的 sample rate/audio bitrate,可以通过设置 AVProfile。
// audio sample rate is 44100, audio bitrate is 96 * 1024 bps
StreamingProfile.AudioProfile aProfile = new StreamingProfile.AudioProfile(44100, 96 * 1024);
// fps is 30, video bitrate is 1000 * 1024 bps, maxKeyFrameInterval is 48
StreamingProfile.VideoProfile vProfile = new StreamingProfile.VideoProfile(30, 1000 * 1024, 48);
StreamingProfile.AVProfile avProfile = new StreamingProfile.AVProfile(vProfile, aProfile);
mStreamingProfile.setAVProfile(avProfile)
注:44100 是 Android 平台唯一保证所以设备支持的采样率,为了避免音频兼容性问题,建议设置为 44100。
StreamingProfile#setAVProfile
的优先级高于 Quality,也就是说,当同时调用了 Quality 和 AVProfile 的设置,AVProfile 会覆盖 Quality 的设置值,比如:
StreamingProfile.AudioProfile aProfile = new StreamingProfile.AudioProfile(44100, 48 * 1024);
StreamingProfile.VideoProfile vProfile = new StreamingProfile.VideoProfile(30, 1000 * 1024, 48);
StreamingProfile.AVProfile avProfile = new StreamingProfile.AVProfile(vProfile, aProfile);
mStreamingProfile.setAVProfile(avProfile)
.setVideoQuality(StreamingProfile.VIDEO_QUALITY_MEDIUM1) // |30|512|90|
.setAudioQuality(StreamingProfile.AUDIO_QUALITY_HIGH1); // |96|44100|
最终设定的值应该为:
- 音频:48 * 1024, 44100
- 视频:30, 1000 * 1024, 48
为了防止 Dns 被劫持,SDK 加入了 HappyDns 支持,可以从这里查阅源码。 从 v1.4.6 版本开始,需要在宿主项目中的 build.gradle 中加入如下语句:
dependencies {
...
compile 'com.qiniu:happy-dns:0.2.16'
...
}
通过 StreamingProfile
设定自定义 DnsManager
,如下:
public static DnsManager getMyDnsManager(Context context) {
IResolver r0 = null;
IResolver r1 = new DnspodFree();
IResolver r2 = AndroidDnsServer.defaultResolver(context);
try {
r0 = new Resolver(InetAddress.getByName("119.29.29.29"));
} catch (IOException ex) {
ex.printStackTrace();
}
return new DnsManager(NetworkInfo.normal, new IResolver[]{r0, r1, r2});
}
StreamingProfile mProfile = new StreamingProfile();
// Setting null explicitly, means give up {@link DnsManager} and access by the original host.
mStreamingProfile.setDnsManager(getMyDnsManager(this)); // set your DnsManager
若显示地设置为 null,即:
mStreamingProfile.setDnsManager(null);
SDK 会使用系统 DNS 解析,而不会使用 DnsManager
来进行 Dns 解析。
若不调用 StreamingProfile#setDnsManager
方法,SDK 会默认的创建一个 DnsManager
来对 Dns 进行解析。
注意:基于 114 dns 解析的不确定性,使用该解析可能会导致解析的网络 ip 无法做到最大的优化策略,进而出现推流质量不佳的现象。因此建议使用非 114 dns 解析
目前 RC mode 支持的类型:
- EncoderRCModes.QUALITY_PRIORITY: 质量优先,实际的码率可能高于设置的码率
- EncoderRCModes.BITRATE_PRIORITY: 码率优先,更精确地码率控制
可通过 StreamingProfile 的 setEncoderRCMode 方法进行设置,如下:
mStreamingProfile.setEncoderRCMode(StreamingProfile.EncoderRCModes.QUALITY_PRIORITY);
注:默认值为 EncoderRCModes.QUALITY_PRIORITY
SDK 将 Preview size 和 Encoding size 已经分离,他们之间互不影响。Preview size 代表的是 Camera 本地预览的 size,encoding size 代表的是编码时候的 size,即播放端不做处理时候看到视频的 size。
有两种方式可以设置 Encoding size:
- 使用内置的 encoding size level:
StreamingProfile#setEncodingSizeLevel
- 设定一个 encoding size 偏好值:
StreamingProfile#setPreferredVideoEncodingSize(int, int)
- 设定一个从指定坐标(左上角坐标原点)开始的,指定 encoding size 偏好值:
StreamingProfile#setPreferredVideoEncodingSize(int x, int y, int width, int height)
SDK 会秉持一个原则,用户偏好值的优先级会高于内置的设置。若同时调用了上述 api,偏好设置会覆盖内置的 encoding size level。
内置的 Encoding size level 对应的分辨率:
Level | Resolution(16:9) | Resolution(4:3) |
---|---|---|
VIDEO_ENCODING_HEIGHT_240 | 424 x 240 | 320 x 240 |
VIDEO_ENCODING_HEIGHT_480 | 848 x 480 | 640 x 480 |
VIDEO_ENCODING_HEIGHT_544 | 960 x 544 | 720 x 544 |
VIDEO_ENCODING_HEIGHT_720 | 1280 x 720 | 960 x 720 |
VIDEO_ENCODING_HEIGHT_1088 | 1920 x 1088 | 1440 x 1088 |
对于推流信息的反馈,可以通过 StreamingProfile#setStreamStatusConfig
来设定其反馈的频率,
mStreamingProfile#setStreamStatusConfig(new StreamingProfile.StreamStatusConfig(3)); // 单位为秒
其含义为,若注册了 mMediaStreamingManager.setStreamStatusCallback
,每隔 3 秒回调 StreamStatus 信息。
动态更改 Orientation,包括动态更改 Encoding Orientation 以及动态切换横竖屏。
- 动态更改 Encoding Orientation
更改的是编码后图像的方向,但需要重新推流才会生效;目前支持的 ENCODING_ORIENTATION 的类型有:PORT 和 LAND
用户不设置的情况下, Encoding Orientation 会依赖 Activity 的 Orientation,即有如下对应关系:
// 不调用 setEncodingOrientation
情况下,SDK 会默认根据如下关系进行设置 Encoding Orientation
Activity Orientation | -> | Encoding Orientation |
---|---|---|
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT | -> | PORT |
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE | -> | LAND |
设置 ENCODING_ORIENTATION.PORT
之后,播放端会观看竖屏的画面;
设置 ENCODING_ORIENTATION.LAND
之后,播放端会观看横屏的画面。
其设置方式如下:
mProfile.setEncodingOrientation(StreamingProfile.ENCODING_ORIENTATION.PORT);
mMediaStreamingManager.setStreamingProfile(mProfile); // notify MediaStreamingManager that StreamingProfile had been changed.
- 动态切换横竖屏
用户切换 Activity 方向后,相应地调整 Camera 的预览显示效果。
在更改了 Activity Orientation 之后,需要调用
notifyActivityOrientationChanged
通知MediaStreamingManager
。
比如:
setRequestedOrientation(isEncOrientationPort ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
mMediaStreamingManager.notifyActivityOrientationChanged();
需要注意的是,为了防止 setRequestedOrientation
调用后 Activity 的重建,需要在 AndroidManifest.xml 里面配置对应的 Activity 的如下属性:
android:configChanges="orientation|screenSize"
可查看 Demo 中的 .SWCodecCameraStreamingActivity
的配置。
由于无线网络相对于有线网络,可靠性较低,会经常遇到信号覆盖不佳导致的高丢包、高延时等问题,特别是在用网高峰期,由于带宽有限,网络拥塞的情况时有发生。自 v2.1.0 起,PLDroidMediaStreaming 提供了对应的优化方案。可以通过如下 API 开启自适应码率(注意:SDK 默认是关闭自适应码率的):
/**
* adaptive bitrate adjust mode
*/
public enum BitrateAdjustMode {
Auto, // SDK 自适应码率调节
Manual, // 用户自己实现码率调节, 范围:10kbps~10Mbps
Disable // 关闭码率调节
}
mProfile.setBitrateAdjustMode(StreamingProfile.BitrateAdjustMode.Auto);
开启自适应码率后,当 SDK 检测到网络状况差时,会尝试降低码率,直到 StreamingProfile.VIDEO_QUALITY_LOW1 (150 Kbps);反之,会提升码率,直到用户设定的 Target Bitrate(通过 StreamingProfile#setVideoQuality 或 StreamingProfile#VideoProfile 设定的码率值)
另外,可以通过下面接口控制自适应码率调节的范围:
/**
* Sets the range value of video adaptive bitrate.
*
* @param minBitrate min bitrate, unit: bps
* @param maxBitrate max bitrate, unit: bps
* */
public StreamingProfile setVideoAdaptiveBitrateRange(int minBitrate, int maxBitrate)
此时,码率调节策略为:
- 用户网络没有触发自动码率调节,码率一直保持在 Target Bitrate(通过 StreamingProfile#setVideoQuality 或 StreamingProfile#VideoProfile 设定的码率值)附近
- 触发自动码率调节后:
- 向下:
- 逐级调整,直到自适应码率调节范围的下限(minBitrate)
- 向上:
- 如果调节范围的上限 (maxBitrate) 大于 Target Bitrate,最高会调整到 Target Bitrate
- 如果调节范围的上限 (maxBitrate) 小于 Target Bitrate,最高会调整到调节范围的上限(maxBitrate)
- 向下:
- 当图像采集尺寸与推流尺寸不一致时,SDK 会对采集图像进行 resize 操作,通过 FilterMode 参数,可以对 resize 算法进行设置。
- 相关接口如下:
/**
* filter mode for libyuv
*/
public enum YuvFilterMode {
None, // Point sample; Fastest.
Linear, // Filter horizontally only.
Bilinear, // Faster than box, but lower quality scaling down.
Box // Highest quality.
}
/**
* Sets the YUV filter mode
* @param mode the {@link YuvFilterMode}, default: {@link YuvFilterMode#None}
* @return this
*/
public StreamingProfile setYuvFilterMode(YuvFilterMode mode)
- 该参数只对软编和硬编 YUV 有效
所有水印相关的配置,都在 WatermarkSetting
类中进行。
水印的位置信息,目前内置四个方位,如:
public enum WATERMARK_LOCATION {
NORTH_WEST,
NORTH_EAST,
SOUTH_WEST,
SOUTH_EAST,
}
分别在屏幕的位置如下图所示:
/**
* define the relative location of watermark on the screen when start streaming
*
* | NorthWest | | NorthEast
* | | |
* | | |
* | --------------|----------------|--------------
* | | |
* | | |
* | | |
* | --------------|----------------|--------------
* | | |
* | | |
* | SouthWest | | SouthEast
*
*/
除了通过 WATERMARK_LOCATION 的四个固定的内置位置,还可以自定义水印的位置信息:
/**
* Set the custom position of watermark top-left point (percentage of surfaceview).
* top-left of the surfaceview is the origin of the coordinate system.
* positive x-axis pointing right and the positive y-axis pointing down.
* normal values of both x and y MUST be [0.0f-1.0f]
*
* meanwhile unset the location
* @param x
* @param y
*/
public WatermarkSetting setCustomPosition(float x, float y);
使用方式如下:
// 以 Preview 的中心点为起点进行水印的绘制
watermarksetting.setCustomPosition(0.5f, 0.5f);
/**
* define de relative size of watermark
*/
public enum WATERMARK_SIZE {
LARGE,
MEDIUM,
SMALL,
}
传入 drawable 对象作为水印资源:
WatermarkSetting watermarksetting = new WatermarkSetting(this, R.drawable.qiniu_logo, WatermarkSetting.WATERMARK_LOCATION.SOUTH_WEST, WatermarkSetting.WATERMARK_SIZE.MEDIUM, 100); // 100 为 alpha 值
传入图片的绝对路径作为水印资源:
WatermarkSetting watermarksetting = WatermarkSetting(this, "watermark resource absolute path", WatermarkSetting.WATERMARK_LOCATION.SOUTH_WEST, WatermarkSetting.WATERMARK_SIZE.MEDIUM, 100) {
所有音视频推流相关的具体操作,都在 MediaStreamingManager 中进行。
在构造 MediaStreamingManager 阶段会确定其编码的类型,目前 SDK 支持的编码类型有:
- AVCodecType.HW_VIDEO_WITH_HW_AUDIO_CODEC, // 视频硬编,音频硬编
- AVCodecType.SW_VIDEO_WITH_HW_AUDIO_CODEC, // 视频软编,音频硬编
- AVCodecType.SW_VIDEO_WITH_SW_AUDIO_CODEC, // 视频软编,音频软编
- AVCodecType.SW_AUDIO_CODEC, // 纯音频软编
- AVCodecType.HW_AUDIO_CODEC, // 纯音频硬编
- AVCodecType.SW_VIDEO_CODEC, // 纯视频软编
- AVCodecType.HW_VIDEO_CODEC; // 纯视频硬编
构造带有视频 MediaStreamingManager,需要传入 AspectFrameLayout 和 GLSurfaceView
mMediaStreamingManager = new MediaStreamingManager(this, afl, cameraPreviewFrameView,
EncodingType.SW_VIDEO_WITH_SW_AUDIO_CODEC);
构造完毕后,需调用 MediaStreamingManager#prepare
向 SDK 提供对应的配置信息,以 v1.6.2 版本为例:
mMediaStreamingManager.prepare(mCameraStreamingSetting, mMicrophoneStreamingSetting, watermarksetting, mProfile);
为了更好的和 SDK 交互,接受各种状态和其他信息,需要注册对应的 Listener:
mMediaStreamingManager.setStreamingStateListener(this);
mMediaStreamingManager.setStreamingSessionListener(this);
mMediaStreamingManager.setStreamStatusCallback(this);
- StreamingStateChangedListener 接口原型如下:
/**
* Callback interface for Streaming State.
* <p>
*
* Called on an "arbitrary thread".
*
* */
public interface StreamingStateChangedListener {
/**
* Invoked if the {@link StreamingState} changed
*
* @param status the specified {@link StreamingState}
* @param extra the extra information
* */
void onStateChanged(StreamingState status, Object extra);
}
onStateChanged 中 status 对应的含义分别为:
public enum StreamingState {
/**
* The initial state.
*
* */
UNKNOWN,
/**
* Preparing the environment for network connection.
* <p>
* The first state after calling {@link StreamingManager#startStreaming()}
*
* */
PREPARING,
/**
* <ol>
* <li>{@link StreamingManager#resume()} done in pure audio streaming</li>
* <li>{@link StreamingManager#resume()} done and camera be activated in AV streaming.</li>
* </ol>
* */
READY,
/**
* Being connecting.
*
* */
CONNECTING,
/**
* The av datas start sending successfully.
*
* */
STREAMING,
/**
* Streaming has been finished, and you can {@link StreamingManager#startStreaming()} again.
*
* */
SHUTDOWN,
/**
* Connect error.
*
* The following is the possible case:
*
* <ol>
* <li>Stream is invalid</li>
* <li>Network is unreachable</li>
* </ol>
*
* */
IOERROR,
/**
* Notify the camera switched.
* <p>
* extra will including the info the new camera id.
*
* <ol>
* <li>Camera.CameraInfo.CAMERA_FACING_FRONT</li>
* <li>Camera.CameraInfo.CAMERA_FACING_BACK</li>
* </ol>
*
* <pre>
* <code>
* Log.i(TAG, "current camera id:" + (Integer)extra);
* </code>
* </pre>
* */
CAMERA_SWITCHED,
/**
* Notify the torch info after camera be active.
* <p>
* extra will including the info if the device support the Torch.
*
* <ol>
* <li>true, supported</li>
* <li>false, unsupported</li>
* </ol>
*
* <pre>
* <code>
* final boolean isSupportedTorch = (Boolean) extra;
* </code>
* </pre>
*
* */
TORCH_INFO,
/**
* Sending buffer is empty.
*
* */
SENDING_BUFFER_EMPTY,
/**
* Sending buffer have been full.
*
* */
SENDING_BUFFER_FULL,
/**
* Sending buffer have few items witch waiting to be sent.
*
* */
SENDING_BUFFER_HAS_FEW_ITEMS,
/**
* Sending buffer have many items witch waiting to be sent.
*
* */
SENDING_BUFFER_HAS_MANY_ITEMS,
/**
* The network connection has been broken.
*
* */
DISCONNECTED,
/**
* if the device hasn't the supported preview size, then it will select the default preview
* size which mismatch the specified {@link CameraStreamingSetting.PREVIEW_SIZE_RATIO}.
*
* */
NO_SUPPORTED_PREVIEW_SIZE,
/**
* {@link AudioRecord#startRecording()} failed.
*
* */
AUDIO_RECORDING_FAIL,
/**
* camera open failed.
*
* */
OPEN_CAMERA_FAIL,
/**
* Do not support NV21 preview format.
*
* */
NO_NV21_PREVIEW_FORMAT,
/**
* Invalid streaming url.
*
* Gets the message after call {@link StreamingManager#setStreamingProfile(StreamingProfile)} if streaming
* url is invalid. Also gets the url as the extra info.
* */
INVALID_STREAMING_URL,
/**
* The network had been built successfully.
*
* */
CONNECTED,
/**
* Invalid streaming url.
*
* Gets the message after call {@link MediaStreamingManager#setStreamingProfile(StreamingProfile)} if streaming
* url is invalid. Also gets the url as the extra info.
* */
UNAUTHORIZED_STREAMING_URL,
}
- StreamingSessionListener 接口原型如下:
/**
* Callback interface for some particular Streaming incidents.
* <p>
*
* Called on an "arbitrary thread".
*
* */
public interface StreamingSessionListener {
/**
* Invoked when audio recording failed.
* <p>
*
* @param code error code. <b>Unspecified now</b>.
*
* @return true means you handled the event; otherwise, given up.
*
* */
boolean onRecordAudioFailedHandled(int code);
/**
* Restart streaming notification.
* <p>
*
* When the network-connection is broken, {@link StreamingState#DISCONNECTED} will notified first,
* and then invoked this method if the environment of restart streaming is ready.
*
* <p>
* SDK won't limit the number of invocation.
*
* @param code error code. <b>Unspecified now</b>.
*
* @return true means you handled the event; otherwise, given up and then {@link StreamingState#SHUTDOWN}
* will be notified.
*
* */
boolean onRestartStreamingHandled(int code);
/**
* Invoked after camera object constructed.
* <p>
* The supported list exists in following cases:
* <ol>
* <li>If didn't set the
* {@link CameraStreamingSetting#setCameraPrvSizeRatio} when
* initialize {@link CameraStreamingSetting},
* the whole supported list would be passed</li>
* <li>If {@link CameraStreamingSetting#setCameraPrvSizeRatio}
* was set, the supported preview size which filtered by the specified ratio would be passed</li>
* </ol>
*
* @param list supported camera preview list which sorted from smallest to largest. The list maybe null.
*
* @return null means you give up selection and SDK will help you select a proper preview
* size; otherwise, the returned size will be effective.
*
* */
Camera.Size onPreviewSizeSelected(List<Camera.Size> list);
/**
* Custom preview fps, invoked after camera object constructed.
*
* @param supportedPreviewFpsRange
* a list of supported preview fps ranges by Camera. This method returns a
* list with at least one element. Every element is an int array
* of two values - minimum fps and maximum fps.
* @return -1 means you give up selection and SDK will help you select a proper preview
* fps; otherwise, the returned index will be effective.
*/
int onPreviewFpsSelected(final List<int[]> supportedPreviewFpsRange);
}
可以在 StreamingSessionListener 处理一些重连、音频读取失败、preview size 的自定义操作。
@Override
public boolean onRecordAudioFailedHandled(int err) {
mMediaStreamingManager.updateEncodingType(AVCodecType.SW_VIDEO_CODEC);
mMediaStreamingManager.startStreaming();
return true;
}
@Override
public boolean onRestartStreamingHandled(int err) {
return mMediaStreamingManager.startStreaming();
}
@Override
public Camera.Size onPreviewSizeSelected(List<Camera.Size> list) {
if (list != null) {
for (Camera.Size s : list) {
Log.i(TAG, "w:" + s.width + ", h:" + s.height);
}
// return "your choice";
}
return null;
}
在消费了 onRecordAudioFailedHandled 或 onRestartStreamingHandled 之后,您应该返回 true 通知 SDK;若不做任何处理,返回 false。
- StreamStatusCallback 接口原型如下:
/**
* Callback interface used to notify {@link StreamingProfile.StreamStatus}.
*/
public interface StreamStatusCallback {
/**
* Called per the {@link StreamingProfile.StreamStatusConfig#getIntervalMs}
*
* @param status the new {@link StreamingProfile.StreamStatus}
*
* */
void notifyStreamStatusChanged(final StreamingProfile.StreamStatus status);
}
注意:notifyStreamStatusChanged 运行在非 UI 线程中。
StreamStatus 的定义如下:
/**
* The nested class is for feedbacking the av status in real time.
*
* <p>
* You can set the {@link StreamStatusConfig} to get the preferred callback frequency.
*
* */
public static class StreamStatus {
/**
* Audio frame per second.
* */
public int audioFps;
/**
* Video frame per second.
* */
public int videoFps;
/**
* Audio and video total bits per second.
* */
public int totalAVBitrate; // bps
/**
* Audio and video total bits per second.
* */
/**
* Audio bits per second.
* */
public int audioBitrate; // bps
/**
* Video bits per second.
* */
public int videoBitrate; // bps
}
MediaStreamingManager#resume
会进行 Camera 的打开操作,当成功打开后,会返回 STATE.READY 消息,用户可以在接受到 STATE.READY 之后,安全地进行推流操作。
mMediaStreamingManager.resume();
若在一个 Activity 中进行推流操作,建议 mMediaStreamingManager.resume()
在 Activity#onResume
中被调用。
由于 startStreaming 会进行网络链接相关操作,因此需要将 startStreaming 运行在非 UI 线程,否则可能会发生崩溃现象。
mMediaStreamingManager.startStreaming();
对焦之前传入 Focus Indicator , 如果不进行设置,对焦过程中将会没有对应的 UI 显示。
// You should call this after getting {@link STATE#READY}.
mMediaStreamingManager.setFocusAreaIndicator(mRotateLayout,
mRotateLayout.findViewById(R.id.focus_indicator));
点击屏幕触发手动对焦,并设置对应的坐标值。
// You should call this after getting {@link STATE#READY}.
mMediaStreamingManager.doSingleTapUp((int) e.getX(), (int) e.getY());
Camera Zoom 操作。
// mCurrentZoom must be in the range of [0, mMediaStreamingManager.getMaxZoom()]
// You should call this after getting {@link STATE#READY}.
if (mMediaStreamingManager.isZoomSupported()) {
mMediaStreamingManager.setZoomValue(mCurrentZoom);
}
可以获取到当前的 Zoom 值:
mMediaStreamingManager.getZoom();
mMediaStreamingManager.turnLightOn();
关闭闪光灯。
mMediaStreamingManager.turnLightOff();
切换摄像头。
mMediaStreamingManager.switchCamera();
在推流过程中,将声音禁用掉:
mMediaStreamingManager.mute(true);
恢复声音:
mMediaStreamingManager.mute(false);
注:默认为 false
在 Camera 正常预览之后,可以正常进行截帧功能。
在调用 captureFrame 的时候,您需要传入 width 和 height,以及 FrameCapturedCallback,如果传入的 width 或者 height 小于等于 0,SDK 返回的 Bitmap 将会是预览的尺寸 。SDK 完成截帧之后,会回调 onFrameCaptured,并将结果以参数的形式返回给调用者。
mMediaStreamingManager.captureFrame(w, h, new FrameCapturedCallback() {
@Override
public void onFrameCaptured(Bitmap bmp) {
}
}
注意:调用者有义务对 Bitmap 进行回收释放。截帧失败,bmp 会为 null。
停止当前推流。
mMediaStreamingManager.stopStreaming();
当 enabled 设置为 true ,SDK Native 层的 log 将会被打开;当设置为 false,SDK Native 层的 log 将会被关闭。默认处于打开状态。
mMediaStreamingManager.setNativeLoggingEnabled(false);
注:默认值为 true。建议 Release 版本置为 false。
退出 MediaStreamingManager,该操作会主动断开当前的流链接,并关闭 Camera 和释放相应的资源。
mMediaStreamingManager.pause();
释放不紧要资源。
mMediaStreamingManager.destroy();
用户可以通过下面回调接口,获取当前音频数据,实现自定义音频数据处理。
// 注册音频采集数据回调
mMediaStreamingManager.setAudioSourceCallback(AudioSourceCallback callback);
public interface AudioSourceCallback {
/**
* 回调音频采集 PCM 数据
*
* @param srcBuffer 音频 PCM数据,该 buffer 是 direct ByteBuffer。
* @param size buffer的大小
* @param tsInNanoTime 时间戳,单位:纳秒
* @param isEof 采集结束标志
*/
void onAudioSourceAvailable(ByteBuffer srcBuffer, int size, long tsInNanoTime, boolean isEof);
}
通过调用 MediaStreamingManager.updateWatermarkSetting
方法可以动态改变水印的内容、位置、大小。
WatermarkSetting watermarkSetting = new WatermarkSetting(context);
watermarkSetting.setResourceId(R.drawable.qiniu_logo);
watermarkSetting.setAlpha(50);
watermarkSetting.setSize(WatermarkSetting.WATERMARK_SIZE.LARGE);
watermarkSetting.setLocation(WatermarkSetting.WATERMARK_LOCATION.SOUTH_EAST);
mMediaStreamingManager.updateWatermarkSetting(newWatermarkSetting);
可以通过调用如下接口进行 SEI 信息的发送:
/**
* 发送 SEI 信息
*
* @param msg SEI 信息内容
* @param repeatCount SEI 信息需要发送的次数
*/
public void sendSEIMessage(String msg, int repeatCount);
/**
* 发送 SEI 信息
*
* @param msg SEI 信息内容
* @param repeatCount SEI 信息需要发送的次数
* @param timestampNs SEI 信息需要展示的时间
*/
public void sendSEIMessage(String msg, int repeatCount, long timestampNs)
播放端解析 SEI 的过程中,对 payloadSize 的判断需要检测收到的 SEI 信息中 0xFF
的个数,每增加一个 0xFF
代表 payloadSize 增加 255 个字节。实际组成格式如下:
- 1 byte for SEI NAL Header
- 1 byte for SEI payload type
- (n + 1) bytes for SEI payload size
- 16 bytes for SEI NALU uuid
- (n * 255 + xy) bytes for SEI NALU payload content
- 1 byte for SEI NALU rbsp railing bits
若希望在推流过程中动态改变摄像头预览的镜像效果,可以使用如下 API:
mMediaStreamingManager.setPreviewMirror(boolean mirror)
若希望在推流过程中动态改变推流的镜像效果,可以使用如下 API:
mMediaStreamingManager.setEncodingMirror(boolean mirror)
若希望在推流过程中增加背景音乐,可以使用如下 API:
mAudioMixer = mMediaStreamingManager.getAudioMixer();
mAudioMixer.setOnAudioMixListener(new OnAudioMixListener() {
@Override
public void onStatusChanged(MixStatus mixStatus) {
mMixToggleBtn.post(new Runnable() {
@Override
public void run() {
...
}
});
}
@Override
public void onProgress(long progress, long duration) {
// time in Us
}
});
mAudioFile = Cache.getAudioFile(this); // 背景音乐文件路径
if (mAudioFile != null) {
try {
mAudioMixer.setFile(mAudioFile, true); // true/false 是否循环
} catch (IOException e) {
e.printStackTrace();
}
}
boolean s = mAudioMixer.play();
text = s ? "mixing play success" : "mixing play failed !!!";
若希望在推流过程中开启返听(耳返),可以使用如下 API:
mMediaStreamingManager.startPlayback();
mMediaStreamingManager.stopPlayback();
QUIC 是基于 UDP 开发的可靠传输协议,在弱网下拥有更好的推流效果,相比于 TCP 拥有更低的延迟,可抵抗更高的丢包率。
- 通过下面接口开启/关闭 QUIC 推流
mProfile.setQuicEnable(quicEnable);
若希望在推流过程中推送图片,可以通过如下方式实现:
设置推流图片有两种方式:
- 推流前预设图片
推流前可以通过 StreamingPofile 的方式进行图片的预设,支持本地图片或者 resource 图片,示例代码如下:
mProfile.setPictureStreamingFilePath(mPicStreamingFilePath);
或
mProfile.setPictureStreamingResourceId(R.drawable.pause_publish);
- 推流过程中动态改变图片
推流过程中可以通过 MediaStreamingManager 的接口进行图片的更换,支持本地图片或者 resource 图片,示例代码如下:
mMediaStreamingManager.setPictureStreamingFilePath(mPicStreamingFilePath);
或
mMediaStreamingManager.setPictureStreamingResourceId(R.drawable.qiniu_logo);
注意:图片当前仅支持 32 位 png
可以在开始推流之前,通过如下调用 StreamingProfile 的如下接口进行图片帧率的设置:
/**
* 设置图片推流的帧率
*
* @param fps 帧率,仅支持 0-30 的配置
*/
public void setPictureStreamingFps(float fps)
示例代码如下:
mProfile.setPictureStreamingFps(10);
可以通过如下接口进行图片推流的切换:
/**
* 切换图片推流。如果当前正在推图片,则停止图片推流,开启音视频推流;反之,则停止音视频推流,开启图片推流
*/
public boolean togglePictureStreaming()
示例代码如下:
boolean isOK = mMediaStreamingManager.togglePictureStreaming();
需要分别处理预览显示的 filter 效果和 encoding 的 filter 效果:预览显示通过实现 SurfaceTextureCallback interface;encoding 通过实现 StreamingPreviewCallback
interface。两者分别实现,互不影响。
- encoding 部分
public interface StreamingPreviewCallback {
public boolean onPreviewFrame(byte[] bytes, int width, int height, int rotation, int fmt, long tsInNanoTime);
}
onPreviewFrame 会回调 NV21 格式的 YUV 数据,进行 filter 算法处理后的结果仍然保存在 bytes 数组中,SDK 会将 bytes 中的数据当作数据源进行后续的编码和封包等操作;onPreviewFrame 运行在名称为 CameraManagerHt 的子线程中;onPreviewFrame 仅在 STATE.STREAMING 状态下被回调。
- 预览显示部分
public interface SurfaceTextureCallback {
void onSurfaceCreated();
void onSurfaceChanged(int width, int height);
void onSurfaceDestroyed();
int onDrawFrame(int texId, int width, int height);
}
四个回调均执行在 GL rendering thread;如果 onDrawFrame 直接返回 texId,代表放弃 filter 处理,否则 onDrawFrame 应该返回一个 filter 算法处理过的纹理 id; 自定义的 Texture id,即 onDrawFrame 返回的纹理 id, 必须是 GLES20.GL_TEXTURE_2D 类型;SDK 回调的纹理 id,即 onDrawFrame 的参数 texId 类型为 GLES11Ext.GL_TEXTURE_EXTERNAL_OES。
硬编模式下仅需要实现 SurfaceTextureCallback interface 就可实现预览显示和 streaming。
PLDroidMediaStreaming 封装好了录屏相关的底层操作,用户可以非常方便的进行录屏推流。其步骤如下:
- AndroidManifest.xml 注册 com.qiniu.pili.droid.streaming.screen.ScreenCaptureRequestActivity
<activity
android:name="com.qiniu.pili.droid.streaming.screen.ScreenCaptureRequestActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar" >
</activity>
- 构造核心类
ScreenStreamingManager
ScreenStreamingManager
封装了屏幕的录制、音频的采集,编码,封包和推流操作。用户只需要简单的调用相关 API 即可实现录屏推流:
// 构造 ScreenStreamingManager
ScreenStreamingManager screenStreamingManager = new ScreenStreamingManager(this);
// 配置相关参数
screenStreamingManager.prepare(screenSetting, null, streamingProfile);
// 开始推流
screenStreamingManager.startStreaming();
// 停止推流
screenStreamingManager.stopStreaming();
// 销毁
screenStreamingManager.destroy();
**注意:**如果项目 targetVersion >= 29 ,则 startStreaming() 应该运行在子线程,在开始录屏之后 SDK 内部会创建一个默认的 Notification ,如果您需要使用自定义 Notification,那么请在开始推流之前调用如下代码:
screenStreamingManager.setNotification(customNotificationId, customNotification);
如果后续想要更新此 Notification ,那么可以在更改自定义 notification 对象后调用如下代码即可:
screenStreamingManager.notifyNotification()
- 自定义音频数据处理
用户可以通过下面回调接口,获取当前音频数据,实现自定义音频数据处理。
// 注册音频采集数据回调
screenStreamingManager.setAudioSourceCallback(AudioSourceCallback callback);
public interface AudioSourceCallback {
/**
* 回调音频采集 PCM 数据
*
* @param srcBuffer 音频 PCM数据,该 buffer 是 direct ByteBuffer。
* @param size buffer的大小
* @param tsInNanoTime 时间戳,单位:纳秒
* @param isEof 采集结束标志
*/
void onAudioSourceAvailable(ByteBuffer srcBuffer, int size, long tsInNanoTime, boolean isEof);
}
StreamingManager
是类似 MediaStreamingManager 的一个类,两者的区别是:StreamingManager 不带采集,仅包含编码、封包推流模块,从功能层面可以理解为:
MediaStreamingManager = 采集模块 + (处理模块) + StreamingManager
其调用过程类似于 MediaStreamingManager:
构造 StreamingManager -> prepare -> resume -> startStreaming -> inputAudioFrame/inputVideoFrame -> stopStreaming -> pause -> destroy
具体可以参考 Demo 中的 ExtCapStreamingActivity.java
PS:若希望使用自己已有项目中的采集/处理模块,可以选用 StreamingManager.
void inputAudioFrame(ByteBuffer buffer, int size, long tsInNanoTime, boolean isEof);
void inputAudioFrame(byte[] buffer, long tsInNanoTime, boolean isEof);
可以选择传入 ByteBuffer 或者 byte[] 类型的 PCM 源数据,传入的时间戳为 nano time。
void inputVideoFrame(ByteBuffer buffer, int size, int width, int height, int rotation, boolean mirror, int fmt, long tsInNanoTime);
void inputVideoFrame(byte[] buffer, int width, int height, int rotation, boolean mirror, int fmt, long tsInNanoTime);
可以选择传入 ByteBuffer 或者 byte[] 类型的 YUV 数据,其中:
- width 和 height,分别为该 frame 的宽和高,单位像素
- rotation ,指该 frame 需要选择的角度(0,90,180,360),若自己已经处理好角度的旋转,rotation 参数为 0
- mirror,指是否对该 frame 做镜像处理
- fmt,指该 frame 的格式,目前支持 NV21 和 I420,即:PLFourCC.FOURCC_NV21 和 PLFourCC.FOURCC_I420
- tsInNanoTime,指该 frame 对应的时间戳,单位为纳秒
通过调用如下接口开始推流:
mStreamingManager.startStreaming();
通过调用如下接口停止推流:
mStreamingManager.stopStreaming();
getInputSurface
须在 startStreaming
被调用成功之后,从 MediaCodec
获取其 Surface
类型的 InputSurface,用户可以在这个 Surface
上面进行自定义绘制,绘制好后使用 frameAvailable
通知 SDK 进行编码。
若希望使用该高级功能,需在 AVCodecType.HW_VIDEO_SURFACE_AS_INPUT_WITH_HW_AUDIO_CODEC
模式下,否则会抛出异常。
通过调用如下接口进行 SEI 信息的发送:
/**
* 发送 SEI 信息
*
* @param msg 信息内容
* @param repeatCount 信息重复次数
*/
public void sendSEIMessage(String msg, int repeatCount)
/**
* 发送带有时间戳的 SEI 信息
*
* @param msg 信息内容
* @param repeatCount 信息重复次数
* @param timestampNs 信息显示时间
*/
public void sendSEIMessage(String msg, int repeatCount, long timestampNs)