You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

functions.ts 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import { IReduxState } from '../app/types';
  2. import { IJitsiConference } from '../base/conference/reducer';
  3. import { getLocalParticipant } from '../base/participants/functions';
  4. import { extractFqnFromPath } from '../dynamic-branding/functions.any';
  5. import { FACE_BOX_EVENT_TYPE, FACE_LANDMARKS_EVENT_TYPE, SEND_IMAGE_INTERVAL_MS } from './constants';
  6. import logger from './logger';
  7. import { FaceBox, FaceLandmarks } from './types';
  8. /**
  9. * Sends the face landmarks to other participants via the data channel.
  10. *
  11. * @param {any} conference - The current conference.
  12. * @param {FaceLandmarks} faceLandmarks - Face landmarks to be sent.
  13. * @returns {void}
  14. */
  15. export function sendFaceExpressionToParticipants(conference: any, faceLandmarks: FaceLandmarks): void {
  16. try {
  17. conference.sendEndpointMessage('', {
  18. type: FACE_LANDMARKS_EVENT_TYPE,
  19. faceLandmarks
  20. });
  21. } catch (err) {
  22. logger.warn('Could not broadcast the face landmarks to the other participants', err);
  23. }
  24. }
  25. /**
  26. * Sends the face box to all the other participants.
  27. *
  28. * @param {any} conference - The current conference.
  29. * @param {FaceBox} faceBox - Face box to be sent.
  30. * @returns {void}
  31. */
  32. export function sendFaceBoxToParticipants(
  33. conference: any,
  34. faceBox: FaceBox
  35. ): void {
  36. try {
  37. conference.sendEndpointMessage('', {
  38. type: FACE_BOX_EVENT_TYPE,
  39. faceBox
  40. });
  41. } catch (err) {
  42. logger.warn('Could not broadcast the face box to the other participants', err);
  43. }
  44. }
  45. /**
  46. * Sends the face landmarks to prosody.
  47. *
  48. * @param {any} conference - The current conference.
  49. * @param {FaceLandmarks} faceLandmarks - Face landmarks to be sent.
  50. * @returns {void}
  51. */
  52. export function sendFaceExpressionToServer(conference: IJitsiConference | undefined,
  53. faceLandmarks: FaceLandmarks): void {
  54. try {
  55. conference?.sendFaceLandmarks(faceLandmarks);
  56. } catch (err) {
  57. logger.warn('Could not send the face landmarks to prosody', err);
  58. }
  59. }
  60. /**
  61. * Sends face landmarks to backend.
  62. *
  63. * @param {Object} state - Redux state.
  64. * @returns {boolean} - True if sent, false otherwise.
  65. */
  66. export async function sendFaceExpressionsWebhook(state: IReduxState) {
  67. const { webhookProxyUrl: url } = state['features/base/config'];
  68. const { conference } = state['features/base/conference'];
  69. const { jwt } = state['features/base/jwt'];
  70. const { connection } = state['features/base/connection'];
  71. const jid = connection?.getJid();
  72. const localParticipant = getLocalParticipant(state);
  73. const { faceLandmarksBuffer } = state['features/face-landmarks'];
  74. if (faceLandmarksBuffer.length === 0) {
  75. return false;
  76. }
  77. const headers = {
  78. ...jwt ? { 'Authorization': `Bearer ${jwt}` } : {},
  79. 'Content-Type': 'application/json'
  80. };
  81. const reqBody = {
  82. meetingFqn: extractFqnFromPath(),
  83. sessionId: conference?.getMeetingUniqueId(),
  84. submitted: Date.now(),
  85. emotions: faceLandmarksBuffer,
  86. participantId: localParticipant?.jwtId,
  87. participantName: localParticipant?.name,
  88. participantJid: jid
  89. };
  90. if (url) {
  91. try {
  92. const res = await fetch(`${url}/emotions`, {
  93. method: 'POST',
  94. headers,
  95. body: JSON.stringify(reqBody)
  96. });
  97. if (res.ok) {
  98. return true;
  99. }
  100. logger.error('Status error:', res.status);
  101. } catch (err) {
  102. logger.error('Could not send request', err);
  103. }
  104. }
  105. return false;
  106. }
  107. /**
  108. * Gets face box for a participant id.
  109. *
  110. * @param {string} id - The participant id.
  111. * @param {IReduxState} state - The redux state.
  112. * @returns {Object}
  113. */
  114. function getFaceBoxForId(id: string, state: IReduxState) {
  115. return state['features/face-landmarks'].faceBoxes[id];
  116. }
  117. /**
  118. * Gets the video object position for a participant id.
  119. *
  120. * @param {IReduxState} state - The redux state.
  121. * @param {string} id - The participant id.
  122. * @returns {string} - CSS object-position in the shape of '{horizontalPercentage}% {verticalPercentage}%'.
  123. */
  124. export function getVideoObjectPosition(state: IReduxState, id?: string) {
  125. const faceBox = id && getFaceBoxForId(id, state);
  126. if (faceBox) {
  127. const { right, width } = faceBox;
  128. if (right && width) {
  129. return `${right - (width / 2)}% 50%`;
  130. }
  131. }
  132. return '50% 50%';
  133. }
  134. /**
  135. * Gets the video object position for a participant id.
  136. *
  137. * @param {IReduxState} state - The redux state.
  138. * @returns {number} - Number of milliseconds for doing face detection.
  139. */
  140. export function getDetectionInterval(state: IReduxState) {
  141. const { faceLandmarks } = state['features/base/config'];
  142. return Math.max(faceLandmarks?.captureInterval || SEND_IMAGE_INTERVAL_MS);
  143. }