前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TRTC 视频旋转场景方案

TRTC 视频旋转场景方案

原创
作者头像
erickyan
发布2022-04-12 15:27:15
1.7K0
发布2022-04-12 15:27:15
举报
文章被收录于专栏:腾讯云终端专家服务

场景介绍

在网络会议、双人视频通话等场景时,将手机横屏、竖屏放置场景下,实现本地和远端都可以看到正常的画面效果。

效果演示

当左边手机进行旋转时,即进行横屏推流,右边手机的小画面订阅到的远端流,动态调整view进行适配,避免出现黑边;

当两端手机都进行旋转时,两端都进行横屏推流,各自订阅的远端流画面进行动态调整view;

无论如何旋转手机,两端看到的画面都是正的。

(大画面:本地摄像头; 小画面:远端流)

视频旋转效果演示

实现逻辑

推流端

1)开启 SDK 重力感应,默认就是开启的,如果关闭了,请调用接口打开 SDK 重力感应

2)监听手机旋转角度

3)根据不同的旋转角度,设置视频编码参数,即横屏/竖屏编码

4)发送 SEI 消息,告知房间内其他用户,当前是横屏还是竖屏

5)根据不同的旋转角度,旋转自己订阅的远端流的画面

6)根据不同的旋转角度,来调整 activity 为横屏或竖屏

拉流端

1)收到远端用户的第一帧视频,根据宽高数据,调整渲染远端流的 view 宽高,避免小窗口出现黑边

2)当推流端旋转手机时,可以收到发送的 SEI 消息,根据传递过来的横屏或竖屏,调整渲染远端流的 view 宽高,避免出现黑边

代码逻辑

1)TRTC SDK 版本号

这里可以指定使用 9.5.11347 版本的 SDK,体验下效果。

SDK 发布日志历史:参考文档

代码语言:javascript
复制
com.tencent.liteav:LiteAVSDK_TRTC:9.5.11347

2)修改清单文件

android:configChanges 避免重新启动 activity

代码语言:javascript
复制
<activity 
    android:name="com.tencent.trtc.videocall.VideoCallingActivity"
    android:configChanges="orientation|keyboard|layoutDirection|screenSize"
    android:screenOrientation="portrait"
    />

3)开启重力感应

setGSensorMode 设置重力感应接口,默认开启,默认值为 TRTCGSensorMode_UIAutoLayout

代码语言:javascript
复制
mTRTCCloud.setGSensorMode(TRTCCloudDef.TRTC_GSENSOR_MODE_UIAUTOLAYOUT);

4)监听手机的旋转

当手机旋转时,如:90度、180度、270度时,需要将 activity 调整设置对应的 横屏或竖屏

使用 OrientationEventListener 实时监听手机的旋转角度

代码语言:javascript
复制
/**
 * 获取到手机旋转的角度,取四个方向:0、90、180、270度
 * @param orientation 0、90、180、270度
 */
@Override
public void onOrientationChanged(int orientation) {
    // 手机平放,获取不到旋转角度
    if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
        return;
    }

    // 获取手机旋转角度 : 0
    if (orientation > 350 || orientation < 10) {
        // 在当前旋转角度下,仅调用一次,切换角度后再去调用
        if (!isCall(0)) {
            // 根据旋转的角度,调整推流编码、发送SEI、调整渲染的view、activity方向
            adjustVideoEnc(0);
        }
    // 获取手机旋转角度 : 90
    } else if (orientation > 80 &amp;&amp; orientation < 100) {
        // 在当前旋转角度下,仅调用一次,切换角度后再去调用
        if (!isCall(90)) {
            // 根据旋转的角度,调整推流编码、发送SEI、调整渲染的view、activity方向
            adjustVideoEnc(90);
        }
    // 获取手机旋转角度 : 180
    } else if (orientation > 170 &amp;&amp; orientation < 190) {
        // 在当前旋转角度下,仅调用一次,切换角度后再去调用
        if (!isCall(180)) {
            // 根据旋转的角度,调整推流编码、发送SEI、调整渲染的view、activity方向
            adjustVideoEnc(180);
        }
    // 获取手机旋转角度 : 270
    } else if (orientation > 260 &amp;&amp; orientation < 280) {
        // 在当前旋转角度下,仅调用一次,切换角度后再去调用
        if (!isCall(270)) {
            // 根据旋转的角度,调整推流编码、发送SEI、调整渲染的view、activity方向
            adjustVideoEnc(270);
        }
    }
}

手机的旋转角度事件,非常频繁,避免重复调用,需要控制下请求:

(这里仅是示例,根据实际需要进行调整逻辑!)

