推流准备

  1. 注册微吼开发者账号

  2. 创建应用 获取AppID

  3. 设置应用包名、签名

  4. 创建直播间 获取roomId

    可以通过 【测试工具】 或 【API】 获得

  5. 获取直播间包含推流权限的access_token

    可以通过 【测试工具】 或 【API】 获得

工程配置

在开始使用sdk之前,我们要配置好IDE和创建基础的工程代码。相关内容在【工程配置】中有详细说明。

LSS And VOD

时序图

注:已知直播间ID的情况下虚线框部分无需请求。

代码对接

1 VHLivePusher

1.1 VHLivePushConfig 参数类

VHLivePushConfig是推流功能的配置类,也是推流的核心类之一,这个类决定了你推流时一些必备的参数,如视频流的宽高、帧率、码率、编码类型、推流类型等。传递一个分辨率并创建VHLivePushConfig类 。 分辨率定义如下:

推流分辨率定义 描述
VHLivePushFormat.PUSH_MODE_SD 低清分辨率 352 * 288
VHLivePushFormat.PUSH_MODE_HD 普清分辨率 640 * 480
VHLivePushFormat.PUSH_MODE_XHD 高清分辨率 960 * 540
VHLivePushFormat.PUSH_MODE_XXHD 超清分辨率 1280 * 720
/**
 * 初始化配置类
 * 其中有很多参数有默认项配置。
 */
config = new VHLivePushConfig(VHLivePushFormat.PUSH_MODE_HD);
/**
 * 竖屏发起
 */
config.screenOri = VHLivePushFormat.SCREEN_ORI_PORTRAIT

VHLivePushConfig 参数配置 以下是VHLivePushConfig的默认配置,如果需要修改,只需要调用相应的参数重新赋值即可

VHLivePushConfig参数 默认值 描述
screenOri VHLivePushFormat.SCREEN_ORI_LANDSPACE 推流横竖屏,默认横屏
videoFrameRate 15 视频帧率
videoBitrate 400 * 1000 视频码率
pushTimeout 5 * 1000 推流超时
pushReconnectTimes 5 推流重连次数
encodeColorFormat VHLivePushFormat.ENCODE_COLOR_FORMAT_YUV420SP_NV21 采集的颜色空间
streamMode VHLivePushFormat.STREAM_MODE_RTMP 推流协议
encodeType VHLivePushFormat.ENCODE_TYPE_SOFT 编码方式

1.2 初始化音视频采集器

VHVideoCaptureView 是SDK提供的一个视频采集View,VHVideoCaptureView主要操作Camera,完成推流的采集工作和对摄像头的Api调用。需要先在XML中引用View,听过FindView获取采集器的实例。VHAudioCapture 是SDK提供的音频采集类,直接初始化调用

/**
 * 初始化视频采集器
 */
videoCapture = (VHVideoCaptureView) this.findViewById(R.id.videoCaptureView);
/**
 * 视频采集器的XML
 */
<com.vhall.lss.push.VHVideoCaptureView
        android:id="@+id/videoCaptureView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
</com.vhall.lss.push.VHVideoCaptureView>
/**
 * 初始化音频采集器
 */
audioCapture = new VHAudioCapture();

1.3 初始化VHLivePusher

VHLivePusher 是发起推流的核心对象,创建时传递的参数是,视频采集器,音频采集器和VHLivePushConfig,其实就是将1.1到1.3创建对象的实例传入进去。添加监听也在此时配置,监听的描述请看,到这里初始化的操作就完成了

/**
 * 初始化直播器
 * @param IVHCapture videoCapture 视频采集器
 * @param IVHCapture audioCapture 音频采集器
 * @param VHLivePushConfig config 直播参数配置项
 */
VHLivePusher pusher = new VHLivePusher(videoCapture, audioCapture, config);

/**
 * 添加监听事件
 * 具体参考本文的事件监听
 * @ VHPushLiveListener 需要实现的回调
 */
pusher.addListener(new MyListener());

当初始化工作完成后,就可以调用API的功能了

2: 开始推流

当一切就绪时,调用start()开始推流,此方法需要传入房间号roomId和有效的Token,这些参数在调用Api前获取,调用Start()后,如果想要了解推流的状态可以从监听中获取


 /**
 * 初始化直播器成功后调用此方法发起直播
 * 开始直播,传递在准备工作中配置的roomId(房间ID)和AccessToken;
 * @param String roomId; // 房间ID , 通过调用 api 创建
 * @param String accessToken; // 发起的Token  通过调用 api 创建
 */
pubic void start(String roomId ,String accessToken);

3: 切换前置或后置摄像头

顺序切换下一个摄像头,返回摄像头ID , 默认使用后置摄像头(注意 一定要将摄像头的权限打开)

/**
 * 切换摄像头
 * 此方法属于视频采集器 VHVideoCaptureView
 * @return int cameraID 返回摄像头ID
 */
pubic int switchCamera();

4: 打开指定哪个摄像头

返回摄像头ID (注意 一定要将摄像头的权限打开)

