Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

JingleHelperFunctions.js 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import { safeJsonParse } from '@jitsi/js-utils/json';
  2. import { getLogger } from '@jitsi/logger';
  3. import $ from 'jquery';
  4. import { $build } from 'strophe.js';
  5. import { MediaType } from '../../service/RTC/MediaType';
  6. import { SSRC_GROUP_SEMANTICS } from '../../service/RTC/StandardVideoQualitySettings';
  7. import { VideoType } from '../../service/RTC/VideoType';
  8. import { XEP } from '../../service/xmpp/XMPPExtensioProtocols';
  9. const logger = getLogger(__filename);
  10. /**
  11. * Creates a "source" XML element for the source described in compact JSON format in [sourceCompactJson].
  12. * @param {*} owner the endpoint ID of the owner of the source.
  13. * @param {*} sourceCompactJson the compact JSON representation of the source.
  14. * @param {boolean} isVideo whether the source is a video source
  15. * @returns the created "source" XML element.
  16. */
  17. function _createSourceExtension(owner, sourceCompactJson, isVideo = false) {
  18. let videoType = sourceCompactJson.v ? VideoType.DESKTOP : undefined;
  19. // If the video type is not specified, it is assumed to be a camera for video sources.
  20. // Jicofo adds the video type only for desktop sharing sources.
  21. if (!videoType && isVideo) {
  22. videoType = VideoType.CAMERA;
  23. }
  24. const node = $build('source', {
  25. xmlns: XEP.SOURCE_ATTRIBUTES,
  26. ssrc: sourceCompactJson.s,
  27. name: sourceCompactJson.n,
  28. videoType
  29. });
  30. if (sourceCompactJson.m) {
  31. node.c('parameter', {
  32. name: 'msid',
  33. value: sourceCompactJson.m
  34. }).up();
  35. }
  36. node.c('ssrc-info', {
  37. xmlns: 'http://jitsi.org/jitmeet',
  38. owner
  39. }).up();
  40. return node.node;
  41. }
  42. /**
  43. * Creates an "ssrc-group" XML element for the SSRC group described in compact JSON format in [ssrcGroupCompactJson].
  44. * @param {*} ssrcGroupCompactJson the compact JSON representation of the SSRC group.
  45. * @returns the created "ssrc-group" element.
  46. */
  47. function _createSsrcGroupExtension(ssrcGroupCompactJson) {
  48. const node = $build('ssrc-group', {
  49. xmlns: XEP.SOURCE_ATTRIBUTES,
  50. semantics: _getSemantics(ssrcGroupCompactJson[0])
  51. });
  52. for (let i = 1; i < ssrcGroupCompactJson.length; i++) {
  53. node.c('source', {
  54. xmlns: XEP.SOURCE_ATTRIBUTES,
  55. ssrc: ssrcGroupCompactJson[i]
  56. }).up();
  57. }
  58. return node.node;
  59. }
  60. /**
  61. * Finds in a Jingle IQ the RTP description element with the given media type. If one does not exists, create it (as
  62. * well as the required "content" parent element) and adds it to the IQ.
  63. * @param {*} iq
  64. * @param {*} mediaType The media type, "audio" or "video".
  65. * @returns the RTP description element with the given media type.
  66. */
  67. function _getOrCreateRtpDescription(iq, mediaType) {
  68. const jingle = $(iq).find('jingle')[0];
  69. let content = $(jingle).find(`content[name="${mediaType}"]`);
  70. let description;
  71. if (content.length) {
  72. content = content[0];
  73. } else {
  74. // I'm not suree if "creator" and "senders" are required.
  75. content = $build('content', {
  76. name: mediaType
  77. }).node;
  78. jingle.appendChild(content);
  79. }
  80. description = $(content).find('description');
  81. if (description.length) {
  82. description = description[0];
  83. } else {
  84. description = $build('description', {
  85. xmlns: XEP.RTP_MEDIA,
  86. media: mediaType
  87. }).node;
  88. content.appendChild(description);
  89. }
  90. return description;
  91. }
  92. /**
  93. * Converts the short string representing SSRC group semantics in compact JSON format to the standard representation
  94. * (i.e. convert "f" to "FID" and "s" to "SIM").
  95. * @param {*} str the compact JSON format representation of an SSRC group's semantics.
  96. * @returns the SSRC group semantics corresponding to [str].
  97. */
  98. function _getSemantics(str) {
  99. if (str === 'f') {
  100. return SSRC_GROUP_SEMANTICS.FID;
  101. } else if (str === 's') {
  102. return SSRC_GROUP_SEMANTICS.SIM;
  103. }
  104. return null;
  105. }
  106. /**
  107. * Reads a JSON-encoded message (from a "json-message" element) and extracts source descriptions. Adds the extracted
  108. * source descriptions to the given Jingle IQ in the standard Jingle format.
  109. *
  110. * Encoding sources in this compact JSON format instead of standard Jingle was introduced in order to reduce the
  111. * network traffic and load on the XMPP server. The format is described in Jicofo [TODO: insert link].
  112. *
  113. * @param {*} iq the IQ to which source descriptions will be added.
  114. * @param {*} jsonMessageXml The XML node for the "json-message" element.
  115. * @returns {Map<string, Array<string>} The audio and video ssrcs extracted from the JSON-encoded message with remote
  116. * endpoint id as the key.
  117. */
  118. export function expandSourcesFromJson(iq, jsonMessageXml) {
  119. let json;
  120. try {
  121. json = safeJsonParse(jsonMessageXml.textContent);
  122. } catch (error) {
  123. logger.error(`json-message XML contained invalid JSON, ignoring: ${jsonMessageXml.textContent}`);
  124. return null;
  125. }
  126. if (!json?.sources) {
  127. // It might be a message of a different type, no need to log.
  128. return null;
  129. }
  130. // This is where we'll add "source" and "ssrc-group" elements. Create them elements if they don't exist.
  131. const audioRtpDescription = _getOrCreateRtpDescription(iq, MediaType.AUDIO);
  132. const videoRtpDescription = _getOrCreateRtpDescription(iq, MediaType.VIDEO);
  133. const ssrcMap = new Map();
  134. for (const owner in json.sources) {
  135. if (json.sources.hasOwnProperty(owner)) {
  136. const ssrcs = [];
  137. const ownerSources = json.sources[owner];
  138. // The video sources, video ssrc-groups, audio sources and audio ssrc-groups are encoded in that order in
  139. // the elements of the array.
  140. const videoSources = ownerSources?.length && ownerSources[0];
  141. const videoSsrcGroups = ownerSources?.length > 1 && ownerSources[1];
  142. const audioSources = ownerSources?.length > 2 && ownerSources[2];
  143. const audioSsrcGroups = ownerSources?.length > 3 && ownerSources[3];
  144. if (videoSources?.length) {
  145. for (let i = 0; i < videoSources.length; i++) {
  146. videoRtpDescription.appendChild(_createSourceExtension(owner, videoSources[i], true));
  147. ssrcs.push(videoSources[i]?.s);
  148. }
  149. }
  150. if (videoSsrcGroups?.length) {
  151. for (let i = 0; i < videoSsrcGroups.length; i++) {
  152. videoRtpDescription.appendChild(_createSsrcGroupExtension(videoSsrcGroups[i]));
  153. }
  154. }
  155. if (audioSources?.length) {
  156. for (let i = 0; i < audioSources.length; i++) {
  157. audioRtpDescription.appendChild(_createSourceExtension(owner, audioSources[i]));
  158. ssrcs.push(audioSources[i]?.s);
  159. }
  160. }
  161. if (audioSsrcGroups?.length) {
  162. for (let i = 0; i < audioSsrcGroups.length; i++) {
  163. audioRtpDescription.appendChild(_createSsrcGroupExtension(audioSsrcGroups[i]));
  164. }
  165. }
  166. ssrcMap.set(owner, ssrcs);
  167. }
  168. }
  169. return ssrcMap;
  170. }