123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534 |
- import { Injectable } from '@angular/core';
- import { AlertController } from '@ionic/angular';
- import * as Parse from 'parse';
- import { AiChatService } from './aichart.service';
- import { HttpService } from './http.service';
- declare const AgoraRTC: any;
- @Injectable({
- providedIn: 'root',
- })
- export class LiveService {
- isAnchor: boolean = false; //是否是主播
- options: {
- appid: string;
- channel: string;
- token: string;
- } = {
- appid: '71d3357d920d4352b39ec8b8d26a7cb9',
- channel: '',
- token: '',
- };
- localTracks: any = {
- audioTrack: null,
- videoTrack: null,
- };
- rid?: string; //房间id(channel)
- profile?: any = localStorage.getItem('profile');
- client: any; //客户端
- company: string = '';
- UID: any;
- tools: any = {
- audio: false, //是否关闭音频
- camera: false, //是否切换摄像头
- mute: false, //是否静音
- };
- user_published_list = new Set(); //已发布列表
- timer: any; //轮询获取频道token的定时
- alert: any; //提示框
- media_devices: {
- //音视频设备列表
- audioDevices: Array<any>;
- videoDevices: Array<any>;
- } = {
- audioDevices: [],
- videoDevices: [],
- };
- currentUsedDevice: { audioDevice: string; videoDevice: string } = {
- //当前使用的设备
- audioDevice: '',
- videoDevice: '',
- };
- room?: Parse.Object; //直播间
- connection_state?: string; //连接状态
- surplusNumber: number = 0; //剩余可通话时长(单位:秒s)
- countdown: number = 0; // 新增倒计时变量
- timer_countdown: any;
- liveLog?: Parse.Object; //直播记录
- constructor(
- private http: HttpService,
- private aiServ: AiChatService,
- private alertController: AlertController
- ) {
- this.client?.leave();
- this.company = this.aiServ.company;
- this.getProfile();
- }
- async getProfile() {
- if (this.profile) return;
- let queryProfile = new Parse.Query('Profile');
- queryProfile.equalTo('user', Parse.User.current()?.id);
- queryProfile.notEqualTo('isDeleted', true);
- queryProfile.equalTo('isCross', true);
- this.profile = await queryProfile.first();
- this.profile?.id &&
- localStorage.setItem('profile', JSON.stringify(this.profile.toJSON()));
- }
- /* 初始化Agora */
- initAgora() {
- this.timer && clearTimeout(this.timer);
- this.timer_countdown && clearInterval(this.timer_countdown);
- this.options['token'] = '';
- this.options['channel'] = '';
- this.client?.leave();
- this.client = AgoraRTC.createClient({ mode: 'rtc', codec: 'h264' });
- AgoraRTC.enableLogUpload();
- }
- // 获取所有音视频设备
- getDevices() {
- AgoraRTC.getDevices()
- .then((devices: any) => {
- this.media_devices.audioDevices = devices.filter(function (
- device: any
- ) {
- return device.kind === 'audioinput';
- });
- this.media_devices.videoDevices = devices.filter(function (
- device: any
- ) {
- return device.kind === 'videoinput';
- });
- console.log(this.media_devices);
- this.currentUsedDevice = {
- audioDevice: this.media_devices.audioDevices[0].deviceId,
- videoDevice: this.media_devices.videoDevices[0].deviceId,
- };
- // return Promise.all([
- // AgoraRTC.createCameraVideoTrack(),
- // AgoraRTC.createMicrophoneAudioTrack(),
- // ]);
- })
- .then((tracks: any) => {
- console.log(tracks);
- })
- .catch((err: any) => {
- console.warn('获取媒体权限失败', err);
- this.alertTips('获取媒体权限失败');
- });
- }
- /* 获取token */
- async getToken(room: Parse.Object) {
- this.room = room;
- this.isAnchor = room?.get('user')?.id === Parse.User.current()?.id;
- this.timer && clearTimeout(this.timer);
- let remoteEle = document.getElementById('vice-video');
- (remoteEle as any).style.display = 'none';
- //获取频道token记录
- if (this.isAnchor) {
- this.UID = 111111;
- let uid = Parse.User.current()?.id;
- if (!uid) {
- this.timer = setTimeout(() => {
- this.getToken(room);
- }, 2000);
- return;
- }
- let baseurl = 'https://server.fmode.cn/api/webrtc/build_token';
- let reqBody = {
- company: this.company, // this.aiSer.company,
- profile: room?.get('profile').id,
- channelName: room?.get('profile').id,
- };
- let data: any = await this.http.httpRequst(baseurl, reqBody, 'POST');
- console.log(data);
- if (data.code == 200) {
- this.options.token = data.data.token;
- this.options.appid = data.data.appid;
- this.options.channel = room?.get('profile').id;
- }
- } else {
- let data = await this.updateToken(room?.get('profile').id);
- console.log(data);
- if (!data?.token) {
- this.timer = setTimeout(() => {
- this.getToken(room);
- }, 2000);
- return;
- }
- this.options.token = data.token;
- this.options.channel = data.channel;
- }
- this.join();
- }
- async updateToken(pid: string) {
- let sql = `select "rtc"."objectId" as "rid","rtc"."channel", "rtc"."token", "rtc"."expiraTime" from "RtcToken" as "rtc" where "rtc"."profile" = '${pid}' and "rtc"."expiraTime" >= now() order by "createdAt" desc limit 1`;
- let tokenData: any = await this.http.customSQL(sql);
- if (
- tokenData &&
- tokenData.code == 200 &&
- tokenData.data &&
- tokenData.data.length > 0
- ) {
- return tokenData.data[0];
- } else {
- return null;
- }
- }
- /* 进入频道 */
- async join() {
- let path = location.pathname;
- if (path.indexOf('/live/link-room/') == -1) return;
- console.log('频道:', this.options.channel);
- this.client
- .join(
- this.options.appid,
- this.options.channel,
- this.options.token,
- this.UID
- )
- .then(async (uid: any) => {
- // await this.client.setClientRole('host');
- console.log('进入频道当前uid:', uid);
- this.connection_state = this.client.connectionState;
- if (!this.isAnchor) {
- // 观众进入直播间,创建直播记录
- await this.createLiveLog(uid);
- } else {
- //主播进入直播间直接发送自己推流
- await this.publishSelf();
- }
- await this.joinReady();
- this.afterJoin();
- })
- .catch((err: any) => {
- console.log('进入频道失败:', err);
- });
- }
- /* 发布本地视频 */
- async publishSelf() {
- try {
- let data = await Promise.all([
- /* 创建音频和视频轨道 */
- AgoraRTC.createMicrophoneAudioTrack(this.currentUsedDevice.audioDevice),
- AgoraRTC.createCameraVideoTrack(this.currentUsedDevice.videoDevice),
- ]);
- this.localTracks.audioTrack = data[0];
- this.localTracks.videoTrack = data[1];
- // console.log(this.localTracks);
- let remoteEle = document.getElementById('vice-video');
- if (remoteEle) {
- remoteEle.textContent = '';
- remoteEle.style.display = 'block';
- }
- this.localTracks.videoTrack.play('vice-video'); //播放自己视频渲染
- if (this.tools['audio']) {
- this.localTracks.audioTrack.setEnabled(false);
- } else {
- this.localTracks.audioTrack.setEnabled(true);
- }
- await this.client.publish(Object.values(this.localTracks));
- } catch (err) {
- console.log('发布本地视频失败:', err);
- this.alertTips('发布本地视频失败:');
- }
- }
- /* 订阅远程视频 */
- async joinReady() {
- this.client.remoteUsers.forEach((user: any) => {
- console.log('remoteUsers', user.uid);
- this.client.subscribe(user, 'audio').then((audioTrack: any) => {
- this.user_published_list.add(user);
- audioTrack.setVolume(0);
- audioTrack.play();
- });
- this.client.subscribe(user, 'video').then((videoTrack: any) => {
- let remoteEle = document.getElementById('video');
- if (remoteEle) {
- remoteEle.textContent = '';
- }
- videoTrack.play('video');
- });
- });
- this.client.on('user-joined', (user: any) => {
- console.log(user, `${user.uid} 加入频道`);
- });
- this.client.on('user-published', async (user: any, mediaType: string) => {
- console.log('用户推流成功', user);
- await this.client.subscribe(user, mediaType);
- let remoteEle = document.getElementById('video');
- if (remoteEle) {
- remoteEle.textContent = '';
- }
- if (mediaType === 'video') {
- user.videoTrack.play('video');
- this.alert?.dismiss();
- }
- if (mediaType === 'audio') {
- user.audioTrack.play();
- this.user_published_list.add(user);
- }
- });
- this.client.on('user-unpublished', async (user: any, mediaType: string) => {
- if (mediaType === 'audio') {
- console.log('对方已静音');
- this.user_published_list.delete(user);
- }
- if (mediaType === 'video') {
- console.log('用户取消推流');
- let remoteEle = document.getElementById('video');
- if (remoteEle) {
- remoteEle.textContent = '对方离开直播间';
- }
- //主播离开,停止计时计费
- if (!this.isAnchor) {
- this.client.leave();
- }
- this.alertTips('对方已离开直播间');
- }
- });
- this.client.on(
- 'connection-state-change',
- (curState: any, prevState: any) => {
- this.connection_state = curState;
- if (
- curState == 'RECONNECTING' ||
- curState == 'DISCONNECTED' ||
- curState == 'DISCONNECTING'
- ) {
- this.timer && clearTimeout(this.timer);
- this.timer_countdown && clearInterval(this.timer_countdown);
- }
- console.log('状态变更:', this.connection_state);
- console.log(prevState);
- }
- );
- this.monitorDevices();
- }
- async createLiveLog(uid: string) {
- let profile = JSON.parse(localStorage.getItem('profile') || '{}');
- let obj = Parse.Object.extend('LiveLog');
- let liveLog = new obj();
- liveLog.set('title', `与${profile?.name || profile?.mobile}进行直播`);
- liveLog.set('uid', String(uid));
- liveLog.set('company', {
- __type: 'Pointer',
- className: 'Company',
- objectId: this.company,
- });
- liveLog.set('room', {
- __type: 'Pointer',
- className: 'Room',
- objectId: this.room?.id,
- });
- liveLog.set('user', {
- __type: 'Pointer',
- className: '_User',
- objectId: Parse.User.current()?.id,
- });
- liveLog.set('profile', {
- __type: 'Pointer',
- className: 'Profile',
- objectId: this.room?.get('profile')?.id,
- });
- this.liveLog = await liveLog.save();
- }
- /* 连线成功立即创建直播记录,同步获取剩余可通话时长,并且开始计时 */
- async afterJoin() {
- this.timer && clearTimeout(this.timer);
- this.timer_countdown && clearInterval(this.timer_countdown);
- if (this.client.remoteUsers.length > 0) {
- if (this.isAnchor) {
- //如果是主播,进入获取remoteUsers.user获取livelog再获取对方剩余通话时长
- this.getLiveLog();
- } else {
- let query = new Parse.Query('LiveLog');
- query.equalTo('objectId', this.liveLog?.id);
- query.equalTo('isLive', true);
- query.select('objectId');
- const resultData = await query.first();
- if (resultData?.id) {
- //首次进入至少计时2分钟,不足2分钟按2分钟计算扣时
- await this.publishSelf();
- await this.get_duration();
- this.countdown = this.surplusNumber; // 初始化倒计时
- this.startCountdown();
- this.computeDuration(60000 * 2);
- } else {
- this.timer = setTimeout(() => {
- this.afterJoin();
- }, 1000);
- }
- }
- } else {
- this.timer = setTimeout(() => {
- this.afterJoin();
- }, 1000);
- }
- }
- async getLiveLog() {
- let uid = this.client.remoteUsers[0].uid;
- this.timer && clearTimeout(this.timer);
- let query = new Parse.Query('LiveLog');
- query.equalTo('uid', String(uid));
- query.equalTo('room', this.room?.id);
- query.notEqualTo('isDeleted', true);
- query.notEqualTo('isLive', true);
- query.descending('createdAt');
- this.liveLog = await query.first();
- if (this.liveLog?.id) {
- this.liveLog?.set('isLive', true);
- await this.liveLog?.save();
- this.get_duration();
- return;
- }
- this.timer = setTimeout(() => {
- this.getLiveLog();
- }, 1000);
- }
- async get_duration() {
- let url = 'https://server.fmode.cn/api/ailiao/remain_second';
- let params = {
- rid: this.room?.id,
- uid: this.liveLog?.get('user')?.id || this.liveLog?.get('user')?.objectId,
- };
- let data = await this.http.httpRequst(url, params, 'POST');
- console.log(data);
- this.surplusNumber = data.data ?? 0;
- }
- startCountdown() {
- this.timer_countdown = setInterval(() => {
- if (this.countdown > 0) {
- this.countdown--;
- console.log(this.countdown);
- } else {
- clearInterval(this.timer_countdown);
- this.alertTips('通话时间结束');
- this.client.leave(); // 结束通话
- }
- }, 1000);
- }
- /* 开始计时 */
- async computeDuration(num?: number) {
- //每10秒保存一次数据
- this.timer && clearTimeout(this.timer);
- let p = JSON.parse(this.profile);
- console.log(p);
- let url = 'https://server.fmode.cn/api/ailiao/count/duration';
- let params = {
- company: this.company,
- profile: p?.objectId,
- rid: this.room?.id,
- uid: Parse.User.current()?.id,
- duration: num ? num / 1000 : 10,
- logid: this.liveLog?.id,
- };
- let data = await this.http.httpRequst(url, params, 'POST');
- console.log(data);
- this.timer = setTimeout(() => {
- this.computeDuration();
- }, num ?? 10000);
- }
- /* 监听音视频设备插拔 */
- monitorDevices() {
- AgoraRTC.onMicrophoneChanged = async (changedDevice: any) => {
- // 插入麦克风设备时,切换到新插入的设备
- if (changedDevice.state === 'ACTIVE') {
- this.localTracks.audioTrack.setDevice(changedDevice.device.deviceId);
- this.currentUsedDevice.audioDevice = changedDevice.device.deviceId;
- // 拔出设备为当前设备时,切换到一个已有的设备
- } else if (
- changedDevice.device.label ===
- this.localTracks.audioTrack.getTrackLabel()
- ) {
- const oldMicrophones = await AgoraRTC.getMicrophones();
- oldMicrophones[0] &&
- this.localTracks.audioTrack.setDevice(oldMicrophones[0].deviceId);
- this.currentUsedDevice.audioDevice = oldMicrophones[0].deviceId;
- }
- };
- AgoraRTC.onCameraChanged = async (changedDevice: any) => {
- // 插入相机设备时,切换到新插入的设备
- if (changedDevice.state === 'ACTIVE') {
- this.localTracks.videoTrack.setDevice(changedDevice.device.deviceId);
- this.currentUsedDevice.videoDevice = changedDevice.device.deviceId;
- // 拔出设备为当前设备时,切换到一个已有的设备
- } else if (
- changedDevice.device.label ===
- this.localTracks.videoTrack.getTrackLabel()
- ) {
- const oldCameras = await AgoraRTC.getCameras();
- oldCameras[0] &&
- this.localTracks.videoTrack.setDevice(oldCameras[0].deviceId);
- this.currentUsedDevice.videoDevice = oldCameras[0].deviceId;
- }
- };
- }
- /* 关开麦 */
- async updatePublishedAudioTrack() {
- this.tools['audio'] = !this.tools['audio'];
- if (this.tools['audio'] && this.localTracks.audioTrack) {
- await this.localTracks.audioTrack.setEnabled(false);
- console.log('停止推送音频');
- } else {
- await this.localTracks.audioTrack.setEnabled(true);
- console.log('恢复推送音频');
- }
- // await this.client.unpublish(this.localTracks.audioTrack);
- return true;
- }
- /* 静音 */
- async muteAudio() {
- this.tools['mute'] = !this.tools['mute'];
- let list = Array.from(this.user_published_list);
- for (let index = 0; index < list.length; index++) {
- const user = list[index];
- if (this.tools['mute']) {
- console.log('静音');
- await this.client.unsubscribe(user, 'audio');
- } else {
- await this.client.subscribe(user, 'audio');
- console.log('恢复声音');
- }
- }
- }
- /* 切换摄像头 */
- async changeCamera() {
- const oldCameras = await AgoraRTC.getCameras(true);
- let newCamers = oldCameras.find(
- (item: any) => item.deviceId !== this.currentUsedDevice.videoDevice
- );
- console.log(newCamers);
- newCamers &&
- this.localTracks.videoTrack
- .setDevice(newCamers.deviceId)
- .then(() => {
- this.tools['camera'] = !this.tools['camera'];
- this.currentUsedDevice.videoDevice = newCamers.deviceId;
- })
- .catch((err: any) => {
- console.log('set device error', err);
- this.alertTips('切换摄像头失败');
- });
- return true;
- }
- /* 提示 */
- async alertTips(message: string, title?: string, callBack?: Function) {
- this.alert = await this.alertController.create({
- header: title || '提示',
- message: message,
- buttons: [
- {
- text: '确认',
- handler: () => {
- callBack && callBack();
- },
- },
- ],
- });
- await this.alert.present();
- }
- }
|