human: 如何处理七牛云的直播云功能使用

gpt: [直播云](https://developer.qiniu.com/pili) > SDK 下载 > 推流端 >Android 推流端 SDK >功能使用

#  功能使用

最近更新时间: 2023-08-31 20:32:46

# 1 SDK 环境配置

SDK 环境相关的配置，都在 StreamingEnv 类中进行。

## 1.1 初始化 SDK 环境

    
    
    /**
      * 初始化 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)
    

## 1.2 检查当前鉴权状态

    
    
    /**
      * 检查当前鉴权状态
      *
      * @param callback 鉴权结果回调
      */
    public static void checkAuthentication(PLAuthenticationResultCallback callback)
    

其中，鉴权结果回调定义如下：

    
    
    /**
      * 回调当前的鉴权状态
      *
      * @param result 鉴权状态。包括：UnCheck(-1)、UnAuthorized(0)、Authorized(1)
      */
    void onAuthorizationResult(int result);
    

## 1.3 日志的本地保存及上报

    
    
    /**
      * 设置 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 环境的初始化操作。

## 1.4 更新 UserID

    
    
    /**
      * 更新 UserID
      *
      * @param userId 用户唯一标识符，用于区分不同的用户
      */
    public static void updateUid(String uid)
    

可用于切换账号的场景下，更新 init 时传入的 userID。

# 2 摄像头参数配置

所有摄像头相关的配置，都在 CameraStreamingSetting 类中进行。

## 2.1 前置／后置摄像头配置

前后置摄像头配置：

    
    
    mCameraStreamingSetting.setCameraId(Camera.CameraInfo.CAMERA_FACING_FRONT); // 前置摄像头
    mCameraStreamingSetting.setCameraId(Camera.CameraInfo.CAMERA_FACING_BACK); // 后置摄像头
    

注：默认 `CAMERA_FACING_BACK`

若设定的摄像头打开失败，将会回调 `StreamingState.OPEN_CAMERA_FAIL` .

## 2.2 Camera 对焦相关

  * `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

## 2.3 Camera 预览 size

为了兼容更多的 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。

## 2.4 Camera 预览 FPS

  * SDK 默认会根据推流帧率自动选择一个合适的采集帧率，最终选择的帧率范围可通过 MediaStreamingManager#getPreviewFpsRange 接口获取。
  * 用户也可以通过 `StreamingSessionListener#onPreviewFpsSelected` 自定义选择一个合适的预览 FPS，`onPreviewFpsSelected` 的参数 list 是 Camera 系统支持的预览 FPS 列表（Camera.Parameters#getSupportedPreviewFpsRange()）。如果 `onPreviewFpsSelected` 返回为 -1，代表放弃自定义选择，那么 SDK 会使用前面的策略选择一个合适的预览 FPS，否则使用 `onPreviewFpsSelected` 的返回值。

## 2.5 RecordingHint

可以通过 CameraStreamingSetting 对象设置 Recording hint，以此来提升数据源的帧率。

    
    
    CameraStreamingSetting setting = new CameraStreamingSetting();
    setting.setRecordingHint(false);
    

需要注意的是，在部分机型开启 Recording Hint 之后，会出现画面卡帧等风险，所以请慎用该 API。如果需要实现高 fps
推流，可以考虑开启并加入白名单机制。

## 2.6 Encoding Mirror

对于有对前置摄像头进行 Mirror 操作的用户来说，只需通过 `CameraStreamingSetting#setFrontCameraMirror`
设置即可。该操作目前仅针对播放端有效。可以避免前置摄像头拍摄字体镜像反转问题。

    
    
    boolean isMirror = xxx; // false as default
    mCameraStreamingSetting.setFrontCameraMirror(isMirror);
    

## 2.7 内置美颜

内置美颜流程的开启通过 `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)
    

# 3 麦克风参数配置

所有麦克风相关的配置，都在 `MicrophoneStreamingSetting` 类中进行。

## 3.1 设置采样率

SDK 默认采样率为 44100Hz，若需要更改音频采集的采样率，可通过调用如下接口实现：

    
    
    mMicrophoneStreamingSetting.setSampleRate(16000);
    

**注意：设备通常不会支持所有的采样率，44100 Hz 采样率兼容性最好，因此，若无特殊需求，保持 SDK 默认值即可；其他常见采样率：16000
Hz、48000 Hz**

## 3.2 设置声道数

SDK 默认采集为单声道，若需要更改音频采集的声道数，可通过调用如下接口实现：

    
    
    mMicrophoneStreamingSetting.setChannelConfig(AudioFormat.CHANNEL_IN_STEREO);
    

**注意：单双声道分别需要传入`AudioFormat.CHANNEL_IN_MONO` 以及
`AudioFormat.CHANNEL_IN_STEREO`**

## 3.3 是否开启蓝牙麦克风支持

若希望增加蓝牙麦克风的支持，需要：

    
    
    mMicrophoneStreamingSetting.setBluetoothSCOEnabled(true);
    

注：默认值为 false

## 3.4 开启硬件回声消除

若需要开启设备的硬件回声消除功能，可通过调用如下接口实现：

    
    
    mMicrophoneStreamingSetting.setAECEnabled(true);
    

**注意：硬件回声消除可能存在设备兼容性问题，建议开启硬件回声消除的场景下，使用 16kHz，单声道的音频参数进行采集，以获得更好的兼容性**

# 4 推流参数设置

所有推流相关的参数配置，都在 `StreamingProfile` 类中进行。

## 4.1 推流地址配置

从 v2.0.0 开始，在七牛加入推流域名白名单之后，可以使用如下接口直接推流：

    
    
    mProfile.setPublishUrl("rtmp://xxx.xxx/xxx/xxx");
    

其中，推流地址的生成方式可参考[服务端文档 __](https://developer.qiniu.com/pili/1220/server-sdk)

## 4.2 VideoQuality

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

## 4.3 AudioQuality

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。

## 4.4 AVProfile

当需要自定义 video 的 fps、bitrate、profile 或者 audio 的 sample rate、bitrate，可以通过
AVProfile 设置。

其中 video profile 有以下选项

Level | Comment  
---|---  
StreamingProfile.H264Profile.HIGH | 更好的质量，但有一些性能的损耗  
StreamingProfile.H264Profile.MAIN | 质量与性能较好的平衡  
StreamingProfile.H264Profile.BASELINE | 更好的性能，但一般的质量  
  
AVProfile 的使用例子如下

    
    
    // audio sample rate is 44100, audio bitrate is 48 * 1024 bps
    StreamingProfile.AudioProfile aProfile = new StreamingProfile.AudioProfile(44100, 48 * 1024);
    
    // fps is 20, video bitrate is 1000 * 1024 bps, maxKeyFrameInterval is 60, profile is HIGH
    StreamingProfile.VideoProfile vProfile = new StreamingProfile.VideoProfile(20, 1000 * 1024, 60, StreamingProfile.H264Profile.HIGH);
    
    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(20, 1000 * 1024, 60);
    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
  * 视频：20, 1000 * 1024, 60

## 4.5 HappyDns 支持

为了防止 Dns 被劫持，SDK 加入了 HappyDns 支持，可以从[这里 __](https://github.com/qiniu/happy-
dns-android)查阅源码。 从 v1.4.6 版本开始，必须在宿主项目中的 build.gradle 中加入如下语句：

    
    
    dependencies {
        ...
        implementation 'com.qiniu:happy-dns:x.y.z'
        ...
    }
    

其中，不同版本的 SDK 需依赖的 HappyDns 版本可能不同，详情可参阅[版本升级须知
__](https://developer.qiniu.com/pili/3716/PLDroidMediaStreaming-preparation-
before-development#4)

通过 `StreamingProfile` 设定自定义 `DnsManager`，如下：

    
    
    public static DnsManager getMyDnsManager() {
        IResolver[] resolvers = new IResolver[2];
        // 配置自定义 DNS 服务器地址
        String[] udpDnsServers = new String[]{"223.5.5.5"};
        resolvers[0] = new DnsUdpResolver(udpDnsServers, Record.TYPE_A, DNS_DEFAULT_TIMEOUT);
        // 配置 HTTPDNS 地址
        String[] httpDnsServers = new String[]{"https://223.6.6.6/dns-query"};
        resolvers[1] = new DohResolver(httpDnsServers, Record.TYPE_A, DNS_DEFAULT_TIMEOUT);
        return new DnsManager(NetworkInfo.normal, resolvers);
    }
    
    StreamingProfile mProfile = new StreamingProfile();
    mStreamingProfile.setDnsManager(getMyDnsManager()); // 设置自定义 DnsManager
    

  * 可通过创建 DnsUdpResolver 对象配置自定义的 DNS 服务器地址
  * 可通过创建 DohResolver 对象配置支持 Doh(Dns over http) 协议的 url

其中，UDP 的方式解析速度快，但是安全性无法得到保证，HTTPDNS 的方式解析速度慢，但是安全性有保证，您可根据您的使用场景自行选择合适的解析方式。

若不调用 `StreamingProfile#setDnsManager` 方法，SDK 会默认的创建一个 `DnsManager` 来对 Dns
进行解析，默认优先使用 UDP 的解析方式。

## 4.6 码率控制模式

可通过设置 EncoderRCModes 指定推流的码率控制模式，目前 RC mode 支持的类型：

  * EncoderRCModes.QUALITY_PRIORITY: 质量优先，实际的码率可能高于设置的码率
  * EncoderRCModes.BITRATE_PRIORITY: 码率优先，更精确地码率控制

可通过 StreamingProfile 的 setEncoderRCMode 方法进行设置，如下：

    
    
    mStreamingProfile.setEncoderRCMode(StreamingProfile.EncoderRCModes.QUALITY_PRIORITY);
    

注：默认值为 `EncoderRCModes.QUALITY_PRIORITY`

## 4.7 Encoding size 的设定

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)`

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  
  
## 4.8 StreamStatus 反馈配置

对于推流信息的反馈，可以通过 `StreamingProfile#setStreamStatusConfig` 来设定其反馈的频率：

    
    
    mStreamingProfile#setStreamStatusConfig(new StreamingProfile.StreamStatusConfig(3)); // 单位为秒
    

