在vue项目的public目录下的index.html中添加如下脚本,脚本必须引入在 index.html的body中。
<script src="https://video.sdk.qcloudecdn.com/web/TXLivePusher-2.0.1.min.js" charset="utf-8"></script>
<script src="https://web.sdk.qcloud.com/player/tcplayerlite/release/v2.4.1/TcPlayer-2.4.1.js" charset="utf-8"></script>
<template>
<div>
<div><h3>主持人直播</h3></div>
<div>
<!-- 推流的视频展示 -->
<div style="margin-top:30px; float: left; width:780px;">
<div id="id_local_video" style="width:780px;height:450px;border:1px solid blue; float: left; margin: 10px;" ></div>
<div style="margin: 20px;">
<el-button type="primary" @click="openCamera">打开摄像头</el-button>
<el-button type="primary" @click="closeCamera">关闭摄像头</el-button>
<el-button type="primary" @click="startScreenCapture">共享屏幕</el-button>
<el-button type="primary" @click="stopScreenCapture">关闭共享</el-button>
</div>
<div style="margin: 20px;">
<el-button type="primary" @click="startMicrophone">打开麦克风</el-button>
<el-button type="primary" @click="stopMicrophone">关闭麦克风</el-button>
<el-button type="primary" @click="startPush">开始直播</el-button>
<el-button type="primary" @click="stopPush">停止直播</el-button>
<el-button type="primary" @click="openUserList()">所有成员</el-button>
</div>
</div>
</div>
</div>
</template>
<script>
import { getRoomInfoById } from '@/api/api.js'
export default {
name: 'PushVideoPage',
props: {
roomId: { type: [String,Number], default: 0 },
},
data() {
return {
livePusher: null,
roomInfo: null,
}
},
mounted() {
console.log('roomId='+this.roomId);
//--1 初始化腾讯云播放器
this.readyPusher()
//--2 获取房间信息
let param = {id: this.roomId};
getRoomInfoById(param).then(res => {
console.log('getRoomInfoById', res);
if(res.data.code == 200) {
this.roomInfo = res.data.data;
}
})
},
methods: {
//初始化腾讯推流组件
readyPusher(){
this.livePusher = new TXLivePusher();
// 指定本地视频播放器容器:
this.livePusher.setRenderView('id_local_video');
// 设置视频质量
this.livePusher.setVideoQuality('720p');
// 设置音频质量
this.livePusher.setAudioQuality('standard');
// 自定义设置帧率
this.livePusher.setProperty('setVideoFPS', 25);
console.log('readyPusher OK');
//获取视频效果管理实例
this.videoEffectManager = this.livePusher.getVideoEffectManager();
//开启混流
this.videoEffectManager.enableMixing(true);
//设置混流参数
this.videoEffectManager.setMixingConfig({
videoWidth: 1280,
videoHeight: 720,
videoFramerate: 15
});
},
openCamera() {
// 打开摄像头
this.livePusher.startCamera();
},
closeCamera() {
// 关闭摄像头
this.livePusher.stopCamera();
},
startScreenCapture() {
// 打开共享桌面
this.livePusher.startScreenCapture();
},
stopScreenCapture() {
// 关闭共享桌面
this.livePusher.stopScreenCapture();
},
startMicrophone() {
// 打开麦克风
this.livePusher.startMicrophone();
},
stopMicrophone() {
// 关闭麦克风
this.livePusher.stopMicrophone();
},
startPush() {
// this.livePusher.startPush("webrtc://live.shenmazong.com/live/36301?txSecret=134321418f9cfc673c9bfc141c0ecf03&txTime=6309C85A");
// this.livePusher.startPush("webrtc://live.shenmazong.com/live/32301?txSecret=ae4323ac78e0d0e6c7580674824286ba&txTime=6308BB23");
this.livePusher.startPush(this.roomInfo.pushUrl)
},
stopPush() {
// this.livePusher.stopPush("webrtc://live.shenmazong.com/live/32301?txSecret=ae4323ac78e0d0e6c7580674824286ba&txTime=6308BB23");
this.livePusher.stopPush(this.roomInfo.pushUrl)
},
}
}
</script>
<style>
</style>
<template>
<div>
<div><h3>观众看直播</h3></div>
<div>
<div id="id_test_video" style="float: left;margin-left: 123px">
</div>
</div>
</div>
</template>
<script>
import { getRoomInfoById } from '@/api/api.js'
export default {
name: 'PlayVideoPage',
props: {
roomId: { type: [String,Number], default: 0 },
},
data() {
return {
roomInfo: null,
}
},
mounted() {
console.log('roomId='+this.roomId);
//--2 获取房间信息
let param = {id: this.roomId};
getRoomInfoById(param).then(res => {
console.log('getRoomInfoById', res);
if(res.data.code == 200) {
this.roomInfo = res.data.data;
this.joinRoom();
}
});
},
methods: {
joinRoom() {
this.player = new TcPlayer('id_test_video', {
"m3u8": this.roomInfo.playUrl,
"autoplay" : true,
"poster" : this.roomInfo.coverUrl,
"width" : '660',//视频的显示宽度,请尽量使用视频分辨率宽度
"height" : '490'//视频的显示高度,请尽量使用视频分辨率高度
});
},
}
}
</script>
<style>
</style>
{
path: '/push/:roomId',
name: 'PushRoom',
props: true,
component: () => import('../views/PushVideoPage.vue')
},
{
path: '/play/:roomId',
name: 'PlayRoom',
props: true,
component: () => import('../views/PlayVideoPage.vue')
},
package com.shenmazg6.controller;
import com.shenmazg6.service.TbRoomService;
import com.shenmazg6.utils.ResultResponse;
import com.shenmazg6.vo.IdVo;
import com.shenmazg6.vo.PageInfoVo;
import com.shenmazg6.vo.RoomInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 军哥
* @version 1.0
* @description: TbRoomController
* @date 2022/12/22 15:46
*/
@RestController
@Slf4j
@RequestMapping(value = "/room")
public class TbRoomController {
@Autowired
TbRoomService tbRoomService;
@PostMapping(value = "/add")
public ResultResponse add(@RequestBody RoomInfoVo roomInfoVo) {
return tbRoomService.add(roomInfoVo);
}
@PostMapping(value = "/list")
public ResultResponse list(@RequestBody PageInfoVo pageInfoVo) {
return tbRoomService.listByPage(pageInfoVo);
}
@PostMapping(value = "/get")
public ResultResponse get(@RequestBody IdVo idVo) {
return tbRoomService.getRoomById(idVo);
}
}
package com.shenmazg6.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shenmazg6.pojo.TbRoom;
import com.shenmazg6.service.TbRoomService;
import com.shenmazg6.mapper.TbRoomMapper;
import com.shenmazg6.utils.CopyBeanUtils;
import com.shenmazg6.utils.IdWorker;
import com.shenmazg6.utils.ResultResponse;
import com.shenmazg6.utils.TxLiveUtils;
import com.shenmazg6.vo.IdVo;
import com.shenmazg6.vo.PageInfoVo;
import com.shenmazg6.vo.RoomInfoVo;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author Think
* @description 针对表【tb_room(直播房间表)】的数据库操作Service实现
* @createDate 2022-12-22 15:45:15
*/
@Service
public class TbRoomServiceImpl extends ServiceImpl<TbRoomMapper, TbRoom>
implements TbRoomService{
@Override
public ResultResponse add(RoomInfoVo roomInfoVo) {
//--1 复制属性
TbRoom tbRoom = new TbRoom();
CopyBeanUtils.copy(roomInfoVo, tbRoom);
//--2 生成推流地址
tbRoom.setRoomNo(""+ IdWorker.getId());
String pushUrl = TxLiveUtils.makePushUrl(tbRoom.getRoomNo(), roomInfoVo.getCloseTime());
tbRoom.setPushUrl(pushUrl);
//--3 生成播放地址
String playUrl = TxLiveUtils.makePlayUrl(tbRoom.getRoomNo());
tbRoom.setPlayUrl(playUrl);
//--3 写入数据库
tbRoom.setCoverUrl("https://upload.shenmazong.com/upload/955bb2ca-b4d6-4ab5-8e1e-69c35f277af7.png");
save(tbRoom);
return ResultResponse.SUCCESS();
}
@Override
public ResultResponse listByPage(PageInfoVo pageInfoVo) {
// 得到列表数据
Page<TbRoom> p = new Page<>(pageInfoVo.getPageNum(), pageInfoVo.getPageSize());
Page<TbRoom> tbRoomPage = page(p, new QueryWrapper<TbRoom>().lambda().orderByDesc(TbRoom::getRoomId));
// 数据转换
Page<RoomInfoVo> infoVoPage = new Page<>(pageInfoVo.getPageNum(), pageInfoVo.getPageSize());
BeanUtils.copyProperties(tbRoomPage, infoVoPage);
List<RoomInfoVo> collect = tbRoomPage.getRecords().stream().map(item -> {
RoomInfoVo roomInfoVo = new RoomInfoVo();
BeanUtils.copyProperties(item, roomInfoVo);
return roomInfoVo;
}).collect(Collectors.toList());
infoVoPage.setRecords(collect);
return ResultResponse.SUCCESS(infoVoPage);
}
@Override
public ResultResponse getRoomById(IdVo idVo) {
TbRoom tbRoom = getById(idVo.getId());
if(tbRoom == null) {
return ResultResponse.FAILED(404, "房间不存在");
}
return ResultResponse.SUCCESS(tbRoom);
}
}