/**
 * 打开指定的摄像头
 * 此方法属于视频采集器 VHVideoCaptureView
 */
pubic int changeCamera(int cameraId);

5: 切换闪光灯

切换闪光灯 , 只有在后置的摄像头才能打开闪光灯

/**
 * 切换闪光灯
 * 此方法属于视频采集器 VHVideoCaptureView
 * @param boolean isOpen; //是否打开闪光灯
 */
pubic boolean changeFlash(boolean flash);

6: 获取当前直播状态

获取直播时的状态,也可以根据监听获取状态

状态 描述
IDEL 闲置状态 ,默认状态
STARTED 运行状态
STOPED 停止状态
END 结束状态
/**
 * 获取直播的状态
 * 此方法属于直播器 VHLivePusher
 */
pubic Status getStatus();
enum Status {
	IDEL,STARTED, STOPED, END
}

7: 暂停直播 / 恢复直播

调用后推流会暂停,采集工作也会停止,根据应用场景调用 ,调用后推流重新连接,采集工作重新开始,根据应用场景调用

/**
 * 暂停直播 / 恢复直播
 * 此方法属于直播器 VHLivePusher
 */
pubic void pause();
pubic void resume();

8: 设置摄像头变焦

/**
 * 个别手机支不支持摄像头变焦。
 * 此方法属于视频采集器 VHVideoCaptureView
 * @param int level (0~100)
 */
pubic void setZoom(int level);

9: 设置view内手势是否可用

videoCaptureView内部实现了手势聚焦和变焦,如果不需要手势,可设置false,自行调用focus和zoom接口

/**
 * 设置手势是否可用
 * 此方法属于视频采集器 VHVideoCaptureView
 */
pubic void setGestureEnable(boolean enable);

10: 设置摄像头预览模式

可通过 setCameraDrawMode(int mode) 接口来设置画面预览模式,具体的模式参考下列表格

预览模式 参数
VHLivePushFormat.DRAW_MODE_ASPECTFILL 将图像等比例铺满整个屏幕,多余部分裁剪掉,此模式下画面不会留黑边,但可能因为部分区域被裁剪而显示不全
VHLivePushFormat.DRAW_MODE_ASPECTFIT 将图像等比例缩放,适配最长边,缩放后的宽和高都不会超过显示区域,居中显示,画面可能会留有黑边
VHLivePushFormat.DRAW_MODE_NONE 默认
``
/**
 * 摄像头预览模式
 * 此方法属于视频采集器 VHVideoCaptureView
 */
pubic void setCameraDrawMode(int mode);

11: 结束直播

当调用stop()方法时,内部的请求地址会置空,当重新发起推流会重新获取推流地址

/**
 * 结束直播
 * 此方法属于视频采集器 VHVideoCaptureView
 */
pubic void stop();

12:销毁

退出播放并释放相关资源 , 此方法会销毁当前已经初始化的视频采集器和音频采集器,如果需要再次使用需要重新初始化直播器

/**
 * 退出播放并释放相关资源。
 * 此方法属于视频采集器 VHVideoCaptureView
 */
pubic void release();

事件处理

在构建推流器的时候添加监听,用户根据监听回调的事件可以进行相应的处理,详细信息请查看Demo ,此处只做简单描述

错误码 描述
VHLivePusher.ERROR_PUSH 推送过程出错
VHLivePusher.ERROR_AUDIO_CAPTURE 音频采集过程出错
VHLivePusher.ERROR_VIDEO_CAPTURE 视频采集过程出错
直播事件 描述
VHLivePusher.EVENT_STATUS_STARTING 开始推流
VHLivePusher.EVENT_STATUS_STARTED 推流成功
VHLivePusher.EVENT_STATUS_STOPED 推流停止
VHLivePusher.EVENT_UPLOAD_SPEED 下载速度
class MyListener implements VHPushLiveListener {
        @Override
        public void onError(int errorCode, int innerErrorCode) {
            switch (errorCode) {
                case VHLivePusher.ERROR_PUSH://推送过程出错
                    break;
                case VHLivePusher.ERROR_AUDIO_CAPTURE://音频采集过程出错
                    break;
                case VHLivePusher.ERROR_VIDEO_CAPTURE://视频采集过程出错
                    break;
            }
        }

        @Override
        public void onEvent(int eventCode, String eventMsg) {
            switch (eventCode) {
                case VHLivePusher.EVENT_STATUS_STARTING:
                    break;
                case VHLivePusher.EVENT_STATUS_STARTED:
                    break;
                case VHLivePusher.EVENT_STATUS_STOPED:
                    break;
                case VHLivePusher.EVENT_UPLOAD_SPEED:
                    break;
            }
        }
    }

滤镜美颜直播

第一步 创建美颜滤镜 VHBeautyFilter
第二步 设置给VHVideoCaptureView
第三步 设置美颜等级

    if (beautyFilter == null)
        beautyFilter = new VHBeautyFilter();
    videoCapture.setFilter(beautyFilter);
	<!- 设置美颜滤镜的等级 ->
	beautyFilter.setBeautyLevel(level) (1~5)
	
	<!--关闭滤镜-->
	videoCapture.setFilter(null);