其含义为，若注册了 `mMediaStreamingManager.setStreamStatusCallback` ，每隔 3 秒回调
StreamStatus 信息。

## 4.9 动态更改 Orientation

动态更改 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` 的[配置
__](https://github.com/pili-
engineering/PLDroidMediaStreaming/blob/master/PLDroidMediaStreamingDemo/app/src/main/AndroidManifest.xml)。

## 4.10 自适应码率

由于无线网络相对于有线网络，可靠性较低，会经常遇到信号覆盖不佳导致的高丢包、高延时等问题，特别是在用网高峰期，由于带宽有限，网络拥塞的情况时有发生。自
[v2.1.0 __](https://github.com/pili-
engineering/PLDroidMediaStreaming/releases/tag/v2.1.0-beta)起，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)

## 4.11 FilterMode 参数设置

  * 当图像采集尺寸与推流尺寸不一致时，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 有效

## 4.12 SRT 协议支持

从 v3.1.0 版本开始，新增对 SRT 传输协议的支持，开启方式如下：

    
    
     /**
      * 开启/关闭 SRT 协议传输
      *
      * @param enabled 开启/关闭 (true / false)
      * @return this
      */
    public StreamingProfile setSrtEnabled(boolean enabled)
    

开启后，使用符合官方标准的 SRT 推流地址即可实现 SRT 协议的音视频传输，SRT 推流地址标准可参考 [access-control
__](https://github.com/Haivision/srt/blob/master/docs/features/access-
control.md#general-syntax)，示例如下：

    
    
    srt://pili-publish.qnsdk.com?streamid=#!::h=sdk-live/xxx,m=publish
    

## 4.13 视频编码格式设置

从 v3.1.5 版本开始，新增视频编码格式的设置，支持设置 H.264 或 H.265 编码格式，默认使用 H.264 编码格式。

设置方式如下：

    
    
    /**
     * 视频编码格式
     */
    public enum PLVideoEncodeType {
        H264,  // H.264 编码格式
        HEVC   // H.265 编码格式
    }
    
    /**
     * 设置视频编码格式
     *
     * @param videoEncodeType 视频编码格式
     * @return this
     */
    public StreamingProfile setVideoEncodeType(PLVideoEncodeType videoEncodeType)
    

基于 Android 设备的兼容性问题，视频编码格式可能由于设备不支持该编码而出现创建编码器失败的情况，最终的编码格式将会通过
**StreamingStateChangedListener** 回调接口返回。处理回调的示例代码如下：

    
    
    private StreamingStateChangedListener mStreamingStateChangedListener = new StreamingStateChangedListener() {
      @Override
      public void onStateChanged(StreamingState streamingState, final Object extra) {
          case VIDEO_ENCODER_READY:
            // 编码器初始化完成，编码类型为 ((PLVideoEncodeType) extra).name())
            break;
        });
      }
    };
    

**注意：**HEVC(H.265) 视频编码格式仅在视频硬编且传输协议为 RTMP 和 QUIC 时生效。软编和 SRT 协议下，将会自动切换到
H.264 视频编码格式

# 5 水印设置

所有水印相关的配置，都在 `WatermarkSetting` 类中进行。

## 5.1 水印位置信息

水印的位置信息，目前内置四个方位，如：

    
    
    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);
    

## 5.2 水印显示大小

    
    
    /**
     * define de relative size of watermark
     */
     public enum WATERMARK_SIZE {
         LARGE,
         MEDIUM,
         SMALL,
     }
    

2.4.0 版本后提供了设置自定义水印像素大小的功能，使用方式如下：

    
    
    watermarksetting.setCustomSize(int width, int height);
    

## 5.3 构造 `WatermarkSetting`

传入 drawable 对象作为水印资源：

    
    
    WatermarkSetting watermarksetting = new WatermarkSetting(mContext, R.drawable.qiniu_logo, WatermarkSetting.WATERMARK_LOCATION.SOUTH_WEST, WatermarkSetting.WATERMARK_SIZE.MEDIUM, 100); // 100 为 alpha 值
    

传入图片的绝对路径作为水印资源：

    
    
    WatermarkSetting watermarksetting = WatermarkSetting(mContext, "watermark resource absolute path", WatermarkSetting.WATERMARK_LOCATION.SOUTH_WEST, WatermarkSetting.WATERMARK_SIZE.MEDIUM, 100) {
    

# 6 核心类 MediaStreamingManager

所有音视频推流相关的具体操作，都在 MediaStreamingManager 中进行。

## 6.1 构造 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，需要传入 GLSurfaceView

    
    
    mMediaStreamingManager = new MediaStreamingManager(mContext, mGLSurfaceView, EncodingType.SW_VIDEO_WITH_SW_AUDIO_CODEC);
    

构造完毕后，需调用 `MediaStreamingManager#prepare` 向 SDK 提供对应的配置信息，以 v1.6.2 版本为例：

    
    
    mMediaStreamingManager.prepare(mCameraStreamingSetting, mMicrophoneStreamingSetting, mWatermarkSetting, mProfile);
    