代码语言:javascript
复制
/**
 * 在指定旋转角度,是否已经操作了,避免连续在同一个旋转角度操作多次
 * @param mOrientation 0、90、180、270度
 * @return true: 之前一次已经操作过了,不要再次操作了;  false:之前一次没操作过,你去操作吧
 */
public boolean isCall(int mOrientation) {
    Boolean flag = mRotationCall.get(mOrientation);
    if (flag == null) {
        flag = false;
    }

    switch (mOrientation) {
        case 0:
            mRotationCall.put(0, true);
            mRotationCall.put(90, false);
            mRotationCall.put(180, false);
            mRotationCall.put(270, false);
            break;
        case 90:
            mRotationCall.put(0, false);
            mRotationCall.put(90, true);
            mRotationCall.put(180, false);
            mRotationCall.put(270, false);
            break;
        case 180:
            mRotationCall.put(0, false);
            mRotationCall.put(90, false);
            mRotationCall.put(180, true);
            mRotationCall.put(270, false);
            break;
        case 270:
            mRotationCall.put(0, false);
            mRotationCall.put(90, false);
            mRotationCall.put(180, false);
            mRotationCall.put(270, true);
            break;
        default:
            break;
    }
    return flag;
}

5)根据不同的旋转角度,调整编码参数、发送SEI消息、旋转远端用户的画面、activity横竖屏

代码语言:javascript
复制
/**
 * 1)推到远端流的编码参数
 *    1.1)如果是竖屏,就设置竖屏的编码参数
 *    1.2)如果是横屏,就设置横屏的编码参数
 * 2)发送 SEI 消息
 *    1.1)如果当前是横屏推流,就告诉房间内其他人,我当前在推横屏
 *        1.1.1)房间内其他用户收到后,调整对应用户的 view 的宽高比,避免出现黑边
 *    1.2)如果当前是竖屏推流,就告诉房间内其他人,我当前在推竖屏
 *        1.1.1)房间内其他用户收到后,调整对应用户的 view 的宽高比,避免出现黑边
 * 3)旋转远端用户的画面
 *    3.1)我当前拉取到了其他人的流,我当前切换成 横屏/竖屏 ,需要旋转远端用户的画面,避免方向不一致
 * 4)设置当前 activity 横屏 或 竖屏
 * @param mOrientation 旋转的角度,如:0 90 180 270
 */
