更新记录
1.1(2023-12-06) 下载此版本
解决安卓推流反复重连的bug
1.0(2023-07-27) 下载此版本
推流组件 播流组件
平台兼容性
Android | Android CPU类型 | iOS |
---|---|---|
适用版本区间:8.0 - 12.0 | armeabi-v7a:未测试,arm64-v8a:未测试,x86:未测试 | 适用版本区间:9 - 14 |
原生插件通用使用流程:
- 购买插件,选择该插件绑定的项目。
- 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
- 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
- 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
- 开发完毕后正式云打包
付费原生插件目前不支持离线打包。
Android 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/android
iOS 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/ios
注意事项:使用HBuilderX2.7.14以下版本,如果同一插件且同一appid下购买并绑定了多个包名,提交云打包界面提示包名绑定不一致时,需要在HBuilderX项目中manifest.json->“App原生插件配置”->”云端插件“列表中删除该插件重新选择
腾讯直播组件使用DEMO
一、推流功能
模板部分
<template>
<view class="content">
<Tx-Push
@onNetStatus="onNetStatus"
@onError="onError"
@onStatisticsUpdate="onStatisticsUpdate" :style="{width: lebWidth+'rpx', height:lebHeight+'rpx'}"
ref="TxPush" class="liveview"></Tx-Push>
<view class="input">
<input class="iptVal" type="text" placeholder="输入参数" v-model="iptVal" />
</view>
<view class="controlrow">
<button class="livebutton" @click="setConfig">初使化</button>
<button class="livebutton" @click="startCameraPreview">预览</button>
<button class="livebutton" @click="stopCameraPreview">停止预览</button>
</view>
<view class="controlrow">
<button class="livebutton" @click="startPusher">开始推流</button>
<button class="livebutton" @click="stopPusher">结束推流</button>
<button class="livebutton" @click="setBeautyFilter">美颜</button>
</view>
<view class="controlrow">
<button class="livebutton" @click="setVideoQuality2">码率设置</button>
<button class="livebutton" @click="switchCamera">切换摄像头</button>
<button class="livebutton" @click="turnOnFlashLight">打开闪光灯</button>
</view>
<view class="controlrow">
<button class="livebutton" @click="setZoom">调整焦距</button>
<button class="livebutton" @click="getMaxZoom">获取最大焦距</button>
<button class="livebutton" @click="setExposureCompensation">曝光比例</button>
</view>
<view class="controlrow">
<button class="livebutton" @click="setMirror">观众端的镜像</button>
<button class="livebutton" @click="setRenderRotation">设置翻转</button>
<button class="livebutton" @click="requestAndroidPermission">申请权限</button>
</view>
<view class="controlrow">
<button class="livebutton" @click="changeTouchFocus">切换对焦</button>
<button class="livebutton" @click="pausePusher">暂停推流</button>
<button class="livebutton" @click="resumePusher">恢复推流</button>
<view class="emptyitem"></view>
</view>
<view class="output">
<textarea class="textarea" v-model="msg"></textarea>
</view>
</view>
</template>
脚本部分
<script>
var TXLiveConstants = {};
TXLiveConstants.VIDEO_ANGLE_HOME_DOWN = 1; //竖屏推流
TXLiveConstants.VIDEO_ANGLE_HOME_RIGHT = 0; //横屏推流
TXLiveConstants.VIDEO_ANGLE_HOME_LEFT = 2; //横屏推流
import permision from "@/js_sdk/wa-permission/permission.js"
export default {
data() {
return {
touchFocus:false,
iptVal: '',
title: 'Hello',
lebWidth: 750,
lebHeight: 350,
msg: '=='
}
},
onLoad() {},
methods: {
//推流状态回调
onNetStatus(res) {
this.msg = res.detail.SPD
},
changeTouchFocus(){
if(this.touchFocus){
this.touchFocus = false
}else{
this.touchFocus = true
}
},
toMsg(obj) {
if (obj instanceof Object) {
this.msg += JSON.stringify(obj);
}
if (obj instanceof String) {
this.msg += obj;
}
},
//初使化
setConfig() {
//enablePureAudioPush纯音频
//homeOrientatio 横屏模式
var licenceURL =
'http://license.vod2.myqcloud.com/license/v1/cf7126e6484fe916ef7e23c4acc9eb1c/TXLiveSDK.licence';
var licenceKey = 'e8353becb62cc5cedfabe4fa4ef2f9f0';
var bitrate = 1000
var minBitrate = 1000
var maxBitrate = 1800
var fps = 20
var touchFocus = this.touchFocus
var sampleRate = 48000
var setConfig = {
enablePureAudioPush: false,
autoAdjustBitrate: true,
bitrate: bitrate,
minBitrate: minBitrate,
maxBitrate: maxBitrate,
touchFocus: touchFocus,
sampleRate: sampleRate,
fps: fps,
homeOrientatio: this.VIDEO_ANGLE_HOME,
licenceURL: licenceURL,
licenceKey: licenceKey
}
this.$refs.TxPush.setConfig(setConfig, ret=>{
uni.showToast({
title: `初使化状态:${ret}`
})
console.log(`初使化状态:${ret}`)
this.msg = `初使化状态:${ret}`
})
},
//开始预览
startCameraPreview() {
var value = this.iptVal
console.log('startCameraPreview')
this.$refs.TxPush.startCameraPreview({
'value': value
})
},
//停止预览
stopCameraPreview() {
console.log('stopCameraPreview')
this.$refs.TxPush.stopCameraPreview();
},
//开始推流
startPusher() {
var value = 'rtmp://live-tencent.51bidlive.com/live/PM3984955411651497985?txSecret=08ce36c94552cb316da60a5a34c673d4&txTime=61bff362';
console.log('startPusher')
this.$refs.TxPush.startPusher(value,ret=>{
uni.showToast({
title: `推流状态:${ret}`
})
console.log(`推流状态:${ret}`)
this.msg = `推流状态:${ret}`
})
},
//停止推流
stopPusher() {
console.log('stopPusher')
this.$refs.TxPush.stopPusher();
},
//暂停推流
pausePusher() {
console.log('pausePusher')
this.$refs.TxPush.pausePusher();
},
//恢复推流
resumePusher() {
console.log('resumePusher')
this.$refs.TxPush.resumePusher();
},
//切换摄像头
switchCamera() {
console.log('switchCamera')
var value = this.iptVal;
this.$refs.TxPush.switchCamera({
'value': value
});
},
//打开闪光灯
enableCameraTorch() {
console.log('enableCameraTorch')
var value = this.iptVal;
this.$refs.TxPush.enableCameraTorch({
'value': value
});
},
setVideoQuality2(){
var bitrate = 300
var minBitrate = 300
var maxBitrate = 300
var fps = 20
var value = this.iptVal;
var config = {
autoAdjustBitrate: true,
bitrate: bitrate,
minBitrate: minBitrate,
maxBitrate: maxBitrate,
fps: fps
}
this.$refs.TxPush.setVideoQuality2(config);
},
//设置推流质量
setVideoQuality() {
//VIDEO_QUALITY_STANDARD_DEFINITION YES 360 × 640 500kbps - 900kbps
//VIDEO_QUALITY_STANDARD_DEFINITION NO 360 × 640 800kbps
//VIDEO_QUALITY_HIGH_DEFINITION YES 540 × 960 800kbps - 1500kbps
//VIDEO_QUALITY_HIGH_DEFINITION NO 540 × 960 1200kbps
//VIDEO_QUALITY_SUPER_DEFINITION YES 720 × 1280 1000kbps - 1800kbps
//VIDEO_QUALITY_SUPER_DEFINITION NO 720 × 1280 1800kbps
//VIDEO_QUALITY_ULTRA_DEFINITION YES 1080 × 1920 2500kbps - 3000kbps
//VIDEO_QUALITY_ULTRA_DEFINITION NO 1080 × 1920 3000kbps
//VIDEO_QUALITY_LINKMIC_MAIN_PUBLISHER 不支持设置 540 × 960 800kbps - 1500kbps
//VIDEO_QUALITY_LINKMIC_SUB_PUBLISHER 不支持设置 320 × 480 350kbps
TXLiveConstants.VIDEO_QUALITY_STANDARD_DEFINITION = 1;
TXLiveConstants.VIDEO_QUALITY_HIGH_DEFINITION = 2;
TXLiveConstants.VIDEO_QUALITY_SUPER_DEFINITION = 3;
TXLiveConstants.VIDEO_QUALITY_LINKMIC_MAIN_PUBLISHER = 4;
TXLiveConstants.VIDEO_QUALITY_LINKMIC_SUB_PUBLISHER = 5;
TXLiveConstants.VIDEO_QUALITY_REALTIEM_VIDEOCHAT = 6;
TXLiveConstants.VIDEO_QUALITY_ULTRA_DEFINITION = 7;
var qualityPara = {
adjustBitrate: true,
quality: TXLiveConstants.VIDEO_QUALITY_LINKMIC_SUB_PUBLISHER
}
this.$refs.TxPush.setVideoQuality(qualityPara);
},
//设置屏幕翻转
setRenderRotation() {
console.log('setRenderRotation')
//0,90,180,270,360
var value = this.iptVal;
this.$refs.TxPush.setRenderRotation({
'value': value
});
},
//调整摄像头的焦距
setZoom() {
console.log('setZoom')
var value = this.iptVal;
this.$refs.TxPush.setZoom({
'value': value
});
},
//测试
test() {
console.log('test')
var value = this.iptVal;
this.$refs.TxPush.test({
'value': value
});
},
//获取当前摄像头
getCurrentCamera() {
console.log('getCurrentCamera')
var value = this.iptVal;
this.$refs.TxPush.getCurrentCamera({
'value': value
});
},
//获取摄像头的最大焦距
getMaxZoom() {
console.log('getMaxZoom')
var zoom = this.$refs.TxPush.getMaxZoom();
uni.showToast({
title: zoom + 'zoom'
})
if (zoom == 0) {
this.$refs.TxPush.getMaxZoom(function(ret) {
uni.showToast({
title: JSON.stringify(ret)
})
});
}
},
//获取摄像头曝光
setExposureCompensation() {
//曝光比例,表示该手机支持最大曝光调整值的比例,取值范围从-1到1。
//负数表示调低曝光,-1是最小值,对应 getMinExposureCompensation。
//正数表示调高曝光,1是最大值,对应 getMaxExposureCompensation。
//0表示不调整曝光,默认值为0。
console.log('setExposureCompensation')
var value = this.iptVal;
this.$refs.TxPush.setExposureCompensation({
'value': value
});
},
//设置镜像
setMirror() {
console.log('setMirror')
var value = this.iptVal;
this.$refs.TxPush.setMirror({
'value': value
});
},
//设置美颜
setBeautyFilter() {
//style 美颜算法: 0:光滑 1:自然 2:朦胧
//beautyLevel 磨皮等级: 取值为 0-9.取值为 0 时代表关闭美颜效果.默认值: 0,即关闭美颜效果.
//whiteningLevel 美白等级: 取值为 0-9.取值为 0 时代表关闭美白效果.默认值: 0,即关闭美白效果.
//ruddyLevel 红润等级: 取值为 0-9.取值为 0 时代表关闭美白效果.默认值: 0,即关闭美白效果.
var style = 0;
var beautyLevel = 8;
var whiteningLevel = 8;
var ruddyLevel = 8;
var para = {
style: style,
beautyLevel: beautyLevel,
whiteningLevel: whiteningLevel,
ruddyLevel: ruddyLevel
}
this.$refs.TxPush.setBeautyFilter(para);
},
//暂时不用
setFilter() {
console.log('setFilter')
var value = this.iptVal;
this.$refs.TxPush.setFilter({
'value': value
});
},
//请求相机权限
requestAndroidPermission() {
console.log('requestAndroidPermission')
var _this = this;
var permisionID = 'android.permission.CAMERA';
permision.requestAndroidPermission(permisionID).then((result) => {
if (result == 1) {
uni.showToast({
title: '获得CAMERA授权'
})
permisionID = 'android.permission.RECORD_AUDIO';
permision.requestAndroidPermission(permisionID).then((result) => {
if (result == 1) {
uni.showToast({
title: '获得RECORD授权'
})
//READ_EXTERNAL_STORAGE
//WRITE_EXTERNAL_STORAGE
permisionID = 'android.permission.READ_EXTERNAL_STORAGE';
permision.requestAndroidPermission(permisionID).then((result) => {
if (result == 1) {
uni.showToast({
title: '获得READ_EXTERNAL_STORAGE授权'
})
permisionID = 'android.permission.WRITE_EXTERNAL_STORAGE';
permision.requestAndroidPermission(permisionID).then((result) => {
if (result == 1) {
uni.showToast({
title: '获得WRITE_EXTERNAL_STORAGE授权'
})
}
});
}
});
}
});
} else if (result == 0) {
uni.showToast({
title: result + '未获得授权'
})
} else {
uni.showToast({
title: result + '被永久拒绝权限'
})
permision.gotoAppPermissionSetting()
}
})
},
onStatisticsUpdate(data) {
console.log(data.detail)
if(this.iptVal==1){
this.msg = JSON.stringify(data.detail);
}
},
onError(data) {
if(this.iptVal==2){
console.log(data.detail)
this.msg = JSON.stringify(data.detail);
}
},
onPushStatusUpdate(data) {
console.log(data.detail)
if(this.iptVal==3){
console.log(data.detail)
this.msg = JSON.stringify(data.detail);
}
},
}
}
</script>
样式部分
<style scoped>
.input {
padding: 10rpx;
}
.output {
padding: 10rpx;
}
.iptVal {
border: 1px solid #cccccc;
background-color: #F1F1F1;
height: 60rpx;
padding-left: 20rpx;
}
.liveview {
/* width: 750rpx;
height: 421rpx; */
background-color: #C0C0C0;
overflow: hidden;
}
.controlrow {
width: 750rpx;
flex-direction: row;
flex-wrap: wrap;
margin-bottom: 10rpx;
display: flex;
}
.livebutton {
flex: 1;
width: 200rpx;
margin: 0rpx 10rpx 0rpx 10rpx;
font-size: 24rpx;
padding: 0rpx 0rpx 0rpx 0rpx;
}
.emptyitem {
flex: 1;
width: 200rpx;
margin: 0rpx 10rpx 0rpx 10rpx;
font-size: 24rpx;
}
.textarea {
width: 750rpx;
height: 250rpx;
}
</style>
二、播流功能
模板部分
<template>
<view class="content">
<Leb-Live @onEventConnected="onEventConnected"
@onEventConnectFailed="onEventConnectFailed"
@onEventFirstFrameRendered="onEventFirstFrameRendered"
@onEventStatsReport="onEventStatsReport"
:style="{width: lebWidth+'px', height:lebHeight+'px'}" ref="LebLive" class="liveview"></Leb-Live>
<view class="buttons">
<view class="input">
<input class="iptVal" type="text" placeholder="输入参数" v-model="iptVal" />
</view>
<view class="controlrow">
<button class="livebutton" @click="startPlay">播放</button>
<button class="livebutton" @click="pausePlay">暂停</button>
<button class="livebutton" @click="stopPlay">结束</button>
</view>
<view class="controlrow">
<button class="livebutton" @click="release">释放资源</button>
<button class="livebutton" @click="mutePlay">静音播放</button>
<button class="livebutton" @click="setVolume">音量设置</button>
</view>
<view class="controlrow">
<button class="livebutton" @click="setRenderRotation">旋转</button>
<button class="livebutton" @click="initLeb">初使化</button>
<button class="livebutton" @click="rotateVideo(false)">横屏</button>
</view>
<view class="controlrow">
<button class="livebutton" @click="resumePlay">恢复</button>
</view>
<view class="output">
<textarea class="textarea" v-model="msg"></textarea>
</view>
</view>
</view>
</template>
脚本部分
<script>
export default {
data() {
return {
iptVal: '',
title: 'Hello',
primarytype: '',
lebWidth: 0,
lebHeight: 0,
msg: '==',
pageReady: false,
pageHide: false
}
},
onLoad() {
uni.getSystemInfo({
success: res => {
this.lebWidth = res.screenWidth
this.lebHeight = res.screenWidth * 0.5625
}
})
},
onReady() {
if(this.primarytype=='portrait-primary'){
this.initLeb(true)
} else {
this.rotateVideo(true, 'landscape-primary')
}
},
onShow() {
if(this.pageHide){
this.initLeb(true)
}
this.pageHide = false
},
onHide() {
this.pageHide = true
this.stopPlay()
},
methods: {
requestPermissions(){
this.$refs.LebLive.requestPermissions();
},
toMsg(obj){
if(obj instanceof Object){
this.msg += JSON.stringify(obj);
}
if(obj instanceof String){
this.msg += obj;
}
},
onEventConnected(obj){
this.msg = '连接成功'
console.log(this.msg)
},
onEventConnectFailed(obj){
this.msg = '连接失败';
//this.stopPlay()
//this.release()
this.initLeb(true)
console.log(this.msg,obj)
},
onEventFirstFrameRendered(obj){
this.msg = '首帧准备';
console.log(this.msg,obj)
},
onEventStatsReport(obj){
//console.log(obj);
this.msg = JSON.stringify(obj);
},
startPlay(){
console.log('startPlay')
this.$refs.LebLive.startPlay();
},
stopPlay(){
console.log('stopPlay')
this.$refs.LebLive.stopPlay();
},
pausePlay(){
console.log('pausePlay')
this.$refs.LebLive.pausePlay();
},
resumePlay(){
console.log('resumePlay')
this.$refs.LebLive.resumePlay();
},
release(){
console.log('release')
this.$refs.LebLive.release();
},
mutePlay(){
console.log('mutePlay')
var mute = this.iptVal == 'yes' ? true : false;
this.$refs.LebLive.mutePlay({'value':mute});
},
setVolume(){
console.log('setVolume')
var volume = parseFloat(this.iptVal);
this.$refs.LebLive.setVolume({'value':volume});
},
setRenderRotation(){
console.log('setRenderRotation')
var rotation = parseInt(this.iptVal);
//plus.screen.lockOrientation('landscape-primary');
this.$refs.LebLive.setRenderRotation({'value':rotation});
},
setHide(){
if(this.lebWidth==0||this.lebHeight==0){
uni.getSystemInfo({
success: res => {
this.lebWidth = res.screenWidth
this.lebHeight = res.screenWidth * 0.5625
}
})
}else{
this.lebWidth = 0
this.lebHeight = 0
}
},
initLeb(isAutoPlay){
var part = [];
part[0] = '750';
part[1] = '461';
part[2] = 'webrtc://5664.liveplay.myqcloud.com/live/5664_harchar1';
//part[2] = 'webrtc://play-tencent.51bidlive.com/live/37260?txSecret=110e9e93f8ca191c648273be62e264f9';
part[3] = 'no';
part[4] = 'https://webrtc.liveplay.myqcloud.com/webrtc/v1/pullstream';
part[5] = 'https://webrtc.liveplay.myqcloud.com/webrtc/v1/stopstream';
part[6] = 16;
part[7] = 9;
//this.lebWidth = part[0];
//this.lebHeight = part[1];
this.msg = `${this.lebHeight}`
var width = this.lebWidth;
var height = this.lebHeight;
var autoPlay = part[3];
var videoWidthRate = part[6];
var videoHeightRate = part[7];
var liveUrl = part[2];
var requestPullUrl = part[4];
var requestStopUrl = part[5];
this.$refs.LebLive.initLeb({
'width':width,
'height':height,
'liveUrl':liveUrl,
'videoWidthRate':videoWidthRate,
'videoHeightRate':videoHeightRate,
'requestPullUrl':requestPullUrl,
'requestStopUrl':requestStopUrl,
}, ret => {
// console.log('isAutoPlay')
// console.log(isAutoPlay)
if(isAutoPlay){
this.startPlay()
}
// uni.showToast({
// title: `初使化${ret}`
// })
this.$refs.LebLive.resetVideoSize(this.lebWidth, this.lebHeight)
// console.log(`初使化${ret}`)
// this.msg = `初使化${ret}`
})
},
rotateVideo(needPlay, primarytype){
if(primarytype){
//指定屏幕方向
this.primarytype = primarytype
} else {
//切换方向
if(this.primarytype == `portrait-primary`){
this.primarytype = `landscape-primary`
} else {
this.primarytype = `portrait-primary`
}
}
plus.screen.lockOrientation(this.primarytype)
uni.onWindowResize(res => {
//屏幕翻译完成后逻辑
uni.offWindowResize(() => {})
uni.getSystemInfo({
success: res => {
if(this.primarytype == `landscape-primary`){
//横屏全屏
this.lebWidth = res.screenWidth
this.lebHeight = res.screenHeight
} else if(this.primarytype == `portrait-primary`) {
//竖屏全屏
this.lebWidth = res.screenWidth
this.lebHeight = res.screenWidth * 0.5625
}
if(needPlay){
//翻转后播放
this.initLeb(true)
}else{
//仅翻转
this.$refs.LebLive.resetVideoSize(this.lebWidth, this.lebHeight)
}
}
})
})
},
}
}
</script>
样式部分
<style scoped>
.content{
width: 100%;
}
.input{
padding: 10rpx;
}
.output{
padding: 10rpx;
}
.iptVal{
border: 1px solid #cccccc;
background-color: #F1F1F1;
height: 50px;
padding-left: 20rpx;
}
.buttons{
position:fixed;
left: 0;
bottom: 0;
background-color: rgba(0,0,0,0);
}
.liveview{
/* position: fixed;
left: 0;
top: 0;
background-color: #C0C0C0; */
}
.controlrow{
width: 750rpx;
flex-direction: row;
flex-wrap: wrap;
margin-bottom: 10rpx;
display: flex;
}
.livebutton{
flex: 1;
width: 200rpx;
margin: 0rpx 10rpx 0rpx 10rpx;
font-size: 24rpx;
padding: 0rpx 0rpx 0rpx 0rpx;
}
.emptyitem{
flex: 1;
width: 200rpx;
margin: 0rpx 10rpx 0rpx 10rpx;
font-size: 24rpx;
}
.textarea{
width: 750rpx;
height: 250rpx;
}
.btnSetLandscape{
padding: 10px 10px 10px 10px;
background-color: #333333;
}
.btnSetLandscapetext {
color: #FFFFFF;
}
</style>