## 6.2 设置 Listener

为了更好的和 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,
        
        /**
         * start video encoder success
         */
        VIDEO_ENCODER_READY,
        
        /**
         * start video encoder failed
         */
        START_VIDEO_ENCODER_FAIL,
    
        /**
         * video encoder error happened
         */
        VIDEO_ENCODER_ERROR,
    
        /**
         * start audio encoder failed
         */
        START_AUDIO_ENCODER_FAIL,
    
        /**
         * video encoder error happened
         */
        AUDIO_ENCODER_ERROR,
    
        /**
         * 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,
    }
    

**注意：** 如果出现了编码器错误相关的回调状态，如 `START_VIDEO_ENCODER_FAIL` `VIDEO_ENCODER_ERROR`
`START_AUDIO_ENCODER_FAIL` `AUDIO_ENCODER_ERROR` 等，用户可以在接收到状态回调后，更新相应的
AVCodecType 类型，再重新开始推流

  * 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 bits per second.
         * */
        public int audioBitrate;  // bps
    
        /**
         * Video bits per second.
         * */
        public int videoBitrate;  // bps
      
        /**
         * Number of dropped video frames in callback period.
         * */
        public int droppedVideoFrames;
    }
    

## 6.3 resume

`MediaStreamingManager#resume` 会进行 Camera 的打开操作，当成功打开后，会返回 STATE.READY
消息，用户可以在接受到 STATE.READY 之后，安全地进行推流操作。

    
    
    mMediaStreamingManager.resume();
    

