diff --git a/sdk/demo/demo.html b/sdk/demo/demo.html index c932f5105506e5f2884afd07a3ce5fa77cddf745..e045896b112b7ea74d1f1a10310a31590eddcdb1 100644 --- a/sdk/demo/demo.html +++ b/sdk/demo/demo.html @@ -131,6 +131,9 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. selected="selected">720P + 信任目标机器 @@ -167,7 +170,7 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. auto_rotate: true, libPath: commonLibPath, decoder_type: 'H264', // H265\H264 - microPhoneOutputType: 'OPUS', // pcm或opus + microPhoneOutputType: 'OPUS', // PCM或OPUS }; if (CloudApp.isSupport()) { var cloudapp = new CloudApp("container", params); @@ -210,6 +213,8 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. var typedArray = new Uint8Array(utf8Array); window.cloudapp.sendDataToCloudApp(typedArray.buffer); }; + + document.getElementById("trustHost").href = 'https://' + [params.ip, params.port].join(':'); } function exit() { diff --git a/sdk/index.html b/sdk/index.html index 5246f9f364c184b8252cff1f3bc07eb25ced8db0..189dbbef30eba00201a2788d520117edfc0c0798 100644 --- a/sdk/index.html +++ b/sdk/index.html @@ -213,7 +213,7 @@ Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. auto_rotate: true, libPath: commonLibPath, // 配置库文件地址 decoder_type: 'H264', // H265\H264 - microPhoneOutputType: 'OPUS', // pcm或opus + microPhoneOutputType: 'OPUS', // PCM或OPUS media_config: { bitrate: 3000000, frame_rate: 30, diff --git a/sdk/src/AppController.js b/sdk/src/AppController.js index 87cc59e775de52baa4e85649d51e0e4f7ba3b36a..18b6c52148dd82ca7d5bbaa9fc2e6f8e9d3b5e3e 100644 --- a/sdk/src/AppController.js +++ b/sdk/src/AppController.js @@ -29,6 +29,7 @@ import Util from './Util'; import SocketWorker from './worker/SocketWorker'; import MicrophonePlayer from './MicrophonePlayer'; import CameraPlayer from './CameraPlayer'; +import {MOBILE_CAMERA_MODE_MAP} from './config/commonConfig'; /*global __IS_DEBUG__*/ if (__IS_DEBUG__) { @@ -81,6 +82,8 @@ const CAMERA_RSP_LEN = MEDIA_MSG_HEADER_COUNT + 2; const MICROPHONE_RSP_LEN = MEDIA_MSG_HEADER_COUNT + 2; // 麦克风音频流rsp长度 const MIC_START_RECORD_RSP_LEN = MEDIA_MSG_HEADER_COUNT + 4; +// pc场景摄像头最大高度 +const PC_CAMERA_MAX_HEIGHT = 720; class AppController { constructor(options) { @@ -98,8 +101,10 @@ class AppController { 'audioStateChange', 'cloudAppData' ]); - this.mediaMsgHeader = null; - this.mediaMsgBody = null; + this.cameraMsgHeader = null; + this.microPhoneMsgHeader = null; + this.cameraMsgBody = null; + this.microPhoneMsgBody = null; this.cameraMsgType = PROTOCOL_CONFIG.CAMERA_MESSAGE_TYPE; this.microPhoneMsgType = PROTOCOL_CONFIG.MICROPHONE_MESSAGE_TYPE; // WebSocket variable @@ -456,11 +461,11 @@ class AppController { } let buf = new Uint8Array(pkg); - this.mediaMsgHeader = this.getMsgHeader(buf); + this.microPhoneMsgHeader = this.getMsgHeader(buf); if (buf.length > MEDIA_MSG_HEADER_COUNT) { - this.mediaMsgBody = this.getMsgBody(buf.slice(MEDIA_MSG_HEADER_COUNT), 'MICROPHONE'); + this.microPhoneMsgBody = this.getMsgBody(buf.slice(MEDIA_MSG_HEADER_COUNT), 'MICROPHONE'); } - const type = this.mediaMsgHeader.optType; + const type = this.microPhoneMsgHeader.optType; switch(type) { case this.microPhoneMsgType.OPT_MIC_SET_PARAM_REQ: this.handleSetRecordParam(); @@ -482,11 +487,11 @@ class AppController { } let buf = new Uint8Array(pkg); - this.mediaMsgHeader = this.getMsgHeader(buf); + this.cameraMsgHeader = this.getMsgHeader(buf); if (buf.length > MEDIA_MSG_HEADER_COUNT) { - this.mediaMsgBody = this.getMsgBody(buf.slice(MEDIA_MSG_HEADER_COUNT), 'CAMERA'); + this.cameraMsgBody = this.getMsgBody(buf.slice(MEDIA_MSG_HEADER_COUNT), 'CAMERA'); } - const type = this.mediaMsgHeader.optType; + const type = this.cameraMsgHeader.optType; switch(type) { case this.cameraMsgType.OPT_CAMERA_GET_PARAM_REQ: break; @@ -511,9 +516,9 @@ class AppController { sendMediaMsgCmdData(bufLen, msgType, mediaType) { let rspBuf = new Uint8Array(bufLen); - this.makeCommonHeaderBuf(rspBuf, msgType, rspBuf.length); + this.makeCommonHeaderBuf(rspBuf, msgType, rspBuf.length, mediaType); // PCM 0 0 ; OPUS 0 1 - if (msgType === this.microPhoneMsgType.OPT_MIC_START_RECORD_RSP && this.options.microPhoneMsgType === 'OPUS') { + if (msgType === this.microPhoneMsgType.OPT_MIC_START_RECORD_RSP && this.options.microPhoneOutputType === 'OPUS') { rspBuf[rspBuf.length - 2] = 1 >> 8; rspBuf[rspBuf.length - 1] = 1 & 0xFF; } @@ -536,11 +541,12 @@ class AppController { } handleStartRecordReq() { + this.sendMediaMsgCmdData(MIC_START_RECORD_RSP_LEN, this.microPhoneMsgType.OPT_MIC_START_RECORD_RSP, 'MICROPHONE'); this.microphonePlayer = new MicrophonePlayer({ audioOptions: { sampleRate: 48000, channelCount: 1, - maxFrameSize: this.mediaMsgBody?.maxFrameSize || 7680 + maxFrameSize: this.microPhoneMsgBody?.maxFrameSize || 7680 }, sendOutBufHandler: this.sendMediaData.bind(this), microPhoneOutputType: this.options.microPhoneOutputType @@ -548,16 +554,31 @@ class AppController { } handleStartPreviewReq() { + const originWidth= this.cameraMsgBody.width; + const originHeight = this.cameraMsgBody.height; + let videoOptions = { + width: { ideal: originWidth}, + height: { ideal: originHeight}, + frameRate: { ideal: this.cameraMsgBody.fps} + }; + if (originWidth > PC_CAMERA_MAX_HEIGHT && !this.options.isMobile) { + let cameraWidth = (originHeight * PC_CAMERA_MAX_HEIGHT) / originWidth; + cameraWidth = this.util.changeToMultipleOfEight(cameraWidth); + videoOptions.width.ideal = cameraWidth; + videoOptions.height.ideal = PC_CAMERA_MAX_HEIGHT; + } + if (this.options.isMobile) { + videoOptions.facingMode = { + exact: MOBILE_CAMERA_MODE_MAP[this.cameraMsgHeader.devId] + }; + } this.cameraPlayer = new CameraPlayer({ - videoOptions: { - width: { ideal: this.mediaMsgBody.width}, - height: { ideal: this.mediaMsgBody.height}, - frameRate: { ideal: this.mediaMsgBody.fps} - }, - encodeWidth: this.mediaMsgBody.width, - encodeHeight: this.mediaMsgBody.height, + videoOptions, + encodeWidth: this.cameraMsgBody.width, + encodeHeight: this.cameraMsgBody.height, sendOutBufHandler: this.sendMediaData.bind(this), - x264WasmPath: this.options.libPath + x264WasmPath: this.options.libPath, + devId: this.cameraMsgHeader.devId }); } @@ -720,13 +741,13 @@ class AppController { /** * + * @param {string} mediaType 媒体类型 * @param {string} msgType 消息类型 - * @param {*} dataType 媒体类型 - * @param {*} data 8位无符号整型数组Unit8Array 帧数据 + * @param {array} data 8位无符号整型数组Unit8Array 帧数据 */ - sendMediaData(msgType, dataType, data) { - let dataBuf = this.makeMediaData(dataType, data); - let arrayBuf = this.makeDataMsg(msgType, dataBuf); + sendMediaData(mediaType, msgType, data) { + let dataBuf = this.makeMediaData(msgType, data, mediaType); + let arrayBuf = this.makeDataMsg(mediaType, dataBuf); arrayBuf && this.send(arrayBuf); } @@ -822,37 +843,47 @@ class AppController { * @param {object} headerBuf Uint8Array * @param {string} rspValue rsp类型,值为protocalConfig配置的key * @param {number} dataLen 数据总长度 + * @param {string} mediaType 媒体类型 */ - makeCommonHeaderBuf(headerBuf, rspValue, dataLen) { - headerBuf[0] = this.mediaMsgHeader.version >> 8; - headerBuf[1] = this.mediaMsgHeader.version & 0xFF; + makeCommonHeaderBuf(headerBuf, rspValue, dataLen, mediaType) { + let currentMsgHeader = null; + if (mediaType === 'CAMERA') { + currentMsgHeader = this.cameraMsgHeader; + } + if (mediaType === 'MICROPHONE') { + currentMsgHeader = this.microPhoneMsgHeader; + } + + headerBuf[0] = currentMsgHeader.version >> 8; + headerBuf[1] = currentMsgHeader.version & 0xFF; headerBuf[2] = rspValue >> 8; headerBuf[3] = rspValue & 0x00FF; - headerBuf[4] = this.mediaMsgHeader.devType >> 8; - headerBuf[5] = this.mediaMsgHeader.devType & 0xFF; - headerBuf[6] = this.mediaMsgHeader.devId >> 8; - headerBuf[7] = this.mediaMsgHeader.devId & 0xFF; + headerBuf[4] = currentMsgHeader.devType >> 8; + headerBuf[5] = currentMsgHeader.devType & 0xFF; + headerBuf[6] = currentMsgHeader.devId >> 8; + headerBuf[7] = currentMsgHeader.devId & 0xFF; headerBuf[8] = (dataLen & 0xFF000000) >> 24; headerBuf[9] = (dataLen & 0x00FF0000) >> 16; headerBuf[10] = (dataLen & 0x0000FF00) >> 8; headerBuf[11] = dataLen & 0x000000FF; - headerBuf[12] = this.mediaMsgHeader.nextProto >> 8; - headerBuf[13] = this.mediaMsgHeader.nextProto & 0xFF; - headerBuf[14] = this.mediaMsgHeader.hopLimits >> 8; - headerBuf[15] = this.mediaMsgHeader.hopLimits & 0xFF; + headerBuf[12] = currentMsgHeader.nextProto >> 8; + headerBuf[13] = currentMsgHeader.nextProto & 0xFF; + headerBuf[14] = currentMsgHeader.hopLimits >> 8; + headerBuf[15] = currentMsgHeader.hopLimits & 0xFF; } /** * * @param {string} dataType 媒体类型:CAMERA、MICROPHONE * @param {object} data Uint8Array 帧数据 + * @param {string} mediaType 媒体类型 * @returns */ - makeMediaData(dataType, data) { + makeMediaData(dataType, data, mediaType) { let dataBodyLen = data.byteLength; let dataBuf = new Uint8Array(MEDIA_MSG_HEADER_COUNT + dataBodyLen); dataBuf.set(new Uint8Array(data), MEDIA_MSG_HEADER_COUNT); - this.makeCommonHeaderBuf(dataBuf, dataType, dataBuf.length); + this.makeCommonHeaderBuf(dataBuf, dataType, dataBuf.length, mediaType); return dataBuf; } diff --git a/sdk/src/CameraPlayer.js b/sdk/src/CameraPlayer.js index a6978598e3d6652d052e37431d4e1505672b4ace..87db178b31e307734adfda758a65c0433b1fef3a 100644 --- a/sdk/src/CameraPlayer.js +++ b/sdk/src/CameraPlayer.js @@ -1,6 +1,7 @@ import Util from "./Util"; import work from 'webworkify-webpack'; import PROTOCOL_CONFIG from './config/protocolConfig'; +import {CAMERA_COMMON_MODE_MAP} from './config/commonConfig'; const OPT_CAMERA_FRAME = PROTOCOL_CONFIG.CAMERA_MESSAGE_TYPE.OPT_CAMERA_FRAME; @@ -57,7 +58,20 @@ export default class CameraPlayer { render() { const ctx = this.canvas.getContext('2d', {willReadFrequently: true}); - ctx.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height); + ctx.clearRect(0, 0, this.canvas.width,this.canvas.height); + ctx.save; + if (this.options.devId === CAMERA_COMMON_MODE_MAP.USER) { + // 前置摄像头 + ctx.rotate(Math.PI/2); + ctx.drawImage(this.video, 0, -this.canvas.width, this.canvas.height, this.canvas.width); + + } else { + // 后置摄像头 + ctx.rotate(-90 * Math.PI / 180); + ctx.drawImage(this.video, -this.canvas.height, 0, this.canvas.height, this.canvas.width); + } + ctx.restore(); + const imageData = ctx.getImageData(0, 0, this.canvas.width, this.canvas.height); this.videoEncoderWorker.postMessage({ @@ -75,8 +89,8 @@ export default class CameraPlayer { this.canvas.height = this.options.encodeHeight; this.videoEncoderWorker.postMessage({ type: 'initEncode', - width: this.options.encodeWidth, - height: this.options.encodeHeight, + width: this.canvas.width, + height: this.canvas.height, x264WasmPath: this.options.x264WasmPath }); }); diff --git a/sdk/src/MicrophonePlayer.js b/sdk/src/MicrophonePlayer.js index 19c66ae484f872313562a9ff3f0d2bc81f173c86..4eb9c7eb0e829047d402a9aaa7af564982db5dc5 100644 --- a/sdk/src/MicrophonePlayer.js +++ b/sdk/src/MicrophonePlayer.js @@ -64,7 +64,7 @@ export default class MicrophonePlayer { const inputBuffer = audioProcessEvent.inputBuffer; const pcmData = inputBuffer.getChannelData(0); - if (this.options.microPhoneOutputType === 'pcm') { + if (this.options.microPhoneOutputType === 'PCM') { // 转换为16位整数 let initData = new Int16Array(pcmData.length); for(let i=0; i 10; } } diff --git a/sdk/src/Util.js b/sdk/src/Util.js index 14c820166fef8aea256e2cfcce12099a8ebfc094..f5fca8db6e1937f1d179bdfdea92f52a92b9ad6a 100644 --- a/sdk/src/Util.js +++ b/sdk/src/Util.js @@ -197,6 +197,18 @@ export default class Util { (array[index + 2] << 8) | array[index + 3]; } + + /** + * @param {number} num 需要转换的数字 + * @returns 能被8整除的数 + */ + changeToMultipleOfEight(num) { + if (num % 8 === 0) { + return num; + } else { + return Math.ceil(num / 8) * 8; + } + } } Util.saveAsFile = (() => { diff --git a/sdk/src/config/commonConfig.js b/sdk/src/config/commonConfig.js index 7300da7a106efac872d9d1b8611be07893a6934f..0f638d5a628d3b0080cb31ac37a16071bec26fe8 100644 --- a/sdk/src/config/commonConfig.js +++ b/sdk/src/config/commonConfig.js @@ -1,4 +1,12 @@ export const DECODER_TYPE_MAP = { 'H264': 0, 'H265': 1 +} +export const MOBILE_CAMERA_MODE_MAP = { + 1: 'user', // 前置 + 0: 'environment' // 后置 +} +export const CAMERA_COMMON_MODE_MAP = { + 'USER': 1, // 前置 + 'ENVIRONMENT': 0 // 后置 } \ No newline at end of file