public void adjustVideoEnc(int mOrientation) {
    // 本地画面旋转角度设置 0
    TRTCCloudDef.TRTCRenderParams params = new TRTCCloudDef.TRTCRenderParams();
    params.rotation = TRTC_VIDEO_ROTATION_0;
    mTRTCCloud.setLocalRenderParams(params);

    switch (mOrientation) {
        case 0:
            Log.d(TAG, "旋转角度 0");
            // 1)设置推到远端的编码参数
            TRTCCloudDef.TRTCVideoEncParam param = new TRTCCloudDef.TRTCVideoEncParam();
            param.videoResolutionMode = TRTC_VIDEO_RESOLUTION_MODE_PORTRAIT; // 竖屏
            mTRTCCloud.setVideoEncoderParam(param);

            // 2)发送消息给房间内其它用户,说自己调整了横竖屏
            JSONObject buildInfoJson = new JSONObject();
            try {
                buildInfoJson.put("TRTC_VIDEO_RESOLUTION_MODE", "PORTRAIT");
            } catch (Exception e) {
            }
            mTRTCCloud.sendSEIMsg(buildInfoJson.toString().getBytes(), 1);

            // 3)旋转订阅的远端画面
            for (String uid : mRemoteUidList){
                TRTCCloudDef.TRTCRenderParams renderParams = new TRTCCloudDef.TRTCRenderParams();
                renderParams.rotation = TRTCCloudDef.TRTC_VIDEO_ROTATION_0;
                renderParams.fillMode = TRTCCloudDef.TRTC_VIDEO_RENDER_MODE_FIT;  // 默认值fill,这里手动指定fit
                mTRTCCloud.setRemoteRenderParams(uid, TRTC_VIDEO_STREAM_TYPE_BIG, renderParams);
            }

            // 4)设置 activity 竖屏
            VideoCallingActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            break;
        case 90:
            Log.d(TAG, "旋转角度 90");
            // 1)设置推到远端的编码参数
            param = new TRTCCloudDef.TRTCVideoEncParam();
            param.videoResolutionMode = TRTC_VIDEO_RESOLUTION_MODE_LANDSCAPE; // 横屏
            mTRTCCloud.setVideoEncoderParam(param);

            // 2)发送消息给房间内其它用户,说自己调整了横竖屏
            buildInfoJson = new JSONObject();
            try {
                buildInfoJson.put("TRTC_VIDEO_RESOLUTION_MODE", "LANDSCAPE");
            } catch (Exception e) {
            }
            mTRTCCloud.sendSEIMsg(buildInfoJson.toString().getBytes(), 1);

            // 3)旋转订阅的远端画面
            for (String uid : mRemoteUidList){
                TRTCCloudDef.TRTCRenderParams renderParams = new TRTCCloudDef.TRTCRenderParams();
                renderParams.rotation = TRTCCloudDef.TRTC_VIDEO_ROTATION_180;
                renderParams.fillMode = TRTCCloudDef.TRTC_VIDEO_RENDER_MODE_FIT;  // 默认值fill,这里手动指定fit
                mTRTCCloud.setRemoteRenderParams(uid, TRTC_VIDEO_STREAM_TYPE_BIG, renderParams);
            }

            // 4)设置 activity 横屏
            VideoCallingActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            break;
        case 180:
            Log.d(TAG, "旋转角度 180");
            // 1)设置推到远端的编码参数
            param = new TRTCCloudDef.TRTCVideoEncParam();
            param.videoResolutionMode = TRTC_VIDEO_RESOLUTION_MODE_PORTRAIT; // 竖屏
            mTRTCCloud.setVideoEncoderParam(param);

            // 2)发送消息给房间内其它用户,说自己调整了横竖屏
            buildInfoJson = new JSONObject();
            try {
                buildInfoJson.put("TRTC_VIDEO_RESOLUTION_MODE", "PORTRAIT");
            } catch (Exception e) {
            }
            mTRTCCloud.sendSEIMsg(buildInfoJson.toString().getBytes(), 1);

            // 3)旋转订阅的远端画面
            for (String uid : mRemoteUidList){
                TRTCCloudDef.TRTCRenderParams renderParams = new TRTCCloudDef.TRTCRenderParams();
                renderParams.rotation = TRTCCloudDef.TRTC_VIDEO_ROTATION_180;
                renderParams.fillMode = TRTCCloudDef.TRTC_VIDEO_RENDER_MODE_FIT;  // 默认值fill,这里手动指定fit
                mTRTCCloud.setRemoteRenderParams(uid, TRTC_VIDEO_STREAM_TYPE_BIG, renderParams);
            }

            // 4)设置 activity 竖屏
            VideoCallingActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            break;
        case 270:
            Log.d(TAG, "旋转角度 270");
            // 1)设置推到远端的编码参数
            param = new TRTCCloudDef.TRTCVideoEncParam();
            param.videoResolutionMode = TRTC_VIDEO_RESOLUTION_MODE_LANDSCAPE; // 横屏
            mTRTCCloud.setVideoEncoderParam(param);

            // 2)发送消息给房间内其它用户,说自己调整了横竖屏
            buildInfoJson = new JSONObject();
            try {
                buildInfoJson.put("TRTC_VIDEO_RESOLUTION_MODE", "LANDSCAPE");
            } catch (Exception e) {
            }
            mTRTCCloud.sendSEIMsg(buildInfoJson.toString().getBytes(), 1);

            // 3)旋转订阅的远端画面
            for (String uid : mRemoteUidList){
                TRTCCloudDef.TRTCRenderParams renderParams = new TRTCCloudDef.TRTCRenderParams();
                renderParams.rotation = TRTCCloudDef.TRTC_VIDEO_ROTATION_0;
                renderParams.fillMode = TRTCCloudDef.TRTC_VIDEO_RENDER_MODE_FIT;  // 默认值fill,这里手动指定fit
                mTRTCCloud.setRemoteRenderParams(uid, TRTC_VIDEO_STREAM_TYPE_BIG, renderParams);
            }

            // 4)设置 activity 横屏
            VideoCallingActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            break;
        default:
            break;
    }
}

6)收到SEI消息、首帧视频

代码语言:javascript
复制
/***
 * 收到元旦用户告知,远端用户切换了横屏/竖屏,赶紧调整下 view 的窗口大小
 * @param userId 发送消息的用户
 * @param data 发送的数据
 */
@Override
public void onRecvSEIMsg(String userId, byte[] data) {
    // 解析数据
    Map<String, Object> objectMap = new Gson().fromJson(new String(data), Map.class);
    Object mode = objectMap.get("TRTC_VIDEO_RESOLUTION_MODE");
    if (mode instanceof String) {
        // 横屏
        if (mode.equals("LANDSCAPE")) {
            // 当前收到远端用户告知为横屏,调整该远端用户 view 的大小,以免出现黑边
            // 设置传入的宽高,仅表示 宽 > 高
            adjustRemoteViewResolutionMode(userId, 1, 0);
        // 竖屏
        } else if (mode.equals("PORTRAIT")) {
            // 当前收到远端用户告知为竖屏,调整该远端用户 view 的大小,以免出现黑边
            // 设置传入的宽高,仅表示 宽 < 高
            adjustRemoteViewResolutionMode(userId, 0, 1);
        }
    }
}