若在一个 Activity 中进行推流操作，建议 `mMediaStreamingManager.resume()` 在
`Activity#onResume` 中被调用。

## 6.4 开始推流

由于 startStreaming 会进行网络链接相关操作，因此需要将 startStreaming 运行在非 UI 线程，否则可能会发生崩溃现象。

    
    
    mMediaStreamingManager.startStreaming();
    

## 6.5 手动对焦

对焦之前传入 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());
    

## 6.6 Zoom

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();
    

## 6.7 闪光灯操作

开启闪光灯。

    
    
    mMediaStreamingManager.turnLightOn();
    

关闭闪光灯。

    
    
    mMediaStreamingManager.turnLightOff();
    

## 6.8 切换摄像头

切换摄像头。

    
    
    mMediaStreamingManager.switchCamera();
    

## 6.9 获取采集帧率范围

获取 Camera 设置的采集帧率范围。

    
    
    mMediaStreamingManager.getPreviewFpsRange();
    

注意：

  * 该接口返回的是实际帧率乘以 1000 的帧率范围，如 [10000, 30000]，代表帧率范围为 10fps～30fps
  * 该接口需要在收到 OnStateChanged.READY 状态后调用，否则将会返回 null

## 6.10 禁音推流

在推流过程中，将声音禁用掉：

    
    
    mMediaStreamingManager.mute(true);
    

