直播云

  • 功能使用

    最近更新时间:2018-11-16 16:25:52

    1 摄像头参数配置

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

    1.1 前置/后置摄像头配置

    前后置摄像头配置:

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

    注:默认 CAMERA_FACING_BACK

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

    1.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_PICTUREFOCUS_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

    1.3 Camera 预览 size

    为了兼容更多的 Camera ,SDK 采用了相关的措施:

    • 使用 PREVIEW_SIZE_LEVELPREVIEW_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_LEVELPREVIEW_SIZE_RATIO 分别是:

    // PREVIEW_SIZE_LEVEL
    - SMALL
    - MEDIUM
    - LARGE
    
    // PREVIEW_SIZE_RATIO
    - RATIO_4_3
    - RATIO_16_9
    

    需要注意的是:在某些机型上,list 可能为 null。

    1.4 Camera 预览 FPS

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

    1.5 RecordingHint

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

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

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

    1.6 Encoding Mirror

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

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

    1.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)
    

    2 麦克风参数配置

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

    2.1 是否开启蓝牙麦克风支持

    若希望增加蓝牙麦克风的支持,需要:

    mMicrophoneStreamingSetting.setBluetoothSCOEnabled(true);
    

    注:默认值为 false

    3 推流参数设置

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

    3.1 推流地址配置

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

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

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

    3.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

    3.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。

    3.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

    3.5 HappyDns 支持

    为了防止 Dns 被劫持,SDK 加入了 HappyDns 支持,可以从这里查阅源码。 从 v1.4.6 版本开始,需要在宿主项目中的 build.gradle 中加入如下语句:

    dependencies {
        ...
        compile 'com.qiniu:happy-dns:0.2.7'
        ...
    }
    

    通过 StreamingProfile 设定自定义 DnsManager,如下:

    public static DnsManager getMyDnsManager() {
          IResolver r0 = new DnspodFree();
          IResolver r1 = AndroidDnsServer.defaultResolver();
          IResolver r2 = null;
          try {
              r2 = 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()); // set your DnsManager
    

    若显示地设置为 null,即:

    mStreamingProfile.setDnsManager(null);
    

    SDK 会使用系统 DNS 解析,而不会使用 DnsManager 来进行 Dns 解析。

    若不调用 StreamingProfile#setDnsManager 方法,SDK 会默认的创建一个 DnsManager 来对 Dns 进行解析。

    3.6 软编的 EncoderRCModes

    目前 RC mode 支持的类型:

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

    可通过 StreamingProfile 的 setEncoderRCMode 方法进行设置,如下:

    mStreamingProfile.setEncoderRCMode(StreamingProfile.EncoderRCModes.QUALITY_PRIORITY);
    

    注:默认值为 EncoderRCModes.QUALITY_PRIORITY

    3.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

    3.8 StreamStatus 反馈配置

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

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

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

    3.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配置

    3.10 自适应码率

    由于无线网络相对于有线网络,可靠性较低,会经常遇到信号覆盖不佳导致的高丢包、高延时等问题,特别是在用网高峰期,由于带宽有限,网络拥塞的情况时有发生。自 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)

    3.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 水印设置

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

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

    4.2 水印显示大小

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

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

    watermarksetting.setCustomSize(int width, int height);
    

    4.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) {
    

    5 核心类 MediaStreamingManager

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

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

    5.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,
    
        /**
         * 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
    }
    

    5.3 resume

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

    mMediaStreamingManager.resume();
    

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

    5.4 开始推流

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

    mMediaStreamingManager.startStreaming();
    

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

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

    5.7 闪光灯操作

    开启闪光灯。

    mMediaStreamingManager.turnLightOn();
    

    关闭闪光灯。

    mMediaStreamingManager.turnLightOff();
    

    5.8 切换摄像头

    切换摄像头。

    mMediaStreamingManager.switchCamera();
    

    5.9 禁音推流

    在推流过程中,将声音禁用掉:

    mMediaStreamingManager.mute(true);
    

    恢复声音:

    mMediaStreamingManager.mute(false);
    

    注:默认为 false

    5.10 截帧

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

    5.11 停止推流

    停止当前推流。

    mMediaStreamingManager.stopStreaming();
    

    5.12 Log 管理

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

    mMediaStreamingManager.setNativeLoggingEnabled(false);
    

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

    5.13 pause

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

    mMediaStreamingManager.pause();
    

    5.14 destroy

    释放不紧要资源。

    mMediaStreamingManager.destroy();
    

    5.15 自定义音频数据处理

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

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

    5.16 动态水印

    通过调用 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 自定义滤镜

    6.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。

    6.2 硬编模式滤镜实现

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

    7 录屏

    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();
    
    // 配置相关参数
    screenStreamingManager.prepare(context, screenSetting, null, streamingProfile);
    
    // 开始推流
    screenStreamingManager.startStreaming();
    
    // 停止推流
    screenStreamingManager.stopStreaming();
    
    // 销毁
    screenStreamingManager.destroy();
    
    • 自定义音频数据处理

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

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

    8 StreamingManager

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

    其调用过程类似于 MediaStreamingManager:

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

    具体可以参考 Demo 中的 ImportStreamingActivity.java

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

    8.1 外部输入音频

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

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

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

    8.3 getInputSurface

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

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

    8.4 动态改变预览镜像

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

    mMediaStreamingManager.setPreviewMirror(boolean mirror)
    

    8.5 动态改变推流镜像

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

    mMediaStreamingManager.setEncodingMirror(boolean mirror)
    

    8.6 推流时增加背景音乐

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

    8.7 返听/耳返功能

    若希望在推流过程中开启返听(耳返),可以使用如下 API:

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

    8.8 QUIC 推流

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

    • 通过下面接口开启/关闭 QUIC 推流
    mProfile.setQuicEnable(quicEnable);
    
    以上内容是否对您有帮助?
  • Icon free helper
    Close