Demo示例:

public class PushActivity extends Activity {

    VHVideoCaptureView videoCapture;
    IVHCapture audioCapture;
    VHLivePusher pusher;
    VHLivePushConfig config;
    //status info
    boolean isFlashOpen = false;
    int mCameraId = 0;
    boolean isAudioEnable = true;
    boolean isPushing = false;
    int mDrawMode = VHLivePushFormat.DRAW_MODE_NONE;
    //view
    TextView mSpeedView;
    ProgressBar mLoadingView;
    ImageView mPushBtn;

    private String roomId = "";
    private String accessToken = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        roomId = getIntent().getStringExtra("roomid");
        accessToken =getIntent().getStringExtra("token");
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setContentView(R.layout.push_layout);
        //initview
        mSpeedView = (TextView) this.findViewById(R.id.tv_speed);
        mLoadingView = (ProgressBar) this.findViewById(R.id.pb_loading);
        mPushBtn = (ImageView) this.findViewById(R.id.btn_push);
        //配置发直播系列参数
        config = new VHLivePushConfig(VHLivePushFormat.PUSH_MODE_HD);
        config.screenOri = VHLivePushFormat.SCREEN_ORI_PORTRAIT;//横竖屏设置 重要
        //初始化音视频采集器
        videoCapture = (VHVideoCaptureView) this.findViewById(R.id.videoCaptureView);
        audioCapture = new VHAudioCapture();
        //初始化直播器
        pusher = new VHLivePusher(videoCapture, audioCapture, config);
        pusher.addListener(new MyListener());
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (pusher.getStatus() == IVHLivePusher.Status.STARTED) {
            pusher.pause();
            mPushBtn.setImageResource(R.mipmap.icon_start_bro);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (pusher.resumeAble())
            pusher.resume();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        pusher.release();
    }

    public void push(View view) {
        if (isPushing) {
            pusher.stop();
            mPushBtn.setImageResource(R.mipmap.icon_start_bro);
        } else {
            if (pusher.resumeAble())
                pusher.resume();
            else
                pusher.start(roomId, accessToken);
            mPushBtn.setImageResource(R.mipmap.icon_pause_bro);
        }

    }

    public void changeFlash(View view) {
        isFlashOpen = videoCapture.changeFlash(!isFlashOpen);
    }

    public void changeCamera(View view) {
        mCameraId = videoCapture.switchCamera();
    }

    public void changeFilter(View view) {
        int level = (++mBeautyLevel) % 6;
        if (level == 0)
            videoCapture.setFilter(null);
        else {
            videoCapture.setFilter(new VHBeautyFilter());
            videoCapture.setBeautyLevel(level);
        }
        Toast.makeText(this, "level:" + level, Toast.LENGTH_SHORT).show();
    }

    public void switchAudio(View view) {
        isAudioEnable = audioCapture.setEnable(!isAudioEnable);
    }

    public void changeMode(View view) {
        switch (mDrawMode) {
            case VHLivePushFormat.DRAW_MODE_NONE:
                mDrawMode = VHLivePushFormat.DRAW_MODE_ASPECTFILL;
                break;
            case VHLivePushFormat.DRAW_MODE_ASPECTFILL:
                mDrawMode = VHLivePushFormat.DRAW_MODE_ASPECTFIT;
                break;
            case VHLivePushFormat.DRAW_MODE_ASPECTFIT:
                mDrawMode = VHLivePushFormat.DRAW_MODE_NONE;
                break;
        }
        videoCapture.setCameraDrawMode(mDrawMode);
    }

    class MyListener implements VHPushLiveListener {

        @Override
        public void onError(int errorCode, int innerErrorCode) {
            mLoadingView.setVisibility(View.GONE);
            switch (errorCode) {
                case VHLivePusher.ERROR_PUSH://推送过程出错
                    break;
                case VHLivePusher.ERROR_AUDIO_CAPTURE://音频采集过程出错
                    break;
                case VHLivePusher.ERROR_VIDEO_CAPTURE://视频采集过程出错
                    break;
            }
            Toast.makeText(PushActivity.this, "push error,errorCode:" + errorCode + ",innerCode:" + innerErrorCode, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onEvent(int eventCode, String eventMsg) {
            switch (eventCode) {
                case VHLivePusher.EVENT_STATUS_STARTING:
                    mLoadingView.setVisibility(View.VISIBLE);
                    break;
                case VHLivePusher.EVENT_STATUS_STARTED:
                    mLoadingView.setVisibility(View.GONE);
                    isPushing = true;
                    break;
                case VHLivePusher.EVENT_STATUS_STOPED:
                    mLoadingView.setVisibility(View.GONE);
                    isPushing = false;
                    break;
                case VHLivePusher.EVENT_UPLOAD_SPEED:
                    mSpeedView.setText(eventMsg + "kbps");
                    break;
            }
        }
    }
}

其他相关文档

  1. 快速开始
  2. 如何集成观看直播
  3. 发起直播参考手册