恢复声音：

    
    
    mMediaStreamingManager.mute(false);
    

注：默认为 false

## 6.11 截帧

在 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。

## 6.12 停止推流

停止当前推流。

    
    
    mMediaStreamingManager.stopStreaming();
    

## 6.13 Log 管理

当 enabled 设置为 true ，SDK Native 层的 log 将会被打开；当设置为 false，SDK Native 层的 log
将会被关闭。默认处于打开状态。

    
    
    mMediaStreamingManager.setNativeLoggingEnabled(false);
    

注：默认值为 true。建议 Release 版本置为 false。

## 6.14 pause

退出 MediaStreamingManager，该操作会主动断开当前的流链接，并关闭 Camera 和释放相应的资源。

    
    
    mMediaStreamingManager.pause();
    

## 6.15 destroy

释放不紧要资源。

    
    
    mMediaStreamingManager.destroy();
    

## 6.16 自定义音频数据处理

用户可以通过下面回调接口，获取当前音频数据，实现自定义音频数据处理。

    
    
    // 注册音频采集数据回调
    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);
    }
    

## 6.17 动态水印

通过调用 `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);
    

## 6.18 发送 SEI 信息

可以通过调用如下接口进行 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

## 6.19 动态改变预览镜像

若希望在推流过程中动态改变摄像头预览的镜像效果，可以使用如下 API：

    
    
    mMediaStreamingManager.setPreviewMirror(boolean mirror)
    

## 6.20 动态改变推流镜像

若希望在推流过程中动态改变推流的镜像效果，可以使用如下 API：

    
    
    mMediaStreamingManager.setEncodingMirror(boolean mirror)
    

## 6.21 推流时增加背景音乐

若希望在推流过程中增加背景音乐，可以使用如下 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 !!!";
    

## 6.22 返听／耳返功能

若希望在推流过程中开启返听（耳返），可以使用如下 API：

    
    
     mMediaStreamingManager.startPlayback();
     mMediaStreamingManager.stopPlayback();
    

## 6.23 QUIC 推流

QUIC 是基于 UDP 开发的可靠传输协议，在弱网下拥有更好的推流效果，相比于 TCP 拥有更低的延迟，可抵抗更高的丢包率。

  * 通过下面接口开启/关闭 QUIC 推流

    
    
    mProfile.setQuicEnable(quicEnable);
    

## 6.24 SRT 推流

SRT 协议是基于 UDT 的传输协议，抗丢包能力强，适用于复杂的网络。相比于 TCP 可抵抗更高的丢包率。

  * 通过下面接口开启/关闭 SRT 推流

    
    
    mProfile.setSrtEnabled(enabled);
    

## 6.25 图片推流

若希望在推流过程中推送图片，可以通过如下方式实现：

### 设置推流的图片

设置推流图片有两种方式：

  * 推流前预设图片

推流前可以通过 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();
    

# 7 自定义滤镜

## 7.1 软编模式滤镜实现

需要分别处理预览显示的 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。

## 7.2 硬编模式滤镜实现

硬编模式下仅需要实现 SurfaceTextureCallback interface 就可实现预览显示和 streaming。

# 8 录屏

PLDroidMediaStreaming 封装好了录屏相关的底层操作，用户可以非常方便的进行录屏推流。其步骤如下：

## 8.1 AndroidManifest.xml 修改

注册 SDK 中携带的 ScreenCaptureRequestActivity

    
    
    <activity
        android:name="com.qiniu.pili.droid.streaming.screen.ScreenCaptureRequestActivity"
        android:theme="@android:style/Theme.Translucent.NoTitleBar" >
    </activity>
    

**注意：** 如果你的项目 targetVersion 大于等于 29 ，那么你还需要额外注册一个 ScreenRecordService

    
    
    <service
        android:name="com.qiniu.pili.droid.streaming.screen.ScreenRecordService"
        android:enabled="true"
        android:foregroundServiceType="mediaProjection" />
    

## 8.2 构造核心类 `ScreenStreamingManager`

`ScreenStreamingManager` 封装了屏幕的录制、音频的采集，编码，封包和推流操作。用户只需要简单的调用相关 API 即可实现录屏推流：

    
    
    // 构造 ScreenStreamingManager
    ScreenStreamingManager screenStreamingManager = new ScreenStreamingManager();
    
    // 配置相关参数
    screenStreamingManager.prepare(context, screenSetting, null, streamingProfile);
    
    // 开始推流
    screenStreamingManager.startStreaming();
    
    // 停止推流
    screenStreamingManager.stopStreaming();
    
    // 销毁
    screenStreamingManager.destroy();
    

**注意：**如果项目 targetVersion >= 29 ，则 startStreaming() 应该运行在子线程，在开始录屏之后 SDK
内部会创建一个默认的 Notification ，如果您需要使用自定义 Notification，那么请在开始推流之前调用如下代码：

    
    
    screenStreamingManager.setNotification(customNotificationId, customNotification);
    

如果后续想要更新此 Notification ，那么可以在更改自定义 notification 对象后调用如下代码即可：

    
    
    screenStreamingManager.notifyNotification()
    

## 8.3 自定义音频数据处理

用户可以通过下面回调接口，获取当前音频数据，实现自定义音频数据处理。

    
    
    // 注册音频采集数据回调
    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);
    }
    

# 9 StreamingManager

`StreamingManager` 是类似 MediaStreamingManager 的一个类，两者的区别是：StreamingManager
不带采集，仅包含编码、封包推流模块，从功能层面可以理解为：  
MediaStreamingManager = 采集模块 + (处理模块) + StreamingManager

其调用过程类似于 MediaStreamingManager：

    
    
    构造 StreamingManager -> prepare -> resume -> startStreaming -> inputAudioFrame/inputVideoFrame -> stopStreaming -> pause -> destroy
    

具体可以参考 Demo 中的 [ImportStreamingActivity.java __](https://github.com/pili-
engineering/PLDroidMediaStreaming/blob/master/PLDroidMediaStreamingDemo/app/src/main/java/com/qiniu/pili/droid/streaming/demo/activity/ImportStreamingActivity.java)

PS：若希望使用自己已有项目中的采集／处理模块，可以选用 StreamingManager.

## 9.1 外部输入音频

    
    
    void inputAudioFrame(ByteBuffer buffer, int size, long tsInNanoTime, boolean isEof);
    void inputAudioFrame(byte[] buffer, long tsInNanoTime, boolean isEof);
    

可以选择传入 ByteBuffer 或者 byte[] 类型的 PCM 源数据，传入的时间戳为 nano time。

## 9.2 外部输入视频

    
    
    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 对应的时间戳，单位为纳秒

## 9.3 开始推流

通过调用如下接口开始推流：

    
    
    mStreamingManager.startStreaming();
    

## 9.4 停止推流

通过调用如下接口停止推流：

    
    
    mStreamingManager.stopStreaming();
    

## 9.5 getInputSurface

`getInputSurface` 须在 `startStreaming` 被调用成功之后，从 `MediaCodec` 获取其 `Surface` 类型的
InputSurface，用户可以在这个 `Surface` 上面进行自定义绘制，绘制好后使用 `frameAvailable` 通知 SDK 进行编码。

若希望使用该高级功能，需在 `AVCodecType.HW_VIDEO_SURFACE_AS_INPUT_WITH_HW_AUDIO_CODEC`
模式下，否则会抛出异常。

## 9.6 发送 SEI 信息

通过调用如下接口进行 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)
    

以上内容是否对您有帮助？