如今,在国内移动互联网发展了几年的时间,移动开发技术也相对的成熟,在咱们日常使用的手机App中也少不了直播的功能,不管是娱乐类、游戏类、体育类还是教育类等的App都会有直播的功能,可以说直播的功能在一些商业应用或者非商业应用中都是不可或缺的功能。目前国内比较火直播App有如:斗鱼(游戏直播)、YY直播(全民娱乐直播)、虎牙(游戏+电竞直播)以及映客(娱乐直播)等直播。 而要想在自己的Android应用中实现直播的功能,那么就少不了对目前市面上直播推流SDK做一个技术的选型(如果有条件的公司可以自己开发一个直播推流平台,就可以不用第三方推流SDK),由于之前我们公司的项目需要用到直播推流SDK,所以笔者对目前市场的各大直播推流SDK颇有了解。所以下面我就简单的介绍如何在Android里接入腾讯云推流SDK实现直播推流的功能。(P.S.如果想了解阿里云推流SDK实现直播推流的功能的话可以看笔者之前写的这篇博客:https://blog.csdn.net/fukaimei/article/details/103237654 开通腾讯云直播服务的功能 绑定推流和拉流的域名 申请移动直播 SDK License 的 Key 值和 LicenseUrl 值 首先将下载的腾讯云 Android 推流 SDK 相关的 aar 文件拷贝到 AS 工程的 libs 目录下,如下图: 最后在清单文件AndroidManifest.xml中添加直播推流所需的相应权限 注意:直播推流在 Android 6.0 以上在相机界面预览前和发起直播推流之前必须在 Java 代码或者 Kotlin 代码里动态授予相机权限(Manifest.permission.CAMERA)和麦克风权限(Manifest.permission.RECORD_AUDIO),否则应用将会闪退。 在编码实现的这一环节,可以直接查看 腾讯云推流开发文档 来接入,也可以下载他们提供的Demo源码来接入。 视频云 SDK 中的 TXLivePlayer 模块负责实现直播播放功能,并使用 setPlayerView 接口将它与我们刚刚添加到界面上的 pusher_tx_cloud_view 控件进行关联。 在开始直播推流之前首先先校验 SDK 是否已经初始化了,接入校验 SDK License 是否有效,如果 mLivePusher.startPusher(pushUrl.trim()) 的返回值等于 -5 的话说明 License 校验失败,这时就到腾讯云直播平台检查 License 是否已经过期或者配置 Android 包名是否与自己在 AS 创建项目的包名是否一致。其中的 pushUrl 是直播的推流地址,直播的推流地址格式可以是 RTMP、FLV 或者 m3u8 格式,如果对直播推流时延要求比较高的话建议使用前两种格式。 停止直播推流调用了 TXLivePusher 的 stopPusher() 方法,如果当前有相机画面在预览的话则要调用 TXLivePusher 的 stopCameraPreview(true) 方法进行关闭。 切换前后置摄像头比较简单,只需一行代码调用 TXLivePusher 的 switchCamera() 方法就可以实现。 直播横屏或者竖屏推流采集到的画面预览在主播端可能看不到效果,但在观众端就能看到效果,比如主播设置为横屏推流时,那么观众端看到的画面是主播端看到的画面旋转 90 度。 打开网址(https://www.cutv.com/demo/live_test.swf)输入拉流地址观看测试推流是否成功,如果能观看到画面则是推流成功。画面如下: 可以扫描以下二维码进行下载安装,或者点击以下链接 https://app.fukaimei.top/tcpush 进行下载安装体验。 ———————— The end ———————— 码字不易,如果您觉得这篇博客写的比较好的话,可以赞赏一杯咖啡吧~~
前 言
)接入推流SDK前的准备工作
云直播的服务本质是一个广播的过程,类似于电视台的直播节目通过有线电视网发送给千家万户。为了完成这个过程,云直播需要有采集和推流设备(类似摄像头)、云直播服务(类似电视台的有线电视网)和播放设备(类似电视)。而采集和推流设备以及播放设备可以是手机、PC、Pad 等智能终端以及 Web 浏览器。
开通腾讯云直播服务地址如下:https://console.cloud.tencent.com/live/livestat?from=product-banner-use-lvb
绑定推流和拉流的域名的前提条件是自己的域名必须是已经备案好的域名才能绑定,然后选择 【域名管理】,单击【添加域名】添加您已备案的推流域名,详细请参见 添加自有域名。
SDK License 的 Key 值和 LicenseUrl 值是在 SDK 初始化时会使用到。详细申请流程请参见 License 使用指南。开始接入推流SDK
然后在AS的内层build.gradle里添加推流相关的依赖 android { ........ defaultConfig { ........ ndk { abiFilters 'armeabi-v7a', 'arm64-v8a' } } sourceSets { main { jniLibs.srcDir 'libs' } } repositories { flatDir { dirs 'libs' } } } dependencies { ........ implementation(name: 'LiteAVSDK_Smart', ext: 'aar') }
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_LOGS" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" /> <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
编码实现
为了能够展示播放器的视频画面,我们第一步要做的就是在布局 xml 文件里加入如下一段代码:<com.tencent.rtmp.ui.TXCloudVideoView android:id="@+id/pusher_tx_cloud_view" android:layout_width="match_parent" android:layout_height="match_parent" />
// 设置 SDK License String licenceURL = ShareUtils.getString(this, "LicenceURL", ""); String licenceKey = ShareUtils.getString(this, "LicenceKey", ""); TXLiveBase.getInstance().setLicence(this, licenceURL, licenceKey); mLivePushConfig = new TXLivePushConfig(); // 允许双指手势放大预览画面 mLivePushConfig.setEnableZoom(true); // 设置噪声抑制 mLivePushConfig.enableAEC(true); // 开启硬件加速 mLivePushConfig.setHardwareAcceleration(TXLiveConstants.ENCODE_VIDEO_HARDWARE); // 开启 MainProfile 硬编码模式 mLivePushConfig.enableVideoHardEncoderMainProfile(true); mLivePusher = new TXLivePusher(this); mLivePusher.setConfig(mLivePushConfig); mLivePusher.startCameraPreview(mPusherView);
获取推拉流拼接地址=推拉流域名+房间号+加密信息及有效时间戳的鉴权串信息,生成的推拉推工具代码如下:/** * 推流拉流地址拼接地址 */ public class PushUrlToken { public static String getUrlToken() { String pushKey = ShareUtils.getString(PushApplication.getInstance(), "PushKey", ""); String urlNO = ShareUtils.getString(PushApplication.getInstance(), "UrlNO", ""); String urlToken = getSafeUrl(pushKey, urlNO, dateToStamp()); return urlToken; } private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; /* * KEY+ streamName + txTime */ private static String getSafeUrl(String key, String streamName, long txTime) { String input = new StringBuilder(). append(key). append(streamName). append(Long.toHexString(txTime).toUpperCase()).toString(); String txSecret = null; try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); txSecret = byteArrayToHexString( messageDigest.digest(input.getBytes("UTF-8"))); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return txSecret == null ? "" : new StringBuilder(). append("txSecret="). append(txSecret). append("&"). append("txTime="). append(Long.toHexString(txTime).toUpperCase()). toString(); } private static String byteArrayToHexString(byte[] data) { char[] out = new char[data.length << 1]; for (int i = 0, j = 0; i < data.length; i++) { out[j++] = DIGITS_LOWER[(0xF0 & data[i]) >>> 4]; out[j++] = DIGITS_LOWER[0x0F & data[i]]; } return new String(out); } /** * 十位数的时间戳 * * @return * @throws ParseException */ private static long dateToStamp() { Long time = System.currentTimeMillis(); time += 30 * 1000 * 60; String res = ""; try { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = simpleDateFormat.parse(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time)); long ts = date.getTime(); ts = ts / 1000; res = String.valueOf(ts); } catch (Exception e) { e.printStackTrace(); } return Long.valueOf(res).longValue(); } }
/** * 开始推流 */ private void startPush() { if (isPush) { mLivePusher = new TXLivePusher(this); mLivePusher.setConfig(mLivePushConfig); mLivePusher.startCameraPreview(mPusherView); } int ret = mLivePusher.startPusher(pushUrl.trim()); L.i("ret:" + ret); if (ret == -5) { L.i("startRTMPPush: license 校验失败"); ToastUtil.showToastLong("startRTMPPush: license 校验失败"); } }
/** * 停止推流 */ private void stopPush() { mLivePusher.stopPusher(); // 如果已经启动了摄像头预览,请在结束推流时将其关闭 mLivePusher.stopCameraPreview(true); isPush = true; }
/** * 切换摄像头 */ private void switchCamera() { mLivePusher.switchCamera(); }
/** * 横竖屏推流切换 * * @param isPortrait */ private void onOrientationChange(boolean isPortrait) { if (isPortrait) { mLivePushConfig.setHomeOrientation(TXLiveConstants.VIDEO_ANGLE_HOME_DOWN); mLivePusher.setConfig(mLivePushConfig); mLivePusher.setRenderRotation(0); } else { mLivePushConfig.setHomeOrientation(TXLiveConstants.VIDEO_ANGLE_HOME_RIGHT); mLivePusher.setConfig(mLivePushConfig); // 因为采集旋转了,为了保证本地渲染是正的,则设置渲染角度为90度。 mLivePusher.setRenderRotation(90); } }
public class CameraPusherActivity extends BaseActivity { @BindView(R.id.pusher_tx_cloud_view) TXCloudVideoView mPusherView; @BindView(R.id.tv_play_url) TextView tvPlayUrl; private TXLivePusher mLivePusher; private TXLivePushConfig mLivePushConfig; // 推流url private String bsePushUrl; private String pushUrl; // 拉流url private String basePlayUrl; private String playUrl; private String urlNO; // 鉴权串信息 private String urlToken; // 是否已经推流 private boolean isPush; @Override protected int getLayoutId() { return R.layout.activity_camera_pusher; } @Override protected void initView() { ButterKnife.bind(this); // 如何获取License? 请参考官网指引 https://cloud.tencent.com/document/product/454/34750 String licenceURL = ShareUtils.getString(this, "LicenceURL", ""); String licenceKey = ShareUtils.getString(this, "LicenceKey", ""); TXLiveBase.getInstance().setLicence(this, licenceURL, licenceKey); mLivePushConfig = new TXLivePushConfig(); // 允许双指手势放大预览画面 mLivePushConfig.setEnableZoom(true); // 设置噪声抑制 mLivePushConfig.enableAEC(true); // 开启硬件加速 mLivePushConfig.setHardwareAcceleration(TXLiveConstants.ENCODE_VIDEO_HARDWARE); // 开启 MainProfile 硬编码模式 mLivePushConfig.enableVideoHardEncoderMainProfile(true); mLivePusher = new TXLivePusher(this); mLivePusher.setConfig(mLivePushConfig); mLivePusher.startCameraPreview(mPusherView); isPush = false; } @Override protected void initData() { bsePushUrl = ShareUtils.getString(this, "PushDomain", ""); basePlayUrl = ShareUtils.getString(this, "LiveDomain", ""); urlNO = ShareUtils.getString(this, "UrlNO", ""); urlToken = PushUrlToken.getUrlToken(); pushUrl = bsePushUrl + urlNO + "?" + urlToken; playUrl = basePlayUrl + urlNO + "?" + urlToken; L.i("拉流地址:" + playUrl); // ToastUtil.showToastLong("拉流地址:" + playUrl); } /** * 开始推流 */ private void startPush() { if (isPush) { mLivePusher = new TXLivePusher(this); mLivePusher.setConfig(mLivePushConfig); mLivePusher.startCameraPreview(mPusherView); } int ret = mLivePusher.startPusher(pushUrl.trim()); L.i("ret:" + ret); if (ret == -5) { L.i("startRTMPPush: license 校验失败"); ToastUtil.showToastLong("startRTMPPush: license 校验失败"); } tvPlayUrl.setText(String.format("拉流地址:%s%s", basePlayUrl, urlNO)); L.i(String.format("拉流地址:%s%s", basePlayUrl, urlNO)); } /** * 停止推流 */ private void stopPush() { mLivePusher.stopPusher(); // 如果已经启动了摄像头预览,请在结束推流时将其关闭 mLivePusher.stopCameraPreview(true); tvPlayUrl.setText(""); isPush = true; ToastUtil.showToast("已停止推流"); } /** * 切换摄像头 */ private void switchCamera() { mLivePusher.switchCamera(); ToastUtil.showToast("已切换摄像头"); } /** * 横竖屏推流切换 * * @param isPortrait */ private void onOrientationChange(boolean isPortrait) { if (isPortrait) { mLivePushConfig.setHomeOrientation(TXLiveConstants.VIDEO_ANGLE_HOME_DOWN); mLivePusher.setConfig(mLivePushConfig); mLivePusher.setRenderRotation(0); } else { mLivePushConfig.setHomeOrientation(TXLiveConstants.VIDEO_ANGLE_HOME_RIGHT); mLivePusher.setConfig(mLivePushConfig); // 因为采集旋转了,为了保证本地渲染是正的,则设置渲染角度为90度。 mLivePusher.setRenderRotation(90); } } @OnClick({R.id.btn_start_push, R.id.btn_stop_push, R.id.btn_switch_camera , R.id.btn_horizontal, R.id.btn_portrait}) public void onClick(View v) { switch (v.getId()) { case R.id.btn_start_push: ToastUtil.showToast("开始推流"); startPush(); break; case R.id.btn_stop_push: stopPush(); break; case R.id.btn_switch_camera: switchCamera(); break; case R.id.btn_horizontal: // 横屏推流 onOrientationChange(false); ToastUtil.showToast("已横屏推流"); break; case R.id.btn_portrait: // 竖屏推流 onOrientationChange(true); ToastUtil.showToast("已竖屏推流"); break; default: break; } } @Override protected void onDestroy() { stopPush(); ToastUtil.showToast("已停止推流"); super.onDestroy(); } }
界面运行效果图如下:
apk安装包下载体验地址:
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算