您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

actions.js 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. import JitsiMeetJS, {
  2. JitsiTrackErrors,
  3. JitsiTrackEvents
  4. } from '../lib-jitsi-meet';
  5. import {
  6. CAMERA_FACING_MODE,
  7. MEDIA_TYPE
  8. } from '../media';
  9. import { getLocalParticipant } from '../participants';
  10. import {
  11. TRACK_ADDED,
  12. TRACK_REMOVED,
  13. TRACK_UPDATED
  14. } from './actionTypes';
  15. /**
  16. * Request to start capturing local audio and/or video. By default, the user
  17. * facing camera will be selected.
  18. *
  19. * @param {Object} [options] - For info @see JitsiMeetJS.createLocalTracks.
  20. * @returns {Function}
  21. */
  22. export function createLocalTracks(options = {}) {
  23. return dispatch =>
  24. JitsiMeetJS.createLocalTracks({
  25. cameraDeviceId: options.cameraDeviceId,
  26. devices: options.devices || [ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ],
  27. facingMode: options.facingMode || CAMERA_FACING_MODE.USER,
  28. micDeviceId: options.micDeviceId
  29. })
  30. .then(localTracks => dispatch(_updateLocalTracks(localTracks)))
  31. .catch(err => {
  32. console.error(
  33. `JitsiMeetJS.createLocalTracks.catch rejection reason: ${err}`);
  34. });
  35. }
  36. /**
  37. * Calls JitsiLocalTrack#dispose() on all local tracks ignoring errors when
  38. * track is already disposed. After that signals tracks to be removed.
  39. *
  40. * @returns {Function}
  41. */
  42. export function destroyLocalTracks() {
  43. return (dispatch, getState) =>
  44. dispatch(
  45. _disposeAndRemoveTracks(
  46. getState()['features/base/tracks']
  47. .filter(t => t.local)
  48. .map(t => t.jitsiTrack)));
  49. }
  50. /**
  51. * Create an action for when a new track has been signaled to be added to the
  52. * conference.
  53. *
  54. * @param {(JitsiLocalTrack|JitsiRemoteTrack)} track - JitsiTrack instance.
  55. * @returns {{ type: TRACK_ADDED, track: Track }}
  56. */
  57. export function trackAdded(track) {
  58. return (dispatch, getState) => {
  59. track.on(
  60. JitsiTrackEvents.TRACK_MUTE_CHANGED,
  61. () => dispatch(trackMutedChanged(track)));
  62. track.on(
  63. JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED,
  64. type => dispatch(trackVideoTypeChanged(track, type)));
  65. // participantId
  66. const local = track.isLocal();
  67. let participantId;
  68. if (local) {
  69. const participant = getLocalParticipant(getState);
  70. if (participant) {
  71. participantId = participant.id;
  72. }
  73. } else {
  74. participantId = track.getParticipantId();
  75. }
  76. return dispatch({
  77. type: TRACK_ADDED,
  78. track: {
  79. jitsiTrack: track,
  80. local,
  81. mediaType: track.getType(),
  82. mirror: _shouldMirror(track),
  83. muted: track.isMuted(),
  84. participantId,
  85. videoStarted: false,
  86. videoType: track.videoType
  87. }
  88. });
  89. };
  90. }
  91. /**
  92. * Create an action for when a track's muted state has been signaled to be
  93. * changed.
  94. *
  95. * @param {(JitsiLocalTrack|JitsiRemoteTrack)} track - JitsiTrack instance.
  96. * @returns {{ type: TRACK_UPDATED, track: Track }}
  97. */
  98. export function trackMutedChanged(track) {
  99. return {
  100. type: TRACK_UPDATED,
  101. track: {
  102. jitsiTrack: track,
  103. muted: track.isMuted()
  104. }
  105. };
  106. }
  107. /**
  108. * Create an action for when a track has been signaled for removal from the
  109. * conference.
  110. *
  111. * @param {(JitsiLocalTrack|JitsiRemoteTrack)} track - JitsiTrack instance.
  112. * @returns {{ type: TRACK_REMOVED, track: Track }}
  113. */
  114. export function trackRemoved(track) {
  115. return {
  116. type: TRACK_REMOVED,
  117. track: {
  118. jitsiTrack: track
  119. }
  120. };
  121. }
  122. /**
  123. * Signal that track's video started to play.
  124. *
  125. * @param {(JitsiLocalTrack|JitsiRemoteTrack)} track - JitsiTrack instance.
  126. * @returns {{ type: TRACK_UPDATED, track: Track }}
  127. */
  128. export function trackVideoStarted(track) {
  129. return {
  130. type: TRACK_UPDATED,
  131. track: {
  132. jitsiTrack: track,
  133. videoStarted: true
  134. }
  135. };
  136. }
  137. /**
  138. * Create an action for when participant video type changes.
  139. *
  140. * @param {(JitsiLocalTrack|JitsiRemoteTrack)} track - JitsiTrack instance.
  141. * @param {VIDEO_TYPE|undefined} videoType - Video type.
  142. * @returns {{ type: TRACK_UPDATED, track: Track }}
  143. */
  144. export function trackVideoTypeChanged(track, videoType) {
  145. return {
  146. type: TRACK_UPDATED,
  147. track: {
  148. jitsiTrack: track,
  149. videoType
  150. }
  151. };
  152. }
  153. /**
  154. * Signals passed tracks to be added.
  155. *
  156. * @param {(JitsiLocalTrack|JitsiRemoteTrack)[]} tracks - List of tracks.
  157. * @private
  158. * @returns {Function}
  159. */
  160. function _addTracks(tracks) {
  161. return dispatch =>
  162. Promise.all(tracks.map(t => dispatch(trackAdded(t))));
  163. }
  164. /**
  165. * Disposes passed tracks and signals them to be removed.
  166. *
  167. * @param {(JitsiLocalTrack|JitsiRemoteTrack)[]} tracks - List of tracks.
  168. * @protected
  169. * @returns {Function}
  170. */
  171. export function _disposeAndRemoveTracks(tracks) {
  172. return dispatch =>
  173. Promise.all(
  174. tracks.map(t =>
  175. t.dispose()
  176. .catch(err => {
  177. // Track might be already disposed so ignore such an
  178. // error. Of course, re-throw any other error(s).
  179. if (err.name !== JitsiTrackErrors.TRACK_IS_DISPOSED) {
  180. throw err;
  181. }
  182. })
  183. ))
  184. .then(Promise.all(tracks.map(t => dispatch(trackRemoved(t)))));
  185. }
  186. /**
  187. * Finds the first <tt>JitsiLocalTrack</tt> in a specific array/list of
  188. * <tt>JitsiTrack</tt>s which is of a specific <tt>MEDIA_TYPE</tt>.
  189. *
  190. * @param {JitsiTrack[]} tracks - The array/list of <tt>JitsiTrack</tt>s to look
  191. * through.
  192. * @param {MEDIA_TYPE} mediaType - The <tt>MEDIA_TYPE</tt> of the first
  193. * <tt>JitsiLocalTrack</tt> to be returned.
  194. * @private
  195. * @returns {JitsiLocalTrack} The first <tt>JitsiLocalTrack</tt>, if any, in the
  196. * specified <tt>tracks</tt> of the specified <tt>mediaType</tt>.
  197. */
  198. function _getLocalTrack(tracks, mediaType) {
  199. return tracks.find(track =>
  200. track.isLocal()
  201. // XXX JitsiTrack#getType() returns a MEDIA_TYPE value in the terms
  202. // of lib-jitsi-meet while mediaType is in the terms of
  203. // jitsi-meet-react.
  204. && track.getType() === mediaType);
  205. }
  206. /**
  207. * Determines which local media tracks should be added and which removed.
  208. *
  209. * @param {(JitsiLocalTrack|JitsiRemoteTrack)[]} currentTracks - List of
  210. * current/existing media tracks.
  211. * @param {(JitsiLocalTrack|JitsiRemoteTrack)[]} newTracks - List of new media
  212. * tracks.
  213. * @private
  214. * @returns {{
  215. * tracksToAdd: JitsiLocalTrack[],
  216. * tracksToRemove: JitsiLocalTrack[]
  217. * }}
  218. */
  219. function _getLocalTracksToChange(currentTracks, newTracks) {
  220. const tracksToAdd = [];
  221. const tracksToRemove = [];
  222. for (const mediaType of [ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ]) {
  223. const newTrack = _getLocalTrack(newTracks, mediaType);
  224. if (newTrack) {
  225. const currentTrack = _getLocalTrack(currentTracks, mediaType);
  226. tracksToAdd.push(newTrack);
  227. currentTrack && tracksToRemove.push(currentTrack);
  228. }
  229. }
  230. return {
  231. tracksToAdd,
  232. tracksToRemove
  233. };
  234. }
  235. /**
  236. * Returns true if the provided JitsiTrack should be rendered as a mirror.
  237. *
  238. * We only want to show a video in mirrored mode when:
  239. * 1) The video source is local, and not remote.
  240. * 2) The video source is a camera, not a desktop (capture).
  241. * 3) The camera is capturing the user, not the environment.
  242. *
  243. * TODO Similar functionality is part of lib-jitsi-meet. This function should be
  244. * removed after https://github.com/jitsi/lib-jitsi-meet/pull/187 is merged.
  245. *
  246. * @param {(JitsiLocalTrack|JitsiRemoteTrack)} track - JitsiTrack instance.
  247. * @private
  248. * @returns {boolean}
  249. */
  250. function _shouldMirror(track) {
  251. return (
  252. track
  253. && track.isLocal()
  254. && track.isVideoTrack()
  255. // XXX The type of the return value of
  256. // JitsiLocalTrack's getCameraFacingMode happens to be named
  257. // CAMERA_FACING_MODE as well, it's defined by lib-jitsi-meet. Note
  258. // though that the type of the value on the right side of the
  259. // equality check is defined by jitsi-meet-react. The type
  260. // definitions are surely compatible today but that may not be the
  261. // case tomorrow.
  262. && track.getCameraFacingMode() === CAMERA_FACING_MODE.USER
  263. );
  264. }
  265. /**
  266. * Set new local tracks replacing any existing tracks that were previously
  267. * available. Currently only one audio and one video local tracks are allowed.
  268. *
  269. * @param {(JitsiLocalTrack|JitsiRemoteTrack)[]} [newTracks=[]] - List of new
  270. * media tracks.
  271. * @private
  272. * @returns {Function}
  273. */
  274. function _updateLocalTracks(newTracks = []) {
  275. return (dispatch, getState) => {
  276. const tracks
  277. = getState()['features/base/tracks'].map(t => t.jitsiTrack);
  278. const { tracksToAdd, tracksToRemove }
  279. = _getLocalTracksToChange(tracks, newTracks);
  280. return dispatch(_disposeAndRemoveTracks(tracksToRemove))
  281. .then(() => dispatch(_addTracks(tracksToAdd)));
  282. };
  283. }