/***
 * 收到用户的第一帧视频画面
 * 如果 userId 为空值,代表 SDK 已经开始渲染自己本地的视频画面
 * 如果 userId 不为空,代表 SDK 已经开始渲染远端用户的视频画面
 * @param userId 哪个用户的视频首帧
 * @param streamType 流类型
 * @param width 宽
 * @param height 高
 */
@Override
public void onFirstVideoFrame(String userId, int streamType, int width, int height) {
    /**
     * 不为空,即为远端用户的,也仅仅调整远端用户的 view
     */
    if (userId != null) {
        // 根据首帧收到的宽高,来确定流的分辨率,以便调整 view 的宽高
        // 宽 > 高,说明要 view的 宽 > 高
        // 宽 < 高,说明要 view的 宽 < 高
        adjustRemoteViewResolutionMode(userId, width, height);
    }
}

代码语言:javascript
复制
/**
 * 调整远端 view 的大小
 * 当传入的 宽 > 高,说明是要横着展示远端的流,那么 view 的 宽 > 高 来设置
 *    1)如果 view 的 宽 > 高 , 就不做处理
 *    2)如果 view 的 宽 < 高 , 那就把 宽和高 对调下大小
 * 当传入的 宽 < 高,说明是要横着展示远端的流,那么 view 的 宽 < 高 来设置
 *    1)如果 view 的 宽 < 高 , 就不做处理
 *    2)如果 view 的 宽 > 高 , 那就把 宽和高 对调下大小
 * @param userId 对应的远端用户
 * @param width 宽度
 * @param height 高度
 */
private void adjustRemoteViewResolutionMode(String userId, int width, int height) {
    // 获取对应 userId 的下标
    int index = mRemoteUidList.indexOf(userId);
    // index 不等于 -1,说明存在该 userId
    if (index != -1) {
        // 根据 index 获取对应用户的 view
        TXCloudVideoView videoView = mRemoteViewList.get(index);
        // 获取该 userId 对应 view 的 LayoutParams
        ViewGroup.LayoutParams layoutParams = videoView.getLayoutParams();
        // 如果传入过来要求的 宽 > 高,即 横着
        if (width > height) {
            // 如果已经给它的 宽 < 高,那么就要调整他,使它的 宽 > 高,宽高对调即可
            if (layoutParams.width < layoutParams.height) {
                int tempWidth = layoutParams.width;
                layoutParams.width = layoutParams.height;
                layoutParams.height = tempWidth;
                videoView.setLayoutParams(layoutParams);
            }
        // 如果传入过来要求的 宽 < 高,即 竖着
        } else if (width < height) {
            // 如果已经给它的 宽 > 高,那么就要调整他,使它的 宽 < 高,宽高对调即可
            if (layoutParams.width > layoutParams.height) {
                int tempWidth = layoutParams.width;
                layoutParams.width = layoutParams.height;
                layoutParams.height = tempWidth;
                videoView.setLayoutParams(layoutParams);
            }
        }
    }
}

代码示例

当运行demo时,记得修改 GenerateTestUserSig 文件中的 SDKAPPID、SECRETKEY,才能正常运行起来。

文件:Debug\src\main\java\com\tencent\trtc\debug\GenerateTestUserSig.java

控制台上获取对应的应用信息
控制台上获取对应的应用信息

演示示例代码:如附件

参考文档

视频画面旋转和缩放:https://cloud.tencent.com/document/product/647/32237

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 场景介绍
  • 效果演示
  • 实现逻辑
    • 推流端
      • 拉流端
      • 代码逻辑
        • 1)TRTC SDK 版本号
          • 2)修改清单文件
            • 3)开启重力感应
              • 4)监听手机的旋转
                • 5)根据不同的旋转角度,调整编码参数、发送SEI消息、旋转远端用户的画面、activity横竖屏
                  • 6)收到SEI消息、首帧视频
                  • 代码示例
                  • 参考文档
                  相关产品与服务
                  实时音视频
                  实时音视频(Tencent RTC)基于腾讯21年来在网络与音视频技术上的深度积累,以多人音视频通话和低延时互动直播两大场景化方案,通过腾讯云服务向开发者开放,致力于帮助开发者快速搭建低成本、低延时、高品质的音视频互动解